共计 5053 个字符,预计需要花费 13 分钟才能阅读完成。
窗口通信与拓展屏
本期内容次要介绍拓展屏的开启以及两个窗口之间的音讯通信。
拓展屏
在窗口启动这一节里咱们封装了一个createWindow
,用这个来创立一个窗口,拓展屏也是窗口,只不过咱们依据有几个显示屏把其放在对应的外接屏上罢了。
渲染过程
咱们在渲染过程增加一个按钮,让其点击之后,向主过程发送对拓展屏的操作(关上或暗藏)
<a-button type="primary" @click="openScreen"> 关上拓展屏 </a-button>
const state = reactive({
open: true,
message: ''
})
async function openScreen() {
await window.ipcRenderer.invoke('win-subScreen', {
open: state.open,
path: '#/subScreen'
})
state.open = !state.open
}
主过程
主过程承受到之后,通过 screen.getAllDisplays()
获取当初窗口的数组(有几个显示屏),而后依据 externalDisplay
来获取咱们外接屏幕的信息。
同样的,咱们创立新的窗口也是用 createWindow
,之前咱们渲染过程传递了一个 path,这个 path 呢就是咱们拓展屏要展现的页面。
这里须要留神的是 createWindow 返回的值须要用一个全局变量保存起来,之前托盘的时候说过,如果是局部变量的话,在函数执行结束之后会被销毁,那么窗口也就被销毁了。
咱们这里用 global
赋值是前面窗口通信会用到,你这里用全局变量赋值时一样的。
global.js
global.tray = null
global.willQuitApp = false
global.envConfig = {}
global.sharedObject = {
win: '',
subScreen: ''
}
export default global
import {ipcMain, app, screen} from 'electron'
import global from '../config/global'
import createWindow from './createWindow'
import path from 'path'
const win = global.sharedObject.win
ipcMain.handle('win-subScreen', (_, data) => {if (data.open) {const displays = screen.getAllDisplays()
const mainBounds = win.getNormalBounds()
const externalDisplay = displays.find((display) => {return display.bounds.x !== 0 || display.bounds.y !== 0})
if (externalDisplay) {if (global.sharedObject.subScreen) {global.sharedObject.subScreen.show()
} else {
global.sharedObject.subScreen = createWindow({
frame: false,
show: false,
parent: win, // win 是主窗口
fullscreen: true,
webPreferences: {
webSecurity: false,
contextIsolation: false,
enableRemoteModule: true,
nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION,
plugins: true,
preload: path.join(__dirname, 'preload.js'),
devTools: false
},
x: mainBounds.x < 0 && Math.abs(mainBounds.x) > (win.getContentSize()[0] / 2) ? 0 : externalDisplay.bounds.x,
y: externalDisplay.bounds.y
}, data.path, `index.html${data.path}`)
global.sharedObject.subScreen.once('ready-to-show', () => {global.sharedObject.subScreen.show()
})
global.sharedObject.subScreen.on('closed', () => {global.sharedObject.subScreen = null})
}
} else {console.log('未检测到拓展屏')
}
} else {global.sharedObject.subScreen && global.sharedObject.subScreen.destroy()
}
})
这里呢有个小解决,咱们先判断一下咱们的软件是位于那个屏幕,比方咱们有两个屏,1 和 2,那么咱们的主窗口和拓展屏应该处于不同地位,比方:咱们的主窗口在 1 的话,那么咱们的拓展屏应该在 2 关上,如果地位雷同的话,因为咱们拓展屏设置的是全屏,此时会把主窗口给遮挡住,如果没有设置快捷键敞开的话,那么将无奈敞开拓展屏,故有以下解决。
const mainBounds = win.getNormalBounds()
mainBounds 为主窗口信息,如果主窗口有一半以上都处于副屏幕的话,那么咱们认为主窗口在副屏,那么拓展屏关上地位应该在主屏,否则的话应该在副屏。x: mainBounds.x < 0 && Math.abs(mainBounds.x) > (win.getContentSize()[0] / 2) ? 0 : externalDisplay.bounds.x
窗口通信
electorn 的窗口间的通信呢,一般来说有两种形式。
- 窗口 A 渲染过程发送信息到主过程,主过程收到后发送信息给窗口 B 的渲染过程,也就是用主过程做个直达。
- 窗口 A 渲染过程中通过窗口 B 的
WebContents.id
间接发送信息给窗口 B。
通信形式
这里介绍几种常见的通信形式
ipcRenderer.invoke:渲染过程发送音讯给主过程,这是一个 promise,能够在其 resolve 中获取 ipcMain.handle 的返回值
ipcMain.handle:接管 invoke 发送的信息,能够 return 值给 ipcRenderer.invoke
ipcRenderer.send:渲染过程发送音讯给主过程
ipcMain.on:接管 send 的信息
ipcRenderer.on:接管主过程的音讯
webContents.send:主过程发送音讯给渲染过程
ipcRenderer.sendTo:能够通过 webContentsId 间接发送信息到对应的渲染过程窗口
咱们这里把 1,2 都实现一下
渲染过程
渲染过程中能够通过 remote
来获取主过程中 global
的值,remote.getGlobal('sharedObject').subScreen
就是咱们之前拓展屏的窗口。transferSub
是咱们的计划 1,directSub
是计划 2。
div class="subMessage">
<a-textarea
v-model:value="state.message"
:auto-size="{minRows: 2, maxRows: 5}"
/>
<a-button type="primary" :disabled="state.message.length === 0" @click="transferSub"> 直达发送 </a-button>
<a-button type="primary" :disabled="state.message.length === 0" @click="directSub"> 间接发送 </a-button>
</div>
import {defineComponent, reactive, onMounted, onUnmounted, getCurrentInstance} from 'vue'
const {remote} = require('electron')
const state = reactive({
open: true,
message: ''
})
const {proxy} = getCurrentInstance()
const {$message} = proxy
function transferSub() {window.ipcRenderer.invoke('win-subScreen-message', state.message)
}
function directSub() {const subScreen = remote.getGlobal('sharedObject').subScreen
if (subScreen) {window.ipcRenderer.sendTo(subScreen.webContents.id, 'main-subScree', state.message)
}
}
onMounted(() => {window.ipcRenderer.on('subScree-main', (_event, data) => {$message.success(data)
})
})
onUnmounted(() => {window.ipcRenderer.removeListener('subScree-main')
})
主过程
直达一下,用 webContents.send
把信息发送给拓展屏窗口
ipcMain.handle('win-subScreen-message', (_, data) => {if (global.sharedObject.subScreen) {global.sharedObject.subScreen.webContents.send('renderer-subScreen-message', data)
}
})
拓展屏信息接管及发送
咱们这里间接监听直达的音讯以及间接发送的音讯,再间接告诉主窗口音讯。
<template>
<div class="subScreen">{{state.message}}</div>
</template>
<script>
import {defineComponent, reactive, onMounted, onUnmounted} from 'vue'
const {remote} = require('electron')
export default defineComponent({setup() {
const state = reactive({message: ''})
onMounted(() => {const win = remote.getGlobal('sharedObject').win
window.ipcRenderer.on('renderer-subScreen-message', (_event, data) => {
state.message = data
window.ipcRenderer.sendTo(win.webContents.id, 'subScree-main', '我收到了直达发送信息')
})
window.ipcRenderer.on('main-subScree', (_event, data) => {
state.message = data
window.ipcRenderer.sendTo(win.webContents.id, 'subScree-main', '我收到了间接发送信息')
})
})
onUnmounted(() => {window.ipcRenderer.removeListener('renderer-subScreen-message')
window.ipcRenderer.removeListener('main-subScree')
})
return {state}
}
})
</script>
验证
咱们关上拓展屏,在主窗口输出信息,点击间接发送或者直达发送,看看拓展窗口的显示是否为咱们输出的信息,主窗口展现拓展屏的告诉信息。
本系列更新只有利用周末和下班时间整顿,比拟多的内容的话更新会比较慢,心愿能对你有所帮忙,请多多 star 或点赞珍藏反对一下
本文地址:链接
本文 github 地址:链接