electron工具有什么用,在项目中的实际应用是怎样
最近在尝试利用 electron 将一个 web 版的聊天工具包装成一个桌面 APP。作为一个聊天工具,截屏可以说是一个必备功能了。不过遗憾的是没有找到很成熟的库来用,也可能是打开方式不对,总之呢没看到现成的,于是就想从头撸一个简单的截图工具。下面就进入正题吧!
思路
electron 提供了截取屏幕的 API,可以轻松的获取每个屏幕(存在外接显示器的情况)和每个窗口的图像信息。
- 把图片截取出来,然后创建一个全屏的窗口盖住整个屏幕,将截取的图片绘制在窗口上,然后再覆盖一层黑色半透明的元素,看起来就像屏幕定住了一样;
- 在窗口上增加交互制作选区的效果;
- 点击确定,利用 canvas 对应选区的位置截取图片内容,写入剪贴板和保存图片。
搭建项目
首先创建 package.json 填写项目的必要信息, 注意 main 为入口文件。
{
"name": "electorn-capture-screen",
"version": "1.0.0",
"main": "main.js",
"repository": "https://github.com/chrisbing/electorn-capture-screen.git",
"author": "Chris",
"license": "MIT",
"scripts": {
"start": "electron ."
}
,
"dependencies": {
"electron": "^3.0.2"
}
}
创建 main.js , 代码来自 electron 官方文档
const {
app, BrowserWindow, ipcMain, globalShortcut }
= require('electron')
const os = require('os')
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let win
function createWindow() {
// 创建浏览器窗口。
win = new BrowserWindow({
width: 800, height: 600 }
)
// 然后加载应用的 index.html。
win.loadFile('index.html')
// 打开开发者工具
win.webContents.openDevTools()
// 当 window 被关闭,这个事件会被触发。
win.on('closed', () =>
{
// 取消引用 window 对象,如果你的应用支持多窗口的话,
// 通常会把多个 window 对象存放在一个数组里面,
// 与此同时,你应该删除相应的元素。
win = null
}
)
}
// Electron 会在初始化后并准备
// 创建浏览器窗口时,调用这个函数。
// 部分 API 在 ready 事件触发后才能使用。
app.on('ready', createWindow)
// 当全部窗口关闭时退出。
app.on('window-all-closed', () =>
{
// 在 macOS 上,除非用户用 Cmd + Q 确定地退出,
// 否则绝大部分应用及其菜单栏会保持激活。
if (process.platform !== 'darwin') {
app.quit()
}
}
)
app.on('activate', () =>
{
// 在macOS上,当单击dock图标并且没有其他窗口打开时,
// 通常在应用程序中重新创建一个窗口。
if (win === null) {
createWindow()
}
}
)
创建 index.html , html 中放了一个按钮, 用来触发截屏操作
!DOCTYPE html>
html>
head>
meta charset="UTF-8">
title>
Hello World!/title>
/head>
body>
button id="js-capture">
Capture Screen/button>
script>
const {
ipcRenderer }
= require('electron')
document.getElementById('js-capture').addEventListener('click', ()=>
{
ipcRenderer.send('capture-screen')
}
)
/script>
/body>
/html>
这样一个简单的 electron 项目就完成了, 执行 yarn start 或者 npm start 即可看到一个窗口, 窗口中有一个按钮
触发截屏
截屏是一个相对独立的功能, 并且有可能会有全局快捷键以及菜单触发等脱离窗口的情况, 所以截屏的触发应该放在 main 进程中来实现
在 renderer 进程中可以通过 ipc 通讯来完成, 在页面的代码中使用 ipcRenderer 发送事件, 而在 main 中使用 ipcMain 接收事件
// index.html
const {
ipcRenderer }
= require('electron')
document.getElementById('js-capture').addEventListener('click', ()=>
{
ipcRenderer.send('capture-screen')
}
)
在 main 进程中接收 capture-screen 事件
// main.js
// 接收事件
ipcMain.on('capture-screen', captureScreen)
同时加入全局快捷键触发和取消截屏
// main.js
// 注册全局快捷键
// globalShortcut 需要在 app ready 之后
globalShortcut.register('CmdOrCtrl+Shift+A', captureScreen)
globalShortcut.register('Esc', () =>
{
if (captureWin) {
captureWin.close()
captureWin = null
}
}
)
通过快捷键和事件来触发截屏方法 captureScreen , 接下来实现这个方法来创建一个截屏窗口
创建截屏窗口
截屏窗口是要创建一个全屏的窗口, 并且把屏幕图片绘制在窗口上, 再通过鼠标拖拽等交互操作选出特定区域的图像.
第一步是要创建窗口
// main.js
let captureWin = null
const captureScreen = (e, args) =>
{
if (captureWin) {
return
}
const {
screen }
= require('electron')
let {
width, height }
= screen.getPrimaryDisplay().bounds
captureWin = new BrowserWindow({
// window 使用 fullscreen, mac 设置为 undefined, 不可为 false
fullscreen: os.platform() === 'win32' || 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,
}
)
captureWin.setAlwaysOnTop(true, 'screen-saver') // mac
captureWin.setVisibleOnAllWorkspaces(true) // mac
captureWin.setFullScreenable(false) // mac
captureWin.loadFile(path.join(__dirname, 'capture.html'))
// 调试用
// captureWin.openDevTools()
captureWin.on('closed', () =>
{
captureWin = null
}
)
}
窗口需要覆盖全屏, 并且完全置顶, 在 windows 下可以使用 fullscreen 来保证全屏, Mac 下 fullscreen 会把窗口移到单独桌面, 所以采用了另外的办法, 代码注释上标注了不同系统的相关选项, 具体内容可以查看文档
注意这里窗口加载了另外一个 html 文件, 这个文件用来负责截屏和裁剪的一些交互工作
capture.html
首先 html 结构
// capture.html
div id="js-bg" class="bg">
/div>
div id="js-mask" class="mask">
/div>
canvas id="js-canvas" class="image-canvas">
/canvas>
div id="js-size-info" class="size-info">
/div>
div id="js-toolbar" class="toolbar">
div class="iconfont icon-zhongzhi" id="js-tool-reset">
/div>
div class="iconfont icon-xiazai" id="js-tool-save">
/div>
div class="iconfont icon-guanbi" id="js-tool-close">
/div>
div class="iconfont icon-duihao" id="js-tool-ok">
/div>
/div>
script src="capture-renderer.js">
/script>
Bg : 截屏图片 Mask : 一层灰色遮罩 Canvas : 绘制选中的图片区域和边框 Size info : 标识截取范围的尺寸 Toolbar : 操作按钮, 用来取消和保存等 capture-renderer.js : js 代码
@import "./assets/iconfont/iconfont.css";
html, body, div {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.mask {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.6);
}
.bg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.image-canvas {
position: absolute;
display: none;
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 {
font-size: 24px;
padding: 2px 5px;
}
各个元素基本为 absolute 定位, 由 js 控制位置 按钮使用了 iconfont , 所有涉及到的资源文件和完整项目可以到 GitHub - chrisbing/electorn-capture-screen: electron capture screen 中下载
截图交互
完成的功能有截取指定区域图片, 拖拽移动和改变选区尺寸, 实时尺寸显示和工具条
获取屏幕截图
// capture-renderer.js
const {
ipcRenderer, clipboard, nativeImage, remote, desktopCapturer, screen }
= require('electron')
const Event = require('events')
const fs = require('fs')
const {
bounds: {
width, height }
, scaleFactor }
= screen.getPrimaryDisplay()
const $canvas = document.getElementById('js-canvas')
const $bg = document.getElementById('js-bg')
const $sizeInfo = document.getElementById('js-size-info')
const $toolbar = document.getElementById('js-toolbar')
const $btnClose = document.getElementById('js-tool-close')
const $btnOk = document.getElementById('js-tool-ok')
const $btnSave = document.getElementById('js-tool-save')
const $btnReset = document.getElementById('js-tool-reset')
console.time('capture')
desktopCapturer.getSources({
types: ['screen'],
thumbnailSize: {
width: width * scaleFactor,
height: height * scaleFactor,
}
}
, (error, sources) =>
{
console.timeEnd('capture')
let imgSrc = sources[0].thumbnail.toDataURL()
let capture = new CaptureRenderer($canvas, $bg, imgSrc, scaleFactor)
}
)
screen.getPrimaryDisplay() 可以获取主屏幕的大小和缩放比例, 缩放比例在高分屏中适用, 在高分屏中屏幕的物理尺寸和窗口尺寸并不一致, 一般会有2倍3倍等缩放倍数, 所以为了获取到高清的屏幕截图, 需要在屏幕尺寸基础上乘以缩放倍数
desktopCapturer 获取屏幕截图的图片信息, 获取的是一个数组, 包含了每一个屏幕的信息, 这里呢暂时只处理了第一个屏幕的信息
获取了截图信息后创建 CaptureRenderer 进行交互处理
CaptureRenderer
// capture-renderer.js
class CaptureRenderer extends Event {
constructor($canvas, $bg, imageSrc, scaleFactor) {
super()
// ...
this.init().then(() =>
{
console.log('init')
}
)
}
async init() {
this.$bg.style.backgroundImage = `url(${
this.imageSrc}
)`
this.$bg.style.backgroundSize = `${
width}
px ${
height}
px`
let canvas = document.createElement('canvas')
let ctx = canvas.getContext('2d')
let img = await new Promise(resolve =>
{
let img = new Image()
img.src = this.imageSrc
if (img.complete) {
resolve(img)
}
else {
img.onload = () =>
resolve(img)
}
}
)
canvas.width = img.width
canvas.height = img.height
ctx.drawImage(img, 0, 0)
this.bgCtx = ctx
// ...
}
// ...
onMouseDrag(e) {
// ...
this.selectRect = {
x, y, w, h, r, b}
this.drawRect()
this.emit('dragging', this.selectRect)
// ...
}
drawRect() {
if (!this.selectRect) {
this.$canvas.style.display = 'none'
return
}
const {
x, y, w, h }
= this.selectRect
const scaleFactor = this.scaleFactor
let margin = 7
let radius = 5
this.$canvas.style.left = `${
x - margin}
px`
this.$canvas.style.top = `${
y - margin}
px`
this.$canvas.style.width = `${
w + margin * 2}
px`
this.$canvas.style.height = `${
h + margin * 2}
px`
this.$canvas.style.display = 'block'
this.$canvas.width = (w + margin * 2) * scaleFactor
this.$canvas.height = (h + margin * 2) * scaleFactor
if (w &
&
h) {
let imageData = this.bgCtx.getImageData(x * scaleFactor, y * scaleFactor, w * scaleFactor, h * scaleFactor)
this.ctx.putImageData(imageData, margin * scaleFactor, margin * scaleFactor)
}
this.ctx.fillStyle = '#ffffff'
this.ctx.strokeStyle = '#67bade'
this.ctx.lineWidth = 2 * this.scaleFactor
this.ctx.strokeRect(margin * scaleFactor, margin * scaleFactor, w * scaleFactor, h * scaleFactor)
this.drawAnchors(w, h, margin, scaleFactor, radius)
}
drawAnchors(w, h, margin, scaleFactor, radius) {
// ...
}
onMouseMove(e) {
// ...
document.body.style.cursor = 'move'
// ...
}
onMouseUp(e) {
this.emit('end-dragging')
this.drawRect()
}
getImageUrl() {
const {
x, y, w, h }
= this.selectRect
if (w &
&
h) {
let imageData = this.bgCtx.getImageData(x * scaleFactor, y * scaleFactor, w * scaleFactor, h * scaleFactor)
let canvas = document.createElement('canvas')
let ctx = canvas.getContext('2d')
ctx.putImageData(imageData, 0, 0)
return canvas.toDataURL()
}
return ''
}
reset() {
// ...
}
}
代码有点长, 由于篇幅的原因, 这里只列出了关键部分, 完整代码请到 GitHub - chrisbing/electorn-capture-screen: electron capture screen 上查看
初始化时保存一份绘制了全部图片的 canvas , 用来后续取选区部分图片用
绘制过程中从 通过 canvas 中的 getImageData 获取图片内容 然后通过 putImageData 绘制到显示 canvas 中
附加内容
在 CaptureRenderer 类中处理了图片的选取. 还需要工具条和尺寸信息
这一部分代码和图片选取关系不是很大, 所以在外部单独处理, 通过 CaptureRenderer 传出的事件和一些属性即可完成交互
// capture-renderer.js
let onDrag = (selectRect) =>
{
$toolbar.style.display = 'none'
$sizeInfo.style.display = 'block'
$sizeInfo.innerText = `${
selectRect.w}
* ${
selectRect.h}
`
if (selectRect.y >
35) {
$sizeInfo.style.top = `${
selectRect.y - 30}
px`
}
else {
$sizeInfo.style.top = `${
selectRect.y + 10}
px`
}
$sizeInfo.style.left = `${
selectRect.x}
px`
}
capture.on('start-dragging', onDrag)
capture.on('dragging', onDrag)
let onDragEnd = () =>
{
if (capture.selectRect) {
const {
x, r, b, y }
= capture.selectRect
$toolbar.style.display = 'flex'
$toolbar.style.top = `${
b + 15}
px`
$toolbar.style.right = `${
window.screen.width - r}
px`
}
}
capture.on('end-dragging', onDragEnd)
capture.on('reset', () =>
{
$toolbar.style.display = 'none'
$sizeInfo.style.display = 'none'
}
)
移动过程中计算尺寸, 并且实时计算位置, 移动过程中隐藏工具条
重置选区时隐藏工具条和尺寸标识
保存剪贴板
// capture-renderer.js
const audio = new Audio()
audio.src = './assets/audio/capture.mp3'
let selectCapture = () =>
{
if (!capture.selectRect) {
return
}
let url = capture.getImageUrl()
remote.getCurrentWindow().hide()
audio.play()
audio.onended = () =>
{
window.close()
}
clipboard.writeImage(nativeImage.createFromDataURL(url))
ipcRenderer.send('capture-screen', {
type: 'complete',
url,
}
)
}
$btnOk.addEventListener('click', selectCapture)
通过 nativeImage.createFromDataURL 创建图片写入剪贴板, 通知 main 进程截图完毕, 并附带图片的 base64 url, 然后关闭窗口
保存到文件
// capture-renderer.js
$btnSave.addEventListener(‘click', () =>
{
let url = capture.getImageUrl()
remote.getCurrentWindow().hide()
remote.dialog.showSaveDialog({
filters: [{
name: ‘Images',
extensions: [‘png', ‘jpg', ‘gif']
}
]
}
, function (path) {
if (path) {
fs.writeFile(path, new Buffer(url.replace(‘data:image/png;
base64,', ‘'), ‘base64'), function () {
ipcRenderer.send(‘capture-screen', {
type: ‘complete',
url,
path,
}
)
window.close()
}
)
}
else {
ipcRenderer.send(‘capture-screen', {
type: ‘cancel',
url,
}
)
window.close()
}
}
)
}
)
利用 remote.dialog.showSaveDialog 选择保存文件名, 然后通过 fs 模块写入文件
最终整体目录结构
├── index.html ├── lib // 截图核心代码 │ ├── assets // font 和 声音资源 │ ├── capture-main.js // main 中截图部分代码 │ ├── capture-renderer.js // 截图交互代码 │ └── capture.html // 截图 html ├── main.js └── package.json
坑点总结
开发过程中主要遇到了几个坑
首先全屏窗口,在 windows 和 Mac 上存在不同处理,而且 mac 上这个方案在网上没有查到,最后翻阅文档无意中发现的
然后就是选区过程中,各个位置,选区的拖拽操作,需要大量时间调试
再有就是开发过程中代码可能出错,导致全屏窗口盖在屏幕上无法去掉,最后通过 mac 触摸板五指张开的手势隐藏了窗口才关掉了程序
以上就是关于electron工具有什么用,在项目中的实际应用是怎样的介绍,本文内容仅供参考,有需要的朋友可以借鉴了解看看,希望对大家学习或工作,想要了解更多欢迎关注网络,小编每天都会为大家更新不同的知识。
声明:本文内容由网友自发贡献,本站不承担相应法律责任。对本内容有异议或投诉,请联系2913721942#qq.com核实处理,我们将尽快回复您,谢谢合作!
若转载请注明出处: electron工具有什么用,在项目中的实际应用是怎样
本文地址: https://pptw.com/jishu/653761.html
