Author: ivyhaswell
For its introduction, I would like to start with a practical example that happened recently:
Recently, the group began to document the project. We need to find a screen recording tool to make videos and GIfs. So we started looking for something that didn’t capture the status bar, or was too big, or wasn’t available on Windows, and so on. It took a while to finally find one that worked. I was working on the electron project, and I thought: How long would it take to build a simple, cross-platform recording tool for electron?
The answer is 20 minutes, dozens of lines of code.
If you are interested, try the following steps:
(1) Create a screen recording tool
- First, create a directory to install electron.
yarn add -D electron
Copy the code
- create
main.js
Here is the code for the main process;
const { app, BrowserWindow, globalShortcut } = require("electron");
const path = require("path");
app.on("ready".() = > {
const browserWindow = new BrowserWindow({
webPreferences: { nodeIntegration: true.enableRemoteModule: true}}); browserWindow.loadFile(path.resolve(__dirname,"index.html"));
globalShortcut.register('CommandOrControl+Shift+R'.() = > browserWindow.webContents.send("StartRecording"))
globalShortcut.register('CommandOrControl+Shift+S'.() = > browserWindow.webContents.send("StopRecording"))}); app.on('will-quit'.() = > globalShortcut.unregisterAll())
Copy the code
- create
index.html
To introduce the renderer script
<h1>Current Status:<span id="status">free</span></h1>
<ol>
<li>MacOS shortcuts: Command+Shift+R to start recording, Command+Shift+S to stop recording;</li>
<li>Windows: Ctrl+Shift+R to start recording, Ctrl+Shift+S to stop recording;</li>
</ol>
<script src="./renderer.js"></script>
Copy the code
- create
renderer.js
Here is the main business code
const { desktopCapturer, remote, shell, ipcRenderer } = require("electron");
const path = require("path");
const fs = require("fs");
let mediaRecorder = null;
let chunks = []
async function start() {
if (mediaRecorder) return;
const sources = await desktopCapturer.getSources({ types: ["screen"]});const stream = await navigator.mediaDevices.getUserMedia({
audio: false.video: {
mandatory: {
chromeMediaSource: "screen".chromeMediaSourceId: sources[0].id,
},
},
});
mediaRecorder = new MediaRecorder(stream, { mimeType: "video/webm; codecs=vp9" });
mediaRecorder.ondataavailable = (event) = > {
event.data.size > 0 && chunks.push(event.data);
};
mediaRecorder.start();
updateStatusText('Recording... ')}function stop(){
if(! mediaRecorder)return
mediaRecorder.onstop = async() = > {const blob = new Blob(chunks, { type: "video/webm" });
const buffer = Buffer.from(await blob.arrayBuffer());
const filePath = path.resolve(remote.app.getPath("downloads"), `The ${Date.now()}.webm`);
fs.writeFile(filePath, buffer, () = > {
shell.openPath(filePath);
mediaRecorder = null;
chunks = []
});
};
mediaRecorder.stop();
updateStatusText('free')}function updateStatusText(text){
const $statusElement = document.querySelector('#status')
$statusElement.textContent = text
}
ipcRenderer.on("StartRecording", start);
ipcRenderer.on("StopRecording", stop);
Copy the code
The application can then be launched from the command line
./node_modules/.bin/electron main.js
Copy the code
It’s also very simple to use:
- On the Mac, use shortcut keys
Command+Shift+R
Start recording,Command+Shift+S
Stop recording; The Windows shortcut keys areControl+Shift+R
和Control+Shift+S
; - After stopping recording, the recorded video will be automatically opened (the video is saved in the download directory by default).
When the app starts, it looks like this:
The following GIF is a screen recording and conversion using this demo:
(2) Tool code analysis
Twenty minutes and dozens of lines of code may be an exaggeration, as there are many features, such as recording parameter configuration, format conversion, multi-platform packaging, etc., that are yet to be implemented. But it doesn’t stop you from seeing how easy it is to start developing Electron.
Before we dive into the code, let’s look at one of the basic concepts of Electron: the main process and the renderer process.
- Main process passes
BrowserWindow
Create Windows, each window corresponding to a render process; - The rendering process manages the corresponding Web page,
BrowserWindow
After destruction, the corresponding rendering process also terminates; - The crash of one renderer does not affect other renderers;
To understand this concept, let’s try it out:
Start by adding a main-process.js to the root directory
const { app, BrowserWindow, dialog } = require("electron");
const path = require("path");
app.on("ready".() = > {
const win1 = new BrowserWindow({ x: 20.y: 20 });
win1.loadURL("https://github.com");
const win2 = new BrowserWindow({ x: 500.y: 20 });
win2.loadURL("https://stackoverflow.com");
const win3 = new BrowserWindow({
x: 20.y: 500.webPreferences: { nodeIntegration: true}}); win3.loadFile(path.resolve(__dirname,"crash-renderer.html"));
win3.webContents.on('render-process-gone'.async() = > {await dialog.showMessageBoxSync(win3, {message: 'Process has crashed.'.buttons: ['off']})
win3.close()
})
});
Copy the code
Add another crash-renderer.html
<script>
setTimeout(() = > {
process.crash()
}, 2000);
</script>
Copy the code
Start the application with./node_modules/. Bin /electron main-process.js
Here main-process.js creates three Windows, the first opening github, the second opening StackOverflow, and the third opening local HTML files. There are three Windows for each render process.
In crash-renderer.html we executed process.crash(), so we could see that the process in the third window crashed after 2 seconds, while github and StackOverflow remained normal for the other two Windows.
Going back to the screen recording application, its basic functional structure is designed as follows:
To reflect the implementation, let’s start with main.js
First you register the methods after the application is ready and before you exit
app.on('ready'.() = >{... }); app.on('will-quit'.() = >{... });Copy the code
Two things are done in the Ready event, the first is to create the window:
const browserWindow = new BrowserWindow({
webPreferences: { nodeIntegration: true.enableRemoteModule: true}}); browserWindow.loadFile(path.resolve(__dirname,"index.html"));
Copy the code
NodeIntegration and enableRemoteModule are set to true to use the Node and remote modules in the rendering process. Sometimes we need to disable these properties for application security.
Then load index.html with the loadFile method;
Next is the register global shortcut:
globalShortcut.register('CommandOrControl+Shift+R'.() = > browserWindow.webContents.send("StartRecording"))
globalShortcut.register('CommandOrControl+Shift+S'.() = > browserWindow.webContents.send("StopRecording"))
Copy the code
When a shortcut key is pressed, a message is sent to the renderer process via IPC.
In renderer.js, the corresponding IPC message is received and the corresponding method is executed:
ipcRenderer.on("StartRecording", start);
ipcRenderer.on("StopRecording", stop);
Copy the code
Where the start method performs recording:
- Through desktopCapturer. GetSources screen source, here, take one of the first, often give priority to the screen;
- Through the navigator. MediaDevices. GetUserMedia to obtain video stream;
- Create mediaRecorder, record video data through mediaRecorder;
async function start() {
// Already recorded
if (mediaRecorder) return;
// Take the first screen source, usually the main screen
const sources = await desktopCapturer.getSources({ types: ["screen"]});const stream = await navigator.mediaDevices.getUserMedia({
audio: false.video: {
mandatory: {
chromeMediaSource: "screen".chromeMediaSourceId: sources[0].id,
},
},
});
mediaRecorder = new MediaRecorder(stream, { mimeType: "video/webm; codecs=vp9" });
mediaRecorder.ondataavailable = (event) = > {
event.data.size > 0 && chunks.push(event.data);
};
mediaRecorder.start();
updateStatusText('Recording... ')}Copy the code
The end method ends recording and saves the file:
- Add end method to mediaRecorder, call mediaRecorder. Stop end recording;
- At the end, take the recorded data chunks and create Blob objects.
- Convert the Blob object to an arrayBuffer, and then to a buffer;
- Write buffer to local new video file;
- Open the video file;
- Reset mediaRecorder and Chunks
function stop(){
if(! mediaRecorder)return
mediaRecorder.onstop = async() = > {const blob = new Blob(chunks, { type: "video/webm" });
const buffer = Buffer.from(await blob.arrayBuffer());
const filePath = path.resolve(remote.app.getPath("downloads"), `The ${Date.now()}.webm`);
fs.writeFile(filePath, buffer, () = > {
shell.openPath(filePath);
mediaRecorder = null;
chunks = []
});
};
mediaRecorder.stop();
updateStatusText('free')}Copy the code
And you’re done.
(3) probe the Electron
Let’s revisit Electron’s official introduction: Building cross-platform desktop applications with JavaScript, HTML, and CSS.
As you can see from the examples above, nothing is written beyond the troika (not even CSS). For those of you who have developed NodeJS on the front end, it’s easy to understand the code without having to look at the parsing.
Does it feel like, without the main process and communication module, the development experience is like developing a web page with an integrated Node environment?
Electron itself provides a way to quickly open a link or HTML file without even writing the main process code, such as:
electron https://github.com
electron index.html
electron /Users/username/Projects/electron-demo/index.html
...
Copy the code
Each of these methods causes Electron to launch the app and open a window that loads the corresponding web link or file.
But if it’s just a web shell, shouldn’t we just use PWA?
Because Electron has so much more to offer.
Let’s look at the main body of electron, which consists of three parts:
- Chromium: For web content display;
- Node.js: used for file reading and writing, operating system and other low-level API interaction;
- Custom API: used to provide common system operation needs method, such as setting menu and tray, control window, etc.;
The official documentation itself pokes a joke: Developing applications using Electron is like building Node.js applications using a Web interface, or Web pages using seamlessly integrated Node.js. Many students are familiar with Web development and Node.js, so they need to be familiar with the development mode and API provided by ELECTRON to start electron development. In addition, if you want to build a larger project for use in the company, you also need some engineering experience in electron development.
Official documents are a good source of learning, but not the only reference. From the official document to the surrounding library document, the document can not explain their own to try, try not to open VScode source reference examination; Encounter a bug to github to find an issue, sometimes need to trace all the way back to the electron source and chromium source……
With the knowledge we learned and the pitfalls we stepped on, we came up with an open source project:
Github.com/tal-tech/el…
In the project, we summarized our own experience in learning and stepping pits, and made this project by referring to some excellent schemes of the open source community as a quick learning and stepping pits application.
At present, the most important functions are: first, the embedded code in the document can run directly, or you can modify the code to run directly on the interface, which is convenient to adjust parameters to see the effect; The second is the drill field, which is used to write some small functional modules to run directly. Currently, there are only basic templates. Our goal is to add many common functional modules in the future, such as screenshots, such as message notification, such as file upload and download…… Wait, wait, wait.
If you have any suggestions about the project, what functions you want, or what bugs you find, you are welcome to make issue on Github. Our reply speed is super fast.
In order to learn electron better, we have created a series at present, if you are interested, you can have a look
- [Electron playground series] Menu
- 【Electron Playground series 】Dialog with file selection
- [Electron playground series] Agreement
- 【Electron Playground series 】 The tray
For more complete documentation, please refer to the documentation below
Electron-Playground official document
Github address portal: github.com/tal-tech/el…