使用Electron自制錄屏軟件
前言
錄屏軟件對于我們來說都不陌生了,今天我們要做的事情是實現(xiàn)自己的錄屏軟件。載體使用Electron
,因為它更適合錄制桌面的場景。我們今天實現(xiàn)的錄屏軟件會包括下面的功能
- 分辨率調(diào)節(jié)
- 幀率調(diào)節(jié)
- 支持保存為
webm
、mp4
、gif
格式
本文用到的技術(shù)包括:
electron
ffmpeg
vite
antd
下面話不多說,我們馬上進入實戰(zhàn)環(huán)節(jié)
環(huán)境搭建
我個人習慣的技術(shù)棧是React+Vite
,所以這次搭建這個Electron
腳手架的時候我也往這方面去搭建。搭建過程中發(fā)現(xiàn)了一個很好的腳手架工具——electron-vite。需要搭建Electron
項目的兄弟們也可以考慮用這個。功能還是十分強大的,用起來十分舒服,很多東西都預設(shè)好了
直接下面的一條命令就可以搭建一個基礎(chǔ)框架:
npm create @quick-start/electron
搭建完框架之后,下面現(xiàn)在寫一下簡單的配置頁面,因為我們是配置錄制的各種參數(shù)的。配置頁面如下:
就是使用了antd
下了幾個下拉框,還是比較簡單的。
這里寫的是渲染進程的代碼,所以我們寫在了renderer/src/App.jsx
里。做Electron
開發(fā)心里需要清楚,這種頁面相關(guān)的,就寫在渲染進程里,跟我們平時寫web
頁面無異。
另外,我們的配置肯定是要持久化存儲的,所以我會把這些配置存儲在localStorage
里。
useEffect(() => { //清晰度 setLocal(DEFINITION, definition) }, [definition]) useEffect(() => { // 幀率 setLocal(FRAME_RATE, frameRate) }, [frameRate]) useEffect(() => { // 產(chǎn)物拓展名 setLocal(EXT, ext) }, [ext])
以下是三個下拉框的選項配置,可以稍微看一下,后續(xù)會作為參數(shù)使用到錄屏功能中
const DEFINITION_LIST = [ { label: '超清', value: '3840x2160' }, { label: '高清', value: '1280x720' }, { label: '標清', value: '720x480' } ] const FRAME_RATE_LIST = [ { label: '高', value: '60' }, { label: '中', value: '30' }, { label: '低', value: '15' } ] const EXT_LIST = [ { label: 'webm', value: 'webm' }, { label: 'mp4', value: 'mp4' }, { label: 'gif', value: 'gif' } ]
IPC通信
在Electron
中,包括渲染進程跟主進程。渲染進程就是我們寫的頁面,主進程就是跟Electron
窗體相關(guān)的,或者是需要調(diào)用一些node
模塊的邏輯。在主進程跟渲染進程是需要相互通信的,這里的通信方式就是IPC
。
下面介紹兩個例子,幫助理解這兩個進程如何通信
渲染進程發(fā),主進程收
下面需要在主進程中實現(xiàn)錄屏的功能,所以我們頁面的配置需要發(fā)送到主進程。那么就可以通過這種方式發(fā)送到主進程中。
//渲染進程發(fā)送 useEffect(() => { const options = { definition, frameRate, ext } window.electron.ipcRenderer.send(RECORD_EVNET.SET_CONFIG, options) }, [definition, frameRate, ext])
而主進程則可以通過下面的方式來接收
import { ipcMain } from 'electron' let config = {} ipcMain.on(RECORD_EVNET.SET_CONFIG, (e, data) => { config = data })
主進程發(fā),渲染進程收
下面介紹另外一種,主進程發(fā)送給渲染進程
//主進程發(fā)送 const mainWindow = new BrowserWindow({ width: 900, height: 670, show: false, // frame: false, // autoHideMenuBar: true, ...(process.platform === 'linux' ? { icon } : {}), webPreferences: { preload: join(__dirname, '../preload/index.js'), sandbox: false } }) mainWindow.webContents.send('event-name', { name:'jayliang' })
渲染進程接收
window.electron.ipcRenderer.on('event-name', callback)
系統(tǒng)托盤
下面要實現(xiàn)的是系統(tǒng)托盤的功能,方便我們在各個窗口的時候也能快速喚起錄屏以及停止錄屏。
具體代碼實現(xiàn)如下
import { Menu, Tray, nativeImage, } from 'electron' let trayIcon = nativeImage.createFromPath(icon) // 設(shè)置圖標大小 trayIcon = trayIcon.resize({ width: 16, height: 16 }) const tray = new Tray(trayIcon) const contextMenu = Menu.buildFromTemplate([ { label: '開始', type: 'normal', click: startRecording }, { label: '停止', type: 'normal', click: stopRecording }, { type: 'separator' }, { label: '退出', type: 'normal', role: 'quit' } ]) tray.setToolTip('你的應用名稱') tray.setContextMenu(contextMenu)
可拖拽窗體
這個功能跟錄屏軟件無關(guān),覺得這是一個很有意思的功能,就把它實現(xiàn)了哈哈哈。實現(xiàn)的方法也很簡單,具體代碼如下
mainWindow.webContents.on('did-finish-load', () => { mainWindow.webContents.executeJavaScript(` document.addEventListener('mousedown', (e) => { if (e.target.tagName !== 'INPUT' && e.target.tagName !== 'TEXTAREA') { window.isDragging = true; offset = { x: e.screenX - window.screenX, y: e.screenY - window.screenY }; } }); document.addEventListener('mousemove', (e) => { if (window.isDragging) { const { screenX, screenY } = e; window.moveTo(screenX - offset.x, screenY - offset.y); } }); document.addEventListener('mouseup', () => { window.isDragging = false; }); `) })
上面是在主進程中監(jiān)聽渲染進程的加載完成事件,往渲染進程中注入了一段javascript
代碼。這段js
代碼主要做的事情也是監(jiān)聽鼠標事件,然后調(diào)用moveTo
方法來移動整個窗體。
實現(xiàn)錄制
好的,前面啰嗦了那么多。終于到實現(xiàn)真正的錄制功能的時候了。先安裝一下ffmpeg
,這里使用的是ffmpeg-static
這個庫,安裝這個庫的時候它會識別你的操作系統(tǒng),然后去安裝編譯好的ffmpeg
二進制文件,最后導出的是一個ffmpeg
的路徑。
它里面用的下載鏈接可能會超時,所以可以設(shè)置一個鏡像地址:
export FFMPEG_BINARIES_URL=https://cdn.npmmirror.com/binaries/ffmpeg-static
打包的命令記得改一下,為的是安裝不同操作系統(tǒng)的ffmpeg
"build:win": "rm -rf ./node_modules && npm run build && electron-builder --win --config", "build:mac": "rm -rf ./node_modules && npm run build && electron-builder --mac --config", "build:linux": "rm -rf ./node_modules && npm run build && electron-builder --linux --config"
安裝完之后就可以實現(xiàn)開始錄制以及停止錄制功能了,實現(xiàn)的代碼如下:
import ffmpegPath from 'ffmpeg-static' const startRecording = async () => { let ffmpegCommand const { ext, frameRate, definition } = config const fileName = `${app.getPath('downloads')}/${+new Date()}.${ext}` if (ext === 'webm') { ffmpegCommand = `${ffmpegPath} -f avfoundation -framerate ${frameRate} -video_size ${definition} -i "1" -c:v libvpx-vp9 -c:a libopus ${fileName}` } else if (ext === 'mp4') { ffmpegCommand = `${ffmpegPath} -f avfoundation -framerate ${frameRate} -video_size ${definition} -i "1" -vsync vfr -c:v libx264 -preset ultrafast -qp 0 -c:a aac ${fileName}` } else if (ext === 'gif') { ffmpegCommand = `${ffmpegPath} -f avfoundation -framerate ${frameRate} -video_size ${definition} -i "1" -vf "fps=15" -c:v gif ${fileName}` } ffmpegProcess = spawn(ffmpegCommand, { shell: true }) ffmpegProcess.stderr.on('data', (data) => { console.error(`FFmpeg Error: ${data}`) }) ffmpegProcess.on('exit', (code, signal) => { console.log(`Recording process exited with code $[code] and signal ${signal}`) }) } const stopRecording = () => { if (ffmpegProcess) { ffmpegProcess.kill('SIGINT') // 發(fā)送中斷信號停止錄制 } }
解釋一下上面的代碼,ext
是拓展名、frameRate
是幀率、definition
是分辨率,這些都是從渲染進程中獲取的。
不同的錄制產(chǎn)物對應不同的ffmpeg
命令,開始錄制時使用spawn
開啟一個子進程去調(diào)用ffmpeg
錄制,并保存子進程的飲用,在點擊托盤的停止按鈕時,向子進程發(fā)送停止命令。
這樣我們就可以愉快的錄制屏幕,并保存為我們想要的樣子啦
以上還是存在不足:avfoundation
是用在mac
的采集庫,在Windows
下應該是用不了的,所以希望后續(xù)有時間也把windows
版本給兼容了~
以上就是使用Electron自制錄屏軟件的詳細內(nèi)容,更多關(guān)于Electron錄屏的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
動態(tài)加載iframe時get請求傳遞中文參數(shù)亂碼解決方法
這篇文章主要介紹了動態(tài)加載iframe時get請求傳遞中文參數(shù)亂碼解決方法,需要的朋友可以參考下2014-05-05同一個網(wǎng)頁中實現(xiàn)多個JavaScript特效的方法
這篇文章主要介紹了同一個網(wǎng)頁中實現(xiàn)多個JavaScript特效的方法,實例分析了多個特效的實現(xiàn)原理與相關(guān)技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-02-02js拆分字符串并將分割的數(shù)據(jù)放到數(shù)組中的方法
這篇文章主要介紹了js拆分字符串并將分割的數(shù)據(jù)放到數(shù)組中的方法,涉及javascript中split方法及數(shù)組的操作技巧,需要的朋友可以參考下2015-05-05