// 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 模块写入文件
最终整体目录结构