基于Electron + nodejs + 小程序 实现弹幕小工具(上篇)

栏目: Node.js · 发布时间: 5年前

内容简介:上一篇文章,大概讲述我们即将要做的弹幕小工具是什么样的,将使用什么样的技术。那么,从这一篇开始,我们将一步步把想法落地成代码。本文,我们将使用Electron实现接收端,让我们的弹幕飞起来。如上图所示,把放映PPT的同时,用户可以通过扫描小程序二维码,实时发表自己的想法,达到互动的效果。如果你已经对Electron有了一定的了解,可跳过这部分的内容。

上一篇文章,大概讲述我们即将要做的弹幕小 工具 是什么样的,将使用什么样的技术。那么,从这一篇开始,我们将一步步把想法落地成代码。本文,我们将使用Electron实现接收端,让我们的弹幕飞起来。

效果图

基于Electron + nodejs + 小程序 实现弹幕小工具(上篇)

如上图所示,把放映PPT的同时,用户可以通过扫描小程序二维码,实时发表自己的想法,达到互动的效果。

Electron基本介绍

如果你已经对Electron有了一定的了解,可跳过这部分的内容。

Electron是由Github开发,用HTML,CSS和JavaScript来构建跨平台桌面应用程序的一个开源库。 Electron通过将Chromium和Node.js合并到同一个运行时环境中,并将其打包为Mac,Windows和 Linux 系统下的应用来实现这一目的。

在Electron中,主进程和渲染进程是一个很重要的概念。

主进程和渲染进程

一个进程是计算机程序执行中的一个实例。 Electron 应用同时使用了 main(主进程) 和一个或者多个 rendere(渲染进程) 来运行多个程序。

在 Node.js 和 Electron 里面,每个运行的进程包含一个 process对象。 这个对象作为一个全局的提供当前进程的相关信息和操作方法。 作为一个全局变量,它在应用内能够不用 require() 来随时取到。

主进程

主进程,通常是名为main.js 的文件,是每个 Electron 应用的入口文件。它控制着整个 App 的生命周期,从打开到关闭。 它也管理着系统原生元素比如菜单,菜单栏,Dock 栏,托盘等。 主进程负责创建 APP 的每个渲染进程。而且整个 Node API 都集成在里面。

每个 app 的主进程文件都定义在 package.json 中的 main 属性当中。这也是为什么 electron. 能够知道应该使用哪个文件来启动。

在Chromium中, 这个进程被称为 "浏览器进程"。它在Electron被重新命名, 以避免与渲染器进程混淆。

渲染进程

渲染进程是你的应用内的一个浏览器窗口。与主进程不同的是,它能够同时存在多个而且运行在不一样的进程。而且它们也能够被隐藏。

在通常的浏览器内,网页通常运行在一个沙盒的环境挡住并且不能够使用原生的资源。 然而 Electron 的用户在 Node.js 的 API 支持下可以在页面中和操作系统进行一些低级别的交互。

大体架构

从上面的效果图可以看出,界面主要由弹幕界面和小程序二维码组成。

结合上面说的主进程和渲染进程,我们可以将弹幕和二维码分别放在两个渲染进程中,主进程和nodejs服务端进行websocket通信,并将接收到的数据分发到对应的渲染进程。

基于Electron + nodejs + 小程序 实现弹幕小工具(上篇)

快速开始

官方有个快速开始的示例,我们可以通过这个示例来大概了解一下整个应用是怎么跑的。

# 克隆示例项目的仓库
$ git clone https://github.com/electron/electron-quick-start

# 进入这个仓库
$ cd electron-quick-start

# 安装依赖并运行
$ npm install && npm start
复制代码

我们在package.json可以看到,main属性的值为main.js,没错,那就是主进程的入口文件了。在这个入口文件中,通过BrowserWindow创建了一个浏览器窗口,并加载了一个html文件。是的,这就是所说的渲染进程。

这样看来,我们要做的事情其实还是很简单的,基本上和我们写静态页面一样。创建浏览器窗口的代码写好之后,剩下的就是写静态页面了。当然,还需要考虑数据通信的方案。

开始开发

创建浏览器窗口

在主进程中,通过API BrowserWindow 创建两个渲染进程,分别展示弹幕主界面和二维码。

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`);
}

复制代码

对于创建窗口的配置项,还是比较多的,大家可以大概通读一遍官方文档,知道到底能做些什么。

由于我们的弹幕是一直在最上面的,而且不能影响下层的操作,所以我们设置了透明、无边框、且可忽略了鼠标事件(点击可穿透),这样在弹幕的同时,还可以看到下层窗口并进行相关操作。此外,我们还设置了禁止放大缩小,而且将弹幕窗口最大化。

系统托盘

由于不希望窗口出现在任务栏里,毕竟弹幕一直运行着,放在任务栏中,占地方且碍眼,我们更希望它出现在系统托盘中,如下图:

基于Electron + nodejs + 小程序 实现弹幕小工具(上篇)

上图为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服务端通信

在我们的设计中,接受端和服务端之间的通信,采用websocket协议。所以,除了创建渲染进程之外,主进程还有一个很重要的任务,就是和服务端建立websocket连接。

我们选择使用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);
    }
});

复制代码

在和服务器建立连接之后,我们发送了初始化的消息,我们约定,服务端收到初始化消息之后,会根据clientId生成带参数的小程序二维码,并将二维码的base64数据返回来。所以,我们接收到新消息的时候,对消息类型进行判断并转发到对应的渲染进程。

上面提到,需要提供一个唯一的客户端ID传给nodejs服务端,用于生成唯一的小程序二维码。我们可以使用以下方法生成:

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就一直不变,毕竟二维码变来变去,对用户是很不友好的。所以我们需要将这个ID写在本地文件中。偷懒,我们也可以使用别人写好的模块electron-store,原理就是把需要存储在本地的信息写在一个json文件中,各种特殊的路径可以通过app.getPath(name)来获取。

如app.getPath("appData")可以获取当前用户的应用数据文件夹,默认对应:

  • Windows:%APPDATA%
  • Linux:$XDG_CONFIG_HOME or ~/.config
  • macOS: ~/Library/Application Support

其他更多的路径可查看官方文档。

主进程和渲染进程之间通信

这里就要讲到ipcMain、ipcRenderer以及webContents了,他们都是EventEmitter类的一个实例。

ipcMain: 当在主进程中使用时,它处理从渲染进程发送出来的异步和同步信息。 从渲染进程发送的消息将被发送到该模块。

ipcRenderer:你可以使用它提供的一些方法从渲染进程发送同步或异步的消息到主进程。 也可以接收主进程回复的消息。

webContents:BrowserWindow对象的一个属性,其send方法可以向渲染进程发送异步消息,可以发送任意参数。

看看二维码的栗子:

//主进程中
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
复制代码

详细使用可以看看官网文档。


以上所述就是小编给大家介绍的《基于Electron + nodejs + 小程序 实现弹幕小工具(上篇)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

再启动

再启动

[日] 大前研一 / 田龙姬、金枫 / 中华工商联合出版社有限责任公司 / 2010-1 / 29.00元

1、“全球管理大师”、“日本战略之父”大前研一,职场励志最新巨作。 2、2010年1月中华工商联合出版社有限责任公司与日知公司继《货币战争2》《中国大趋势》之后,再度联手,重磅推出。 3、震撼中国职场的宗师级巨作,势必引领2010年中国职场4、世界著名出版商小学馆授予独家中文简体出版权。 5、试问,哪个老板不希望自己的员工不断实现自身的“再启动”呢? 6、只有不断激励鞭策自......一起来看看 《再启动》 这本书的介绍吧!

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试