从头开始的电子手截图工具示例代码
最近尝试用电子把一个网络聊天工具打包成桌面APP。作为聊天工具,屏幕截图是必不可少的功能。不幸的是,我们找不到一个成熟的库来使用,或者我们可能以错误的方式打开了它。简而言之,我们没有看到它是现成的,所以我们想从一开始就采取一个简单的截图工具。让我们言归正传吧!
思路
Electron提供了一个抓取屏幕的API,可以轻松获取每个屏幕(如果有外接显示器)和每个窗口的图像信息。
剪下图片,然后创建一个全屏窗口覆盖整个屏幕,在窗口上画出剪下的图片,然后覆盖一层黑色半透明元素,看起来屏幕是固定的;增加窗口上交互选择的效果;点击确定,利用所选区域对应的画布位置捕捉图片内容,写入剪贴板,保存图片。构建项目
首先,创建package.json来填写项目的必要信息,注意main是入口文件。
{ ' name ' : ' electron-capture-screen ',' version': '1.0.0 ',' main': 'main.js ',' repository ' : ' https://github.com/Chris bing/electron-capture-screen . git ',' author': 'Chris ',' license': 'MIT ',' Scripts ' 3: { ' start ' 3: ' electronic。},' dependencies ' : { ' electron ' : ' 3 . 0 . 2 ' } }创建main.js,代码来自electron官方文档
const { app,BrowserWindow,ipcMain,global快捷键}=require(' electronic ')const OS=require(' OS ')//保留窗口对象的全局引用,如果不保留,当JavaScript对象被垃圾收集时,窗口将//自动关闭。让win函数创建window(){//创建一个浏览器窗口。Win=新建浏览器窗口({宽度: 800,高度: 600 })//然后加载应用的index.html。Win.loadFile('index.html') //打开开发工具win . web contents . opendevtools()//窗口关闭时会触发此事件。Win.on('closed '),()={//取消引用窗口对象。如果您的应用程序支持多个窗口,//它通常将多个窗口对象存储在一个数组中。//同时,您应该删除相应的元素。Win=null })}//电子将在初始化后和准备//创建浏览器窗口时调用此函数。//某些API只能在ready事件触发后使用。App.on('ready ',createWindow)//当所有窗口关闭时退出。在app上。(' window-all-closed ',()={//在macOS上,大多数应用程序及其菜单栏将保持活动状态//,除非用户使用Cmd Q. if退出(process.platform!=='达尔文'){app。退出()}})应用程序。on ('activate ',()={//在macOS上,当你点击dock图标且没有其他窗口打开时,//通常会在应用程序中创建新窗口。if(win===null){ createwindow()} })创建一个index.html,并在HTML中放置一个按钮来触发屏幕捕获操作
!DOCTYPE html html head meta charset=' UTF-8 '标题Hello World!/title/head dybutton id=' js-capture ' capture Screen/button script const { ipcRenderer }=require(' electronic ')document . getelementbyid(' js-capture ')。addeventlistener ('click ',()={ipcrender。send(' capture-screen ')})/script/body/html,这样一个简单的电子项目就完成了,你可以通过执行纱线启动或npm启动看到一个带有按钮的窗口。
触发屏幕截图
截图是一个相对独立的功能,可能会有全局快捷键和菜单触发器离开窗口的情况,所以截图的触发应该在主流程中实现
在渲染器过程中,可以通过ipc通信完成,使用ipcRenderer发送页面代码中的事件,使用IPC main接收main中的事件
//index.html const {IPC渲染器}=require(' electronic ')document . getelementbyid(' js-capture ')。addeventlistener ('click ',()={IPC renderer.send('截图')})在主进程中接收截图事件
//main.js//接收事件ipcmain。on ('capture-screen ',捕获屏幕)并添加全局快捷键来触发和取消屏幕捕获。
//main.js//注册全局快捷键//全局快捷方式需要在应用就绪之后全球的快捷键。寄存器(' CmdOrCtrl Shift A ',captureScreen)全局快捷键。注册(' Esc ',()={ if(captureWin){ captureWin }。close()(captureWin=null)})通过快捷键和事件来触发截屏方法捕获屏幕,接下来实现这个方法来创建一个截屏窗口
创建截屏窗口
截屏窗口是要创建一个全屏的窗口,并且把屏幕图片绘制在窗口上,再通过鼠标拖拽等交互操作选出特定区域的图像。
第一步是要创建窗口
//main。jslet captureWin=null const captureScreen=(e,args)={ if(captureWin){ return } const { screen }=require(' electron ')let { width,height }=screen。getprimarydisplay().界限捕获赢=新浏览器窗口({ //窗口使用全屏麦克设置为未定义,不可为假全屏: OS。platform()==' win32 ' | | undefined,//win width,height,x: 0,y: 0,transparent: true,frame: false,skipTaskbar: true,autoHideMenuBar: true,movable: false,resizable: false,enablelargerthansscreen 3360 true,//MAC hasshadow 3360 false,})调试用//captureWin。opendevtools()captureWin。on(' closed ',()={ captureWin=null })}窗口需要覆盖全屏,并且完全置顶,在窗子下可以使用全屏来保证全屏麦克下全屏会把窗口移到单独桌面,所以采用了另外的办法,代码注释上标注了不同系统的相关选项,具体内容可以查看文档
注意这里窗口加载了另外一个超文本标记语言文件,这个文件用来负责截屏和裁剪的一些交互工作
capture.html
首先超文本标记语言结构
//捕获。html div id=' js-BG ' class=' BG '/div div id=' js-mask ' class=' mask '/div canvasdiv canvasdiv ' class=' image-canvasdiv '/canvasdiv id=' js-size-info ' class=' size-info '/div div id=' js-toolbar ' div class=' icon font icon-zhi ' id=' js-tool-reset '/div class=' icon font icon-夏紫'截屏图片面罩:一层灰色遮罩帆布:绘制选中的图片区域和边框尺寸信息:标识截取范围的尺寸工具栏:操作按钮,用来取消和保存等捕获-渲染器。js : js代码
@import ./资产/图标字体/图标字体。CSS ';html,body,div { margin : 0;padd : 0;盒子尺寸:边框盒子;}.掩模{位置:绝对值;top : 0;左: 0;宽度: 100%;高度: 100%;background: rgba(0,0,0,0.6);}.bg {位置:绝对值;top : 0;左: 0;宽度: 100%;高度: 100%;}.图像-画布{位置:绝对值;显示器:无;z-index : 1;}.size-info { position :绝对值;color : # fffffont-size : 12px;背景: rgba(40,40,40,0.8);padding: 5px边框-半径: 2pxfont-family: Arial Consolas无衬线字体;显示器:无;z-index : 2;}.工具栏{位置:绝对值;color : # 343434 font-size : 12px;background : # f5f 5 F5 padding : 5px 10px border-radius : 4px;font-family: Arial Consolas无衬线字体;显示器:无;box-shadow: 0 0 20px rgba(0,0,0,0.4);z-index : 2;align-items:居中;}.工具栏图标字体{ font-size : 24pxpadding: 2px 5px }各个元素基本为绝对的定位,由射流研究…控制位置按钮使用了图标字体,所有涉及到的资源文件和完整项目可以到GitHub-Chris bing/电子捕获-屏幕:电子捕获屏幕中下载
截图交互
完成的功能有截取指定区域图片,拖拽移动和改变选区尺寸,实时尺寸显示和工具条
获取屏幕截图
//捕获-渲染器。jsconst { ipcRenderer,剪贴板、nativeImage、remote、desktopCapturer、screen }=require(' electronic ')const Event=require(' events ')const fs=require(' fs ')const { bounds : { width,height },scale factor }=screen。getprimarydisplay()const $ canvas=document。getelementbyid(' js-canvas ')const $ BG=document。getelementbyid(' js-BG ')const $ sizeInfo=document .可以获取主屏幕的大小和缩放比例,缩放比例在高分屏中适用,在高分屏中屏幕的物理尺寸和窗口尺寸并不一致,一般会有2倍3倍等缩放倍数,所以为了获取到高清的屏幕截图,需要在屏幕尺寸基础上乘以缩放倍数
桌面捕获器获取屏幕截图的图片信息,获取的是一个数组,包含了每一个屏幕的信息,这里呢暂时只处理了第一个屏幕的信息
获取了截图信息后创建捕捉渲染器进行交互处理
捕捉渲染器
//捕获-渲染器。js类捕获渲染器扩展了事件{构造函数($canvas,$bg,imageSrc,scaleFactor) { super() //.this.init().然后(()={控制台。log(' init ')})} async init(){ this .$ BG。风格。背景图像=` URL($ { this。imagesrc })`这.$ BG。风格。背景尺寸=` $ { width } px $ { height } px ` let canvas=document。createelement(' canvas ')让CTX=canvas。getcontext(' 2d ')让img=等待新的Promise(resolve={ let img=new Image())img。src=这个。图像src if(img。完成){ resolve(img)} else { img。onload=()=resolve(img)})画布。宽度=img。身高CTX .} //.onMouseDrag(e) { //.this.selectRect={x,y,w,h,r,b} this.drawRect() this.emit('拖动,this.selectRect) //.} drawRect() { if(!this.selectRect) { this .$帆布。风格。display=' none ' return } const { x,y,w,h }=this。selectrect常量比例因子=这个。比例因子让边距=7让半径=5这.$帆布。风格。left=`$ { x-margin } px ` this .$帆布。风格。top=` $ { y-margin } px ` this .$帆布。风格。width=` $ { w margin * 2 } px ` this .$帆布。风格。高度=` $ { h边距* 2 } px ` this .$canvas.style.display='block '这个$canvas.width=(w边距* 2) *这个比例因子.$帆布。高度=(h边距* 2)*比例因子if(w h){让imageData=this。bgctx。getimagedata(x *比例因子,y *比例因子,w *比例因子,h *比例因子)这个。CTX。putimagedata(imageData,margin * scaleFactor,margin * scale factor)}这个。CTX。填充样式=' # ffffff '这个。CTX。strokestyle=' # 67 bade '这个。CTX。线宽=2 *这个。比例因子这个。中强.} onMouseMove(e) { //.文件。尸体。风格。光标='移动'//.} OnMousePu(e){ this。发出(' end-draking ')这个。draw rect()} getImageUrl(){ const { x,y,w,h }=this。selectrect if(w h){让imageData=this。bgctx。getimagedata(x *比例因子,y *比例因子,w *比例因子,h *比例因子)让canvas=document。createelement(' canvas ')让CTX=canvas。getcontext(' 2d ')CTX。putimagedata(imageData,0,0)返回帆布.}}代码有点长,由于篇幅的原因,这里只列出了关键部分,完整代码请到GitHub-Chris bing/电子捕获-屏幕:电子捕获屏幕上查看
初始化时保存一份绘制了全部图片的帆布,用来后续取选区部分图片用
绘制过程中从通过帆布中的getImageData获取图片内容然后通过putImageData绘制到显示帆布中
附加内容
在捕捉渲染器类中处理了图片的选取。还需要工具条和尺寸信息
这一部分代码和图片选取关系不是很大,所以在外部单独处理,通过捕捉渲染器传出的事件和一些属性即可完成交互
//捕获-渲染器。jslet onDrag=(selectRect)={ $ toolbar。风格。display=' none ' $ sizeinfo。风格。display=' block ' $ sizeinfo。内部文本=` $ { selectRect。w } * $ { selectRect。h } ` if(selectRect。y 35){ $ sizeinfo。风格。top=` $ { selectRect。y-30 } px ` } else { $ sizeinfo。风格。top=` $ { selectRect。y . y 10 } px ` } $移动过程中计算尺寸,并且实时计算位置,移动过程中隐藏工具条
重置选区时隐藏工具条和尺寸标识
保存剪贴板
//捕获-渲染器。jsconst Audio=新音频()。音频。src=' ./资产/音频/捕获。MP3 ' let selectCapture=()={ if(!capture.selectRect) { return }让URL=捕获。getimageurl()远程。getcurrentwindow().隐藏()。音频。播放()。音频。onended=()={ window。close()}剪贴板。写入图像(nativeimage。create from datarurl(URL))ipcrenderer。send(' capture-screen ',{ type: 'complete ',URL,})} $ btnok。添加事件侦听器(单击,选择捕获)通过nativeImage.createFromDataURL创建图片写入剪贴板,通知主要的进程截图完毕,并附带图片的base64 url,然后关闭窗口
保存到文件
//捕获-渲染器。js $ btnsave。addeventlistener(' click ',()={ let URL=capture。getimageurl()。远程。getcurrentwindow().隐藏()。远程。对话。showsave对话框({ filter s :[{ name : ' Images },extensions: ['png ',' jpg ',' gif ']}),函数(路径){ if(路径){ fs。写入文件(路径),新的缓冲区(网址。替换(' data : image/png;base64 ',''),' base64 '),function(){ ipcrenderer。发送('屏幕截图',{ type: 'complete ',url,路径,})窗口。close()})else { ipcrenderer。send(' capture-screen ',{ type: 'cancel ',url,}) window.close() }) })利用remote.dialog.showSaveDialog选择保存文件名,然后通过满量程模块写入文件
最终整体目录结构
index.html利卜//截图核心代码 资产//字体和声音资源 捕捉-main.js //main中截图部分代码 捕捉-渲染器。js /截图交互代码capture.html//截图html主. js 包。数据坑点总结
开发过程中主要遇到了几个坑
首先全屏窗口,在窗子和苹果个人计算机上存在不同处理,而且老兄上这个方案在网上没有查到,最后翻阅文档无意中发现的
然后就是选区过程中,各个位置,选区的拖拽操作,需要大量时间调试
再有就是开发过程中代码可能出错,导致全屏窗口盖在屏幕上无法去掉,最后通过老兄触摸板五指张开的手势隐藏了窗口才关掉了程序
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。
版权声明:从头开始的电子手截图工具示例代码是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。