Electron实现悬浮球功能

7,358 阅读3分钟

基础准备

了解electron框架的同学知道,electron主要分为两个进程,主进程和渲染进程。 主进程通过NodeJs实现底层的调用,而渲染进程则通过Chromium实现界面的渲染。详情见

功能阐述

在功能实现时,多少会遇到渲染进程与主进程的通信或者界面间的通信。接下来就实现一个最简单的类似360悬浮球效果。主要会涉及进程间通信功能、electron菜单功能等

效果图

实现思路

1.悬浮框的展示可通过变量控制,通过变量的true/false结合ipc通信控制悬浮框的show()/hide()。

2.右键点击悬浮框时,通过ipc通信打开菜单

3.由于在页面需要通过electorn.ipcRenderer()方法进行通信,而electron在页面层导入时需要remote/ipcRenderer等内置方法,所以可以通过import或者require引入electron对象。但是务必需要将BrowserWindow.webPreferences.nodeIntegration参数设置为true,否则将会报:require is not defined的错误。

4.写一个基础的悬浮框页面Suspension.vue并获取对应的页面路由pageUrl,通过new browserWindow().loadUrl(pageUrl)将页面渲染出。

5.悬浮款鼠标效果:

  • 制作点击鼠标左键且移动鼠标时的滑动效果
  • 制作点击鼠标右键时的菜单效果
  • 实现思路: 在Suspension的元素上设置监听器,通过监听鼠标mousedown与mousemove结合来实现左键点击悬浮框且进行拖动事件的监听,以及通过监听右键的点击时间发起ipc通信,通过electron渲染菜单栏

6.主进程收到鼠标右键触发的事件后,创建菜单。

主要代码

import {BrowserWindow, ipcMain, screen, Menu, shell, app, webContents} from 'electron'
const winURL = process.env.NODE_ENV === 'development' ? `http://localhost:3000/main_window#/suspension` : `file://${__dirname}/index.html/main_window#/suspension`;
var win = null;

//主进程js
ipcMain.on('showSuspensionWindow', () => {
    if (win) {
        win.showInactive();
    } else {
        createSuspensionWindow();
    }
});

ipcMain.on('createSuspensionMenu', (e) => {
    const rightM = Menu.buildFromTemplate([
        {label: '功能1-1', enabled: false},
        {label: '功能1-2', enabled: false},
        {label: '功能1-3'},
        {type: 'separator'},
        {label: '功能2-1'},
        {type: 'separator'},
        {label: '功能3-1'},
        {label: '功能3-2'},
        {
            label: '退出软件',
            click: () => {
                app.quit();
            }
        },
    ]);
    rightM.popup({});
});

function createSuspensionWindow() {
    win = new BrowserWindow({
        width: 60, 
        height: 60,
        type: 'toolbar',    //创建的窗口类型为工具栏窗口
        frame: false,   //要创建无边框窗口
        resizable: false, //禁止窗口大小缩放
        webPreferences: {
            nodeIntegration: true,
        },
        transparent: true,  //设置透明
        alwaysOnTop: true,  //窗口是否总是显示在其他窗口之前
    });
    const size = screen.getPrimaryDisplay().workAreaSize;   //获取显示器的宽高
    const winSize = win.getSize();  //获取窗口宽高
    win.setPosition(size.width - winSize[0], 100);
    win.loadURL(winURL);

    win.once('ready-to-show', () => {
        win.show()
    });

    win.on('close', () => {
        win = null;
    })
}

//渲染进程vue界面
<template>
    <div id="suspension">
        <div class="content_body">
            <div class="suspension_ball">悬浮框</div>
        </div>
    </div>
</template>
<script>
    import {remote, ipcRenderer} from 'electron'
    export default {
        name: "suspension",
        mounted() {
            let win = remote.getCurrentWindow();
            let biasX = 0;
            let biasY = 0;
            let that = this;
            document.addEventListener('mousedown', function (e) {
                switch (e.button) {
                    case 0:
                        biasX = e.x;
                        biasY = e.y;
                        document.addEventListener('mousemove', moveEvent);
                        break;
                    case 2:
                        ipcRenderer.send('createSuspensionMenu');
                        break;
                }
            });
            document.addEventListener('mouseup', function () {
                biasX = 0;
                biasY = 0;
                document.removeEventListener('mousemove', moveEvent)
            });
            function moveEvent(e) {
                win.setPosition(e.screenX - biasX, e.screenY - biasY)
            }
        }
    }
</script>

总结

  • ipcMain和ipcRenderer都是EventEmitter的实例,用法上类似于emit-on的通信。
  • 在渲染进程中通过import引入electron的内置方法时,需将网页窗口的BrowserWindow.webPreferences.nodeIntegration设置为true