предисловие
В предыдущей статье я примерно описал, как выглядит устройство заграждения, которое мы собираемся сделать, и какие технологии будут использоваться. Итак, начиная с этой статьи, мы будем шаг за шагом превращать идеи в код. В этой статье мы будем использовать Electron для реализации приемника и запускаем наш заградительный огонь.
визуализация
Как показано на рисунке выше, во время показа PPT пользователи могут выражать свои мысли в режиме реального времени, сканируя QR-код апплета для достижения интерактивного эффекта.
Основное введение в Электрон
Если у вас уже есть некоторое представление об Electron, вы можете пропустить этот раздел.
Electron — это библиотека с открытым исходным кодом, разработанная Github для создания кроссплатформенных настольных приложений с использованием HTML, CSS и JavaScript. Electron делает это, объединяя Chromium и Node.js в одну среду выполнения и упаковывая ее в виде приложения для Mac, Windows и Linux.
В Electron основной процесс и процесс рендеринга — очень важная концепция.
Основной процесс и процесс рендеринга
Процесс — это экземпляр выполнения компьютерной программы. Приложения Electron используют как main (основной процесс), так и один или несколько rendere (процессов рендеринга) для запуска нескольких программ.
В Node.js и Electron каждый запущенный процесс содержит объект процесса. Этот объект используется как глобальный для предоставления соответствующей информации и методов работы текущего процесса. Как глобальная переменная, к ней можно получить доступ в любое время в приложении, не требуя().
основной процесс
Основной процесс, обычно файл с именем main.js, является входным файлом для каждого приложения Electron. Он контролирует весь жизненный цикл приложения, от открытия до закрытия. Он также управляет собственными элементами системы, такими как меню, строки меню, стыковочные панели, панели задач и т. д. Основной процесс отвечает за создание каждого процесса рендеринга приложения. И весь Node API интегрирован внутри.
Основной файл процесса для каждого приложения определяется в основном свойстве в package.json. По этой же причине электрон может знать, какой файл следует использовать для запуска.
В Chromium этот процесс называется «процессом браузера». В Electron он был переименован, чтобы избежать путаницы с процессом рендеринга.
процесс рендеринга
Рендерер — это окно браузера в вашем приложении. В отличие от основного процесса, он может существовать одновременно и выполняться в разных процессах. А еще их можно скрыть.
В обычных браузерах веб-страницы обычно работают в изолированной среде и не могут использовать собственные ресурсы. Однако пользователи Electron могут выполнять некоторые низкоуровневые взаимодействия с операционной системой на странице с поддержкой Node.js API.
Общая структура
Как видно из приведенных выше рендеров, интерфейс в основном состоит из заградительного интерфейса и небольшого программного QR-кода.
Объединив основной процесс и процесс рендеринга, упомянутые выше, мы можем поместить заграждение и QR-код в два процесса рендеринга соответственно.Основной процесс связывается с сервером nodejs через веб-сокет и распределяет полученные данные в соответствующий процесс рендеринга.
быстрый старт
У официального есть пример быстрого запуска, мы можем использовать этот пример, чтобы получить общее представление о том, как работает все приложение.
# 克隆示例项目的仓库
$ git clone https://github.com/electron/electron-quick-start
# 进入这个仓库
$ cd electron-quick-start
# 安装依赖并运行
$ npm install && npm start
Мы видим в package.json, что значением основного свойства является main.js, да, это входной файл основного процесса. В этом входном файле окно браузера создается с помощью BrowserWindow и загружается html-файл. Да, это называется процессом рендеринга.
Таким образом, то, что нам нужно сделать, на самом деле очень простое, в основном то же самое, что и при написании статических страниц. После написания кода для создания окна браузера осталось написать статическую страницу. Конечно, схему передачи данных тоже нужно учитывать.
начать разработку
Создать окно браузера
В основном процессе через API BrowserWindow создаются два процесса рендеринга для отображения основного интерфейса экрана маркеров и QR-кода соответственно.
function createMainWindow() {
mainWindow = new BrowserWindow({
width: 1920,
height: 1080,
transparent: true,
frame: false,
resizable: false,
alwaysOnTop: true,
center: true,
skipTaskbar: true,
autoHideMenuBar: true,
focusable: false
});
mainWindow.setAlwaysOnTop(true, 'pop-up-menu'); //一定要这样设置 要不然在mac下全屏播放PPT的时候看不到
mainWindow.maximize();//窗口最大化
mainWindow.setIgnoreMouseEvents(true); //点击穿透
mainWindow.loadURL(`file://${__dirname}/app/index.html`);
}
function createQrcodeWindow() {
qrcodeWindow = new BrowserWindow({
width: 200,
height: 200,
transparent: true,
frame: false,
resizable: false,
minimizable: false,
maximizable: false,
alwaysOnTop: true,
center: true,
skipTaskbar: true,
autoHideMenuBar: true
});
qrcodeWindow.setAlwaysOnTop(true, 'pop-up-menu'); //一定要这样设置 要不然在mac下全屏播放PPT的时候看不到
qrcodeWindow.loadURL(`file://${__dirname}/app/qrcode.html`);
}
Есть еще много элементов конфигурации для создания окон, вы можете прочитать официальную документацию, чтобы узнать, что можно сделать.
Так как наш шквал всегда вверху и не может повлиять на работу нижнего слоя, мы делаем его прозрачным, без полей и игнорируем события мыши (щелчок, чтобы проникнуть), так что одновременно со шквалом можно еще и Смотрите нижнее окно и выполняйте соответствующие операции. Кроме того, мы также установили запрет на увеличение и уменьшение масштаба и максимизировали окно заграждения.
системный трей
Поскольку мы не хотим, чтобы окно появлялось на панели задач, в конце концов, заграждение работает все время, и оно помещается в панель задач, занимает место и мозолит глаза.Мы предпочитаем, чтобы оно появлялось в системном трее. , как показано ниже:
На картинке выше показан эффект Mac, а Windows появится в правом нижнем углу.
Мы можем установить системный трей через API Tray.
function initTrayMenu() {
let iconPath = path.join(__dirname, 'ico/favicon.ico');
if (os.type() === "Darwin") {
iconPath = path.join(__dirname, 'ico/favicon.png');
}
const nimage = nativeImage.createFromPath(iconPath);
tray = new Tray(nimage);
tray.setToolTip('弹幕');
const contextMenu = Menu.buildFromTemplate([
{
label: '显示弹幕',
type: 'radio',
click: showMainWindow
},
{
label: '关闭弹幕',
type: 'radio',
click: hideMainWindow
},
{
type: 'separator'
},
{
label: '显示二维码',
type: 'radio',
click: showQrcodeWindow
},
{
label: '隐藏二维码',
type: 'radio',
click: hideQrcodeWindow
},
{
type: 'separator'
},
{
label: '退出',
type: 'normal',
click: function () {
app.quit();
}
}
]);
tray.setContextMenu(contextMenu); //设置菜单
tray.on('click', handleToggleShowMainWindow);
}
Связь с сервером nodejs
В нашем проекте для связи между получателем и сервером используется протокол веб-сокетов. Поэтому помимо создания процесса рендеринга у основного процесса есть еще и очень важная задача — установить соединение по вебсокету с сервером.
Мы решили использовать модуль ws для быстрого установления соединения:
const ws = require('ws');
const Socket = new ws('wss://danmu.xxx.com');//参数为socket服务地址
Socket.on('open', function open() {
//在初始化的时候,发送初始化消息,并带上接收客户端ID,获取小程序二维码
const initData = {
type: 'INIT',
clientId: 'xxxx' //客户端唯一ID
}
Socket.send(JSON.stringify(initData));
});
Socket.on('message', function incoming(data) {
// 对收到的数据进行分发
try {
const received_msg = JSON.parse(data);
if (received_msg.type === 'qrcode') {
qrcodeWindow.webContents.send('qrcodeBase64', received_msg.data);
}else{
mainWindow.webContents.send('new-message', received_msg);
}
} catch (error) {
console.log(error);
}
});
После установления соединения с сервером мы отправили инициализирующее сообщение, договорились, что после того, как сервер получит инициализирующее сообщение, он сгенерирует небольшой программный QR-код с параметрами согласно clientId, и вернет base64 данные QR-кода. Поэтому, когда мы получаем новое сообщение, мы оцениваем тип сообщения и направляем его в соответствующий процесс рендеринга.
Как упоминалось выше, серверу nodejs необходимо предоставить уникальный идентификатор клиента для создания уникального QR-кода апплета. Мы можем сгенерировать его, используя:
function generateUUID() {
return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
// 大家不妨思考一下,为什么可以使用这个方法生成UUID呢?
Кроме того, мы надеемся, что после первого использования этого заградительного приложения ID останется прежним, ведь QR-код время от времени меняется, что очень недружелюбно к пользователям. Поэтому нам нужно записать этот идентификатор в локальный файл. Поленитесь, мы также можем использовать модули, написанные другимиelectron-store, принцип заключается в том, чтобы записать информацию, которую необходимо хранить локально, в файл json, а различные специальные пути можно получить через app.getPath(name).
Например, app.getPath("appData") может получить папку данных приложения текущего пользователя, которая соответствует умолчанию:
- Windows: %APPDATA%
- Linux: $XDG_CONFIG_HOME или ~/.config
- macOS: ~/Библиотека/Поддержка приложений
Другие дополнительные пути можно найти в официальной документации.
Связь между основным процессом и процессом рендеринга
Здесь мы поговорим об ipcMain, ipcRenderer и webContents, все они являются экземплярами класса EventEmitter.
ipcMain: при использовании в основном процессе обрабатывает асинхронные и синхронные сообщения, отправленные из процесса визуализации. Сообщения, отправленные из процесса визуализации, будут отправлены в этот модуль.
ipcRenderer: вы можете использовать некоторые из предоставляемых им методов для отправки синхронных или асинхронных сообщений из процесса рендерера в основной процесс. Также возможно получать сообщения, на которые отвечает основной процесс.
webContents: свойство объекта BrowserWindow, его метод отправки может отправлять асинхронные сообщения в процесс рендеринга и может отправлять произвольные параметры.
Посмотрите на каштаны QR-кода:
//主进程中
ipcMain.on("qrcodeFinished", function () {
// doSomething
});
qrcodeWindow.webContents.send('qrcodeBase64', received_msg.data);
//渲染进程
const ipcRenderer = require('electron').ipcRenderer;
ipcRenderer.on('qrcodeBase64', function (event, data) {
const qrcode = document.getElementById('qrcode');
qrcode.src = data;
qrcode.style.display = 'block';
ipcRenderer.send("qrcodeFinished");
});
Пусть шквал летит
Как упоминалось выше, мы создали окно браузера в основном процессе, а затем загрузили html-файл через экземпляр окна браузера и отобразили нашу страницу для обработки. Таким образом, запуск пули экрана ничем не отличается от написания статической страницы Источником данных является получение сообщения, отправленного основным процессом.
Как заставить экран пули двигаться, все должны быть знакомы с этим, просто постоянно обновляйте положение экрана пули.
Здесь мы должны поговорить об артефакте window.requestAnimationFrame(). Этот метод сообщает браузеру, что он хочет выполнить анимацию, и просит браузер вызвать указанную функцию для обновления анимации перед следующей перерисовкой. Этот метод принимает в качестве параметра функцию обратного вызова, которая будет вызываться перед перерисовкой браузера.
Конкретная реализация здесь повторяться не будет.
Пакетные приложения
Вышеизложенное кратко представляет ключевые моменты разработки этого приложения.После завершения разработки наступает этап упаковки.
Мы используемelectron-builderУпаковка приложений может быть легко выполнена.
Установите модуль:
npm install electron-builder --save-dev
Добавьте в package.json следующее:
"scripts": {
"start": "electron .",
"pack:win": "electron-builder --win --ia32",
"pack:mac": "electron-builder --mac"
},
"build": {
"appId": "com.Barrage.app",
"productName": "弹幕666",
"copyright": "Copyright © 2018 ${author}",
"electronVersion": "3.0.4",
"mac": {
"icon": "ico/favicon.icns",
"artifactName": "${productName}_Setup_${version}.${ext}"
},
"win": {
"target": "nsis",
"icon": "ico/favicon.ico",
"artifactName": "${productName}_Setup_${version}.${ext}"
}
}
Команда упаковки:
npm run pack:win
//or
npm run pack:mac
Подробности смотрите в документации официального сайта.
разное
- Автоматическое обновление: это также должно быть обязательной частью, мы можем использовать electronic-updater для настройки функции автоматического обновления для нашего приложения. Я не буду говорить об этом здесь.
- Сбор журнала ошибок. Полное приложение должно иметь записи журнала ошибок.
- more...
Суммировать
В этой статье изложены ключевые моменты разработки приемника Electron. На самом деле, объяснение каждого пункта занимает много места, и нет необходимости объяснять все детали четко. Лучшее решение — практиковать это самостоятельно.Когда вы сталкиваетесь с проблемами и решаете проблемы, вы обязательно что-то приобретете.
Спасибо за терпеливое чтение. Если есть какие-либо ошибки или упущения выше, пожалуйста, поправьте меня!
позже
Когда я только что набирал код, на экране вдруг всплыло несколько слов, что было по-настоящему страшно. После проверки это должно быть, потому что на скриншотах в этой статье есть QR-код моего клиента, и друг отсканировал код, чтобы испытать волну, поэтому я получил его.
Итак, быстро добавьте версию раннего доступа. Функция еще не идеальна, багов должно быть много.
Скачать опыт
@Author: TDGarden