^ _ ^
技术关键点
- 怎么捕获画面? ==> Electron desktopCapture
- 怎么完成用户间连接、画面+指令传输? ==> WebRTC
- 怎么相应控制指令? ==> robotjs
目录架构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| |------- package.json |------- app | |--- common | | |--- ipc-channel.js | | |--- util.js | |--- main | | |--- index.js | | |--- ipc.js | | |--- windows | | | |--- control.js | | | |--- main.js | |--- renderer | | |--- pages | | | |--- control | | | | |--- index.html | | | |--- main | | |--- src |------- resource |------- release |------- dist
|
搭建项目结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| mkdir remote-control cd remote-control npm init -y mkdir app cd app mkdir main mkdir renderer cd renderer mkdir src mkdir pages cd pages mkdir control
# 进入src目录 cd ../src npx create-react-app main --use-npm # 下载完成后进入main目录 cd main # 启动程序 npm start # 打开localhost:3000可以看到应用创建成功
# 进入app下的main目录 cd ../../../main # 添加index.js文件
|
安装依赖
进入项目根目录
electron依赖
1
| cnpm install electron --save-dev
|
electron-is-dev依赖
用于生产环境和开发环境的判断
1
| cnpm install electron-is-dev --save
|
index.js文件
在目录 app/main下
1 2 3 4 5 6 7 8 9 10 11 12
| const { app, BrowserWindow } = require('electron')
app.on('ready', () => { const win = new BrowserWindow({ width: 800, height: 600, webPreferences: { nodeIntegration: true } }) win.loadURL("http://localhost:3000") })
|
修改项目根目录下的package.json文件
1 2 3 4 5
| "main": "app/main/index.js", "scripts": { "start:main": "electron", "start:render": "cd app/renderer/src/main && npm start" }
|
启动命令:
1 2
| npm run start:render npm run start:main
|
输入两次命令可能会比较麻烦,想要一步操作需要进行一些配置。
- 首先是在项目根目录下安装
concurrently 和 wait-on 模块1 2
| cnpm install concurrently --save-dev cnpm install wait-on --save-dev
|
- 在根目录的
package.json 下的 scripts 中增加一条 start 命令1
| "start": "concurrently \"npm run start:render\" \"wait-on http://localhost:3000 && npm run start:main\"",
|
- 终端输入命令
App.js文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| import logo from './logo.svg'; import './App.css'; import React, {useState, useEffect} from 'react';
const {ipcRenderer} = window.require('electron')
function App() { const [remoteCode, setRemoteCode] = useState('') const [localCode, setLocalCode] = useState('') const [controlText, setControlText] = useState('') const login = async () => { let code = await ipcRenderer.invoke('login') setLocalCode(code) } useEffect(() => { login() ipcRenderer.on('control-state-change', handleControlState) return () => { ipcRenderer.removeListener('control-state-change', handleControlState) } }, [])
const startControl = (remoteCode) => { ipcRenderer.send('control', remoteCode) }
const handleControlState = (e, name, type) => { let text = '' if(type == 1){ text = `正在远程控制${name}` } else if(type == 2){ text = `被${name}控制中` } setControlText(text) }
return ( <div className="App"> { controlText === '' ? <> <div>你的控制码 {localCode}</div> <input type="text" value={remoteCode} onChange={e => setRemoteCode(e.target.value)}/> <button onClick={() => startControl(remoteCode)}>确认</button> </> : <div>{controlText}</div> } </div> ); }
export default App;
|
ipc.js
在目录 app/main下
1 2 3 4 5 6 7 8 9
| const {ipcMain} = require('electron')
module.exports = function(){ ipcMain.handle('login', async () => { let code = Math.floor(Math.random() * (999999 - 100000)) + 100000 return code }) }
|
main.js
在 app/main/windows 目录下