如何實現基於Electron的截圖識字App(一)
之前看了下face++的介面,做了人臉融合簡單demo,順便瞧了瞧其他的介面,看到一個識別圖片文字的介面,突然靈機一動。
平時自己看到一些有趣的圖文,裡面的文字就想敲下來放印象筆記裡或者朋友圈裡裝一下文藝。但是關鍵這裡還要手動敲,如果截圖的時候,直接就把文字複製到剪貼簿就好了。於是就有了這個應用,實現並不複雜。大家跟我一起過一遍吧, 喜歡的可以star~
開始
初始化一個electron應用
參考electron官網, 打造你的第一個electron應用
$ yarn add electron -D 複製程式碼
目錄結構
├─ src │└─ main.js#入口檔案,主程序 │└─ index.html # 渲染程序的頁面 複製程式碼
package.json的scripts欄位新增以下
"start": "electron src/main.js" 複製程式碼
入口檔案
electron程序分為主程序和渲染程序。渲染程序相當於前端的UI渲染,可以類比成chrome的一個tab頁,一個tab就是一個程序。渲染程序由主程序管理,主程序相當於chrome的視窗,關閉所有tab頁面還在的那個視窗。有些功能模組只能由主程序來呼叫,渲染程序同理(比如截圖這個就只能在渲染程序)
我們的需求是
- 只需要一個托盤叫做tray(mac右上角,win左下角),不需要開啟具體的視窗介面

- CmdOrCtrl+Shift+V為截圖快捷鍵
const { app, BrowserWindow,globalShortcut ,Tray,Menu,ipcMain} = require('electron') const shell = require('electron').shell; const path=require('path') let win let srcPath=path.join(__dirname,"../src") let clip=true //建立托盤 function createTray () { tray = new Tray(`${srcPath}/images/font.png`) // 指定圖片的路徑,在github裡有 const contextMenu = Menu.buildFromTemplate([ //Menu型別有checkbox,radio,normal等 { label: 'clip', type: 'checkbox',click(){ clip=!clip },checked:true }, { label: 'about', click(){ //開啟預設瀏覽器 shell.openExternal('https://github.com/yokiyokiyoki/clip-font-app'); } }, { label: 'exit',click(){ app.quit() }} ]) tray.setToolTip('圖圖識字') tray.setContextMenu(contextMenu) //註冊快捷鍵 globalShortcut.register('CmdOrCtrl+Shift+V', captureScreen) globalShortcut.register('Esc', () => { if (win) { win.close() win = null } }) } function createCaptureWindow() { // 建立瀏覽器視窗,只允許建立一個(必須得建立,因為只有渲染程序才能截圖) if(win)return console.info('只能有一個CaptureWindow') const { screen } = require('electron') //因為ready才可以引入 let { width, height } = screen.getPrimaryDisplay().bounds win = new BrowserWindow({ fullscreen: process.platform !== 'darwin' || undefined, // win width, height, x: 0, y: 0, transparent: true, frame: false, skipTaskbar: true, autoHideMenuBar: true, movable: false, resizable: false, enableLargerThanScreen: true, // mac hasShadow: false, webPreferences: { webSecurity: false //可以載入本地檔案,這裡不寫的話,打包後會報錯:不允許你載入本地檔案 } }) win.setAlwaysOnTop(true, 'screen-saver') // mac win.setVisibleOnAllWorkspaces(true) // mac win.setFullScreenable(false) // mac // 然後載入應用的 index.html。 win.loadFile(path.join(__dirname,'../index.html')) // 開啟開發者工具 win.webContents.openDevTools() // 當 window 被關閉,這個事件會被觸發。 win.on('closed', () => { win = null }) } app.on('ready', createTray) // 當全部視窗關閉時退出。 app.on('window-all-closed', () => { // 在 macOS 上,除非使用者用 Cmd + Q 確定地退出, // 否則絕大部分應用及其選單欄會保持啟用。 if (process.platform !== 'darwin') { app.quit() } }) app.on('activate', () => { if (win === null) { createCaptureWindow() } }) function captureScreen(){ if(clip){ createCaptureWindow() } } 複製程式碼
編寫index.html
上面我們通過主程序loadfile打開了index.html。這裡我們可以做一個粗淺的UI,需要有尺寸資訊,工具欄等。是基於全屏的html,為什麼要這麼做?你可以思考一下~
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>截圖</title> </head> <style> html, body, div { margin: 0; padding: 0; box-sizing: border-box; } .bg { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } .mask { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.6); } .rect { position: absolute; display: node; z-index: 1; } .size-info { position: absolute; color: #ffffff; font-size: 12px; background: rgba(40, 40, 40, 0.8); padding: 5px 10px; border-radius: 2px; font-family: Arial Consolas sans-serif; display: none; z-index: 2; } .toolbar { position: absolute; color: #343434; font-size: 12px; background: #f5f5f5; padding: 5px 10px; border-radius: 4px; font-family: Arial Consolas sans-serif; display: none; box-shadow: 0 0 20px rgba(0, 0, 0, 0.4); z-index: 2; align-items: center; } .toolbar>.iconfont{ display: inline-block; cursor: pointer; } </style> <body> <!--背景為灰色的遮罩層--> <div class="bg"></div> <div id="mask" class="mask"></div> <canvas class="rect"></canvas> <!--尺寸資訊--> <div class="size-info">200*200</div> <!--toolbar--> <div class="toolbar"> <div class="iconfont icon-close" >關閉</div> <div class="iconfont icon-check" >確認</div> <div class="iconfont icon-literacy" >識別</div> </div> <script src="./src/js/capture.js"></script> <!--現在還沒寫--> </body> </html> 複製程式碼
至此我們已經可以通過 npm start
進行開發,通過快捷鍵 CmdOrCtrl+Shift+V
看到這個頁面了,並且托盤也已經現在左上角。如果報錯,請審視上述之流程。
截圖功能實現
我們把邏輯寫在 capture.js
裡面
electron 提供了擷取螢幕的API(desktopCapturer),可以輕鬆的獲取每個螢幕(存在外接顯示器的情況)和每個視窗的影象資訊。可以先去了解一下~
原理(其實很簡單,就是兩個canvas):
1.通過electron提供擷取全屏生成的dataURL,存在一個特殊的canvas(姑且成為全屏canvas)裡面(dataURL和cavas的相愛相殺),再把這個dataURL賦給背景,讓空白背景圖,變成剛剛全屏的樣子(假象),再加上半透明的遮罩。
2.通過另外一個canvas(選區canvas)來製作選區,滑鼠位置來確定這個選區在全屏canvas裡的位置,然後把全屏canvas的資料導過來這個選區canvas
const {desktopCapturer, screen } = require('electron') const { bounds: { width, height } } = screen.getPrimaryDisplay() const path=require('path') const {Draw} = require(`${__dirname}/src/js/draw.js`) desktopCapturer.getSources({ types: ['screen'], thumbnailSize: { width, height } }, async(error, sources) => { if (error) return console.log(error) let screenImgUrl = sources[0].thumbnail.toDataURL() //獲取dataURL let bg=document.querySelector('.bg') let rect=document.querySelector('.rect') let sizeInfo=document.querySelector('.size-info') let toolbar=document.querySelector('.toolbar') /** * 繪製類 * ScreenImgUrl是整個螢幕base64格式的快照 * bg是背景dom * width是螢幕寬高 * rect是選區canvas * sizeInfo 尺寸資訊容器 * toolbar 工具欄 */ let draw=new Draw(screenImgUrl,bg,width,height,rect,sizeInfo,toolbar) document.addEventListener('mousedown',draw.startRect.bind(draw)) document.addEventListener('mousemove',draw.drawingRect.bind(draw)) document.addEventListener('mouseup',draw.endRect.bind(draw)) }) 複製程式碼
Draw類寫了截圖選區是如何實現的,這裡實在有些長,可以移步 github 。主要就是通過滑鼠畫一個矩形,然後把全屏canvas的資料通過位置定位到具體,然後匯入。

~
//渲染程序傳送訊息 ipcRenderer.send('clip-page', { type: type, message: msg }) //主程序接收 ipcMain.on('clip-page', (event, {type,msg}) => { if(type==='close'){ if (win) { win.close() win = null } } }) 複製程式碼
打包
我們在開發模式下試了下,覺得應該沒有問題了。這時候來打包成各平臺上的應用,供使用者實際使用~
這裡我們選用 electron-builder
,在package.json新建一個指令碼,同時新增一個build欄位(electron-builder自動讀取)
"scripts": { "start": "electron src/main.js", "build": "electron-builder", }, "build":{ //名字 "productName": "clip-font-app", "appId": "Personal.DesktopApp.ClipFont.1.0.0", "directories": { //打包目錄 "output": "dist" }, "files": [ //所有檔案 "./**/**" ], //win下的安裝嚮導 "nsis": { "oneClick": false, "allowElevation": true, "allowToChangeInstallationDirectory": true, "installerIcon": "src/images/icon.ico", "uninstallerIcon": "src/images/icon.ico", "installerHeaderIcon": "src/images/icon.ico", "createDesktopShortcut": true, "createStartMenuShortcut": true, "shortcutName": "ClipFont" }, "dmg": { "contents": [ { "x": 410, "y": 150, "type": "link", //是否拖到應用目錄 "path": "/Applications" }, { "x": 130, "y": 150, "type": "file" } ] }, "mac": { "icon": "src/images/icon.icns" }, "win": { "icon": "src/images/icon.ico", "target": [ { "target": "nsis", "arch": [ "ia32" ] } ] }, "linux": { "icon": "src/images" }, //下載源為淘寶映象,國內某些原因可能會導致失敗 "electronDownload": { "mirror": "http://npm.taobao.org/mirrors/electron/" } } 複製程式碼
然後我們 npm run build
,由於我使用的是mac,然後打包下來的是dmg~,雙擊dmg安裝了。

然後複製到剪貼簿試試