刚刚学习election,心血来潮做些小工具,工具包含常用的矩形、椭圆框线,箭头绘制,笔刷以及文字。

支持框选截图范围后拖动以及裁剪。

大多都是查找网上大佬提供的思路,其中箭头绘制来自大佬代码魔改如何用canvas画一个漂亮的箭头用于永中文档批注场景https://blog.csdn.net/codingMonkeyKing/article/details/51459487

老缝合怪了,第一次做这种,很多不完善的地方,闲暇时候再慢慢优化吧

效果图:

上正菜:

单独用main.js写工具的主线程,免得代码太多分不清

这里面遇到的坑就是打开窗口时会出现闪屏,原因是因为家在页面有一定的延迟,所以用了个比较笨的办法:主线程加载时提前开启窗口并隐藏,调用时直接显示,这种做法费内存但很有效!

为了方便调试没有开启窗口强制置顶,并开启了调试器

//main.jsconst {BrowserWindow, ipcMain, globalShortcut, desktopCapturer, screen} = require('electron')
const os = require('os')
const path = require('path')let captureWin = null;const captureScreen = () => {var mainScreen = screen.getPrimaryDisplay();var allScreens = screen.getAllDisplays();// let size = screen.getPrimaryDisplay().workAreaSize//捕获屏幕截图desktopCapturer.getSources({types: ['screen'],thumbnailSize: {width: mainScreen.size.width, height: mainScreen.size.height}}).then(imgs => {let imageData=imgs[0].thumbnail.toDataURL()global._cut_img_data_temp=imageData; //临时全局变量if (captureWin) {captureWin.webContents.send("imageData",imageData)captureWin.show();if (!process.env.IS_TEST) captureWin.webContents.openDevTools()}})};const useScreenshot = () => {//提前创建窗口防止闪屏captureWin = new BrowserWindow({fullscreen: true,transparent: true,frame: false,resizable: false,//enableLargerThanScreen: true,//skipTaskbar: true,//alwaysOnTop: true,show: false,icon: path.join(__static, '/favicon.ico'), // 更换图标, 这里的图标仅支持svg 和icon 图标webPreferences: {webSecurity: false, // 是否禁用浏览器的跨域安全特性enableRemoteModule: true,nodeIntegration: true, // 是否完整支持nodecontextIsolation: false,//--增加改行解决我的报错preload: __dirname + '/preload.js',// preload:'/src/preload.js'}});captureWin.loadURL(global.winURL + '#/Screenshot');captureWin.on('ready-to-show', function () {// cs_edit_win.webContents.send('imageData',imageData);});captureWin.hide()globalShortcut.register('Esc', () => {closeScreenshot()})globalShortcut.register('CmdOrCtrl+Shift+A', captureScreen)ipcMain.on('captureScreen', (e, {type = 'start', screenId} = {}) => {if (type === 'start') {captureScreen()} else if (type === 'complete') {// nothing} else if (type === 'select') {captureWin.webContents.send('captureScreen', {type: 'select', screenId})}})ipcMain.on('closeScreenshot', (e) => {closeScreenshot()})};const closeScreenshot=function(){if (captureWin) {captureWin.hide();setTimeout(function () {captureWin.close()useScreenshot()},200)// captureWin.hide()}
}exports.useScreenshot = useScreenshot;
exports.captureScreen = captureScreen;

background.js

election默认主线程中引入并初始化

//background.js....const { useScreenshot } = require('../mainProcess/screenshot/main');...app.on('ready', async () => {// 初始化截图useScreenshot()
});

Screenshot.vue

截图窗口的操作页面

Screenshot.vue<template><div id="layout"><div id="mask"></div><div id="mask_tran"></div><img id="imgBase" :src="data:imageData" draggable="false"><canvas id="_canvas" ctrl-type="move" tabindex="0"></canvas><div id="resize-bar-r" class="canvas-resize-bar canvas-resize-r"></div><div id="resize-bar-b" class="canvas-resize-bar canvas-resize-b"></div><div id="resize-bar-rb" class="canvas-resize-bar canvas-resize-rb"></div><div id="sizeBar"></div><div id="edit-bar"><ul><!-- <li :class="{selected:currentSelect==='move'}" title="移动工具" @click="move($event)" ><i class="sofunfont sficonmove"></i></li>--><li class="edit-item" :class="{selected:currentSelect==='rect'}" title="矩形工具" @click="rect($event)"><i class="sofunfont sficonxingzhuang-juxing"></i></li><li class="edit-item" :class="{selected:currentSelect==='circ'}" title="椭圆工具"  @click="circ($event)"><i class="sofunfont sficonquan"></i></li><li class="edit-item" :class="{selected:currentSelect==='arrow'}" title="箭头工具" @click="arrow($event)"><i class="sofunfont sficonyidong"></i></li><li class="edit-item" :class="{selected:currentSelect==='brush'}" title="画笔工具" @click="brush($event)"><i class="sofunfont sficonhuabi"></i></li><li class="edit-item" :class="{selected:currentSelect==='words'}" title="文字工具" @click="words($event)"><i class="sofunfont sficonwenzi"></i></li><li class="edit-item" title="颜色工具" @click="colorSelect($event)"><i class="sofunfont sficoncolorSelector" :style="{color:color}"></i></li><li class="edit-item" title="下载" @click="downloadToLocal"><i class="sofunfont sficonxiazai3"></i></li><li class="edit-item" title="取消" @click="close"><i class="sofunfont sficonclose-bold"></i></li><li class="edit-item" title="完成" @click="done"><i class="sofunfont sficongouxuan"></i></li></ul><div id="colorSelector" v-if="currentSelect === 'color'"><div class="color-item" v-for="item in colorList" :style="{background:item}" @click="onColor(item)"></div></div></div></div>
</template><script>import {EditTools} from "./utils/imageEdit"export default {name: "Screenshot",data() {return {imageData: "",img: null,canvas: null,ctx: null,currentSelect:"move",color:"#ff0000",colorList:["#ff0000","#ffbc38","#07b101","#0634b1","#b11b9b","#39b19a","#ffffff","#000000","#737373","#1990c2","#32b7ff","#b13366","#b1004f",],//截图框起始坐标canvasStartPosition: {x: 0, y: 0},//截图框结束坐标canvasEndPosition: {x: 0, y: 0},//截图框中图片坐标canvasImgPosition: {x: 0, y: 0},//截图框尺寸canvasSize: {width: 0, height: 0},//0:未开始  | 1:正在截取(左键按下)  | 2:截取完成 | 3:正在拖动(左键按下) | 4:正在改变尺寸(左键按下)cutState: 0,editTools:null}},mounted() {let that = this;//获取截屏图片并注入ipcRenderer.on("imageData", (e, imageData) => {that.imageData = imageDatathat.canvas = document.getElementById('_canvas');that.ctx = this.canvas.getContext('2d');that.canvas.width = 0;that.canvas.height = 0;that.init();})},methods: {init: function () {let that = this;let w_w = window.innerWidth;let w_h = window.window.innerHeight;//截图事件let mask = document.getElementById("mask_tran")mask.addEventListener('mousedown', (event) => {if (that.cutState != 0) {return false;}that.cutState = 1;let x = event.offsetX, y = event.offsetY;this.canvasStartPosition = {x: x, y: y};this.canvas.style.top = y + "px";this.canvas.style.left = x + "px";this.canvas.style.display = "inline-block";mask.addEventListener("mousemove", (e) => {if (that.cutState != 1) {return false;}that.setCanvas(e.offsetX, e.offsetY)});});document.addEventListener('mouseup', (event) => {//截图拖动结束if (that.cutState === 1) {that.cutState = 2;that.showEditBar();mask.style.display = "none";that.canvasListener()}//移动截图框结束if (that.cutState === 3) {that.cutState = 2;that.canvasListener()}//resize结束if (that.cutState === 4) {that.cutState = 2;that.canvasListener()}});document.oncontextmenu = () => {if (that.cutState === 2) {that.cutState = 0;that.canvas.width = 0;that.canvas.height = 0;that.hideEditBar();mask.style.display = "inline-block";that.canvas.style.display = "none";that.canvasListener();}//退出截图if (that.cutState === 0) {that.close();}};//canvas拖动事件that.canvas.addEventListener("mousedown", (e) => {if(that.canvas.getAttribute("ctrl-type") !== "move"){return false;}if (that.cutState !== 2) {return false;}that.cutState = 3;//鼠标相当于canvas的坐标let rePosition = {x: e.pageX - that.canvasStartPosition.x,y: e.pageY - that.canvasStartPosition.y};document.addEventListener("mousemove", function (e) {if (that.cutState !== 3) {return false;}let x = e.pageX, y = e.pageY;let reX = x - rePosition.x;let reY = y - rePosition.y;//限制在屏幕范围内reX = reX >= 0 ? reX : 0;reY = reY >= 0 ? reY : 0;reX = reX + that.canvasSize.width <= w_w ? reX : w_w - that.canvasSize.width;reY = reY + that.canvasSize.height <= w_h ? reY : w_h - that.canvasSize.height;that.canvas.style.top = reY + "px";that.canvas.style.left = reX + "px";that.reSetCanvasParams();that.canvasListener();})});let resize_r=document.getElementById("resize-bar-r");let resize_b=document.getElementById("resize-bar-b");let resize_rb=document.getElementById("resize-bar-rb");//canvas resize事件let reType=0;resize_r.addEventListener("mousedown", (e) => {if (that.cutState !== 2) {return false;}that.cutState = 4;reType=0;});resize_b.addEventListener("mousedown", (e) => {if (that.cutState !== 2) {return false;}that.cutState = 4;reType=1;});resize_rb.addEventListener("mousedown", (e) => {if (that.cutState !== 2) {return false;}that.cutState = 4;reType=2;});document.addEventListener("mousemove", function (e) {if (that.cutState !== 4) {return false;}let x = e.pageX, y = e.pageY;//右if(reType===0){that.canvasEndPosition.x=x;that.canvasSize.width=that.canvasEndPosition.x-that.canvasStartPosition.x;that.canvas.width=that.canvasSize.width;}//下if(reType===1){that.canvasEndPosition.y=y;that.canvasSize.height=that.canvasEndPosition.y-that.canvasStartPosition.y;that.canvas.height=that.canvasSize.height;}//右下if(reType===2){that.canvasEndPosition.x=x;that.canvasSize.width=that.canvasEndPosition.x-that.canvasStartPosition.x;that.canvas.width=that.canvasSize.width;that.canvasEndPosition.y=y;that.canvasSize.height=that.canvasEndPosition.y-that.canvasStartPosition.y;that.canvas.height=that.canvasSize.height;}that.reSetCanvasParams();that.canvasListener();})},//canvas监听canvasListener:function(){let r=document.getElementById("resize-bar-r");let b=document.getElementById("resize-bar-b");let rb=document.getElementById("resize-bar-rb");if(this.canvas.style.display==="none" || this.cutState<2){r.style.top="10000px";b.style.top="10000px";rb.style.top="10000px";return false}let width=this.canvas.offsetWidth;let height=this.canvas.offsetHeight;r.style.height=height+"px";r.style.top=this.canvasStartPosition.y+"px";r.style.left=this.canvasEndPosition.x+"px";b.style.width=width+"px";b.style.top=this.canvasEndPosition.y+"px";b.style.left=this.canvasStartPosition.x+"px";rb.style.top=(this.canvasEndPosition.y-5)+"px";rb.style.left=(this.canvasEndPosition.x-5)+"px"},//重设canvas参数reSetCanvasParams: function () {let x = this.canvas.offsetLeft;let y = this.canvas.offsetTop;this.canvasStartPosition.x = x;this.canvasStartPosition.y = y;this.canvasEndPosition.x = x + this.canvasSize.width;this.canvasEndPosition.y = y + this.canvasSize.height;//重设编辑框位置this.showEditBar();//重设尺寸浮标位置let sizeBar = document.getElementById("sizeBar");sizeBar.innerHTML = (this.canvas.width + " x " + this.canvas.height);sizeBar.style.top = (this.canvasStartPosition.y + 2) + "px";sizeBar.style.left = (this.canvasStartPosition.x + 2) + "px";//重设截取区域this.canvasImgPosition.x = -this.canvasStartPosition.x - 1;this.canvasImgPosition.y = -this.canvasStartPosition.y - 1;this.ctx.drawImage(this.img, this.canvasImgPosition.x, this.canvasImgPosition.y);},//设置canvas坐标,尺寸setCanvas: function (endX, endY) {this.canvasImgPosition.x = -this.canvasStartPosition.x - 1;this.canvasImgPosition.y = -this.canvasStartPosition.y - 1;this.canvasEndPosition.x = endX;this.canvasEndPosition.y = endY;let canvasSize = {width: endX - this.canvasStartPosition.x, height: endY - this.canvasStartPosition.y};if (this.canvasSize.width === canvasSize.width && this.canvasSize.height === canvasSize.height) {return}this.canvasSize.width = canvasSize.width >= 0 ? canvasSize.width : 0;this.canvasSize.height = canvasSize.height >= 0 ? canvasSize.height : 0;this.canvas.width = this.canvasSize.width;this.canvas.height = this.canvasSize.height;let img = document.getElementById("imgBase");this.img = imgthis.ctx.drawImage(this.img, this.canvasImgPosition.x, this.canvasImgPosition.y);let sizeBar = document.getElementById("sizeBar");sizeBar.innerHTML = (this.canvas.width + " x " + this.canvas.height);sizeBar.style.display = "inline-block";sizeBar.style.top = (this.canvasStartPosition.y + 2) + "px";sizeBar.style.left = (this.canvasStartPosition.x + 2) + "px";this.canvasListener();},showEditBar: function () {let bar = document.getElementById("edit-bar");let w = bar.offsetWidth;let h = bar.offsetHeight;let w_w = window.innerWidth;let w_h = window.window.innerHeight;bar.style.left = "unset";bar.style.right = "unset";bar.style.top = "unset";bar.style.bottom = "unset";//工具栏默认置于截图框左下角bar.style.top = (this.canvasEndPosition.y + 2) + "px";bar.style.left = this.canvasStartPosition.x + "px";//工具栏右侧超出屏幕if (this.canvasEndPosition.x + w > w_w) {bar.style.left = "unset";bar.style.right = (w_w - this.canvasEndPosition.x) + "px"}//工具栏超出下方屏幕if (this.canvasEndPosition.y + h > w_h) {bar.style.top = (this.canvasStartPosition.y - 2 - h) + "px";}//工具上下方都超出if (this.canvasEndPosition.y + h > w_h && this.canvasStartPosition.y - h - 2 < 0) {bar.style.top = (this.canvasStartPosition.y + 2) + "px";bar.style.left = (this.canvasEndPosition.x - w - 2) + "px";}this.editTools=new EditTools({canvas:this.canvas,ctx:this.ctx,img:this.img,canvasImgPosition:this.canvasImgPosition})},hideEditBar: function () {let bar = document.getElementById("edit-bar");bar.style.left = "unset";bar.style.right = "unset";bar.style.top = "unset";bar.style.bottom = "10000px";document.getElementById("sizeBar").style.display = "none"},close:function(){Platform.send("closeScreenshot")},//下载至本地downloadToLocal:function(){let self=thisthis.downLoad(this.saveAsPNG(this.canvas),function () {//下载完成后关闭截图setTimeout(function () {self.close()},100)});},// 保存成png格式的图片saveAsPNG: function (canvas) {return canvas.toDataURL("image/png");},downLoad: function (url,callback) {let a = document.createElement("a");a.download = "sofun_"+new Date().getTime();// 设置下载的文件名a.href = url;document.body.appendChild(a);a.click();a.remove(); // 下载之后把创建的元素删除callback&&typeof callback=="function"&&callback()},onColor:function(color){this.color=colorthis.editTools.changeColor(color)},//工具栏move:function(e) {this.editTools.move(e);this.currentSelect="move"},rect:function(e) {this.editTools.drawRect(e);this.currentSelect="rect"},circ:function (e) {this.editTools.drawCirc(e);this.currentSelect="circ"},brush:function (e) {this.editTools.brush(e);this.currentSelect="brush"},arrow:function (e) {this.editTools.arrow(e);this.currentSelect="arrow"},words:function (e) {this.editTools.words(e);this.currentSelect="words"},colorSelect:function(e){this.currentSelect="color"},done:function () {}}}
</script><style scoped>* {margin: 0;padding: 0;user-select: none;-webkit-user-drag: none;}#layout {position: fixed;width: 100%;height: 100%;top: 0;left: 0;}#imgBase {position: absolute;width: 100%;z-index: -1;}#mask {z-index: 1;background: rgba(0, 0, 0, 0.6);position: absolute;width: 100%;height: 100%;top: 0;left: 0;}#mask_tran {z-index: 99999;position: absolute;width: 100%;height: 100%;top: 0;left: 0;cursor: crosshair;}canvas {position: absolute;z-index: 5;background: rgba(0, 0, 0, .6);top: 230px;left: 634px;border: 1px dashed #ccc;box-sizing: border-box;display: none;cursor: move;}.canvas-resize-bar{position: absolute;z-index: 99;}.canvas-resize-r{width: 5px;cursor: e-resize;}.canvas-resize-b{height: 5px;cursor: s-resize;}.canvas-resize-rb{width: 10px;height: 10px;cursor: nw-resize;}#edit-bar {position: absolute;z-index: 999;right: 0;bottom: 10000px;background: #fff;height: 40px;white-space: nowrap;border: 1px solid #eaeaea;}#edit-bar ul li {list-style: none;display: inline-block;width: 40px;height: 40px;text-align: center;line-height: 40px;cursor: default;transition: background .3s;position: relative;}#edit-bar ul li:hover {background: #eaeaea;}#edit-bar .selected{box-shadow: inset #ccc 0 0 6px 3px;}#sizeBar {position: absolute;display: none;background: rgba(0, 0, 0, 0.5);color: #fff;z-index: 9;padding: 5px;border-radius: 5px;font-size: 12px;}#colorSelector{position: absolute;top: 43px;background: #fff;box-shadow: #ccc 0 0 2px;}#colorSelector .color-item{width: 15px;height: 15px;margin: 5px;border: 1px solid #ccc;cursor: pointer;display: inline-block;}</style>
<style>#app {background: unset;position: absolute;width: 100%;height: 100%;overflow: hidden;margin: 0;border-radius: 0;}
</style>
imageEdit.js

这里基本都是截图后的工具栏中的相关操作

import $ from "jquery"function EditTools(options) {let self = this;this.canvas = options.canvas;this.ctx = options.ctx;this.img = options.img;this.canvasImgPosition = options.canvasImgPosition;this.history = [];this.historyAll = [];this.color = "#ff0000";this.fillColor = "#ff0000";this.lineWidth = 1;this.fontSize = 20;this.initStyle();this.typeEnum = {move: "move", //移动rect: "rect", //矩形circ: "circ", //圆brush: "brush", //画笔arrow: "arrow", //箭头words: "words", //文字};this.type = "";this.tempParam = [];this.canvas.onmousedown = function (event) {if (self.type !== self.typeEnum.rect&& self.type !== self.typeEnum.circ&& self.type !== self.typeEnum.brush&& self.type !== self.typeEnum.arrow&& self.type !== self.typeEnum.words) {return false;}switch (self.type) {case self.typeEnum.rect:self.doRect(event);break;case self.typeEnum.circ:self.doCirc(event);break;case self.typeEnum.brush:self.tempParam = []; //将临时参数容器类型设为数组,避免保存记录时失败self.doBrush(event);break;case self.typeEnum.arrow:self.doArrow(event);break;case self.typeEnum.words:self.doWords(event);break;}}this.canvas.addEventListener('mouseup', (event) => {if (self.type !== self.typeEnum.rect&& self.type !== self.typeEnum.circ&& self.type !== self.typeEnum.brush&& self.type !== self.typeEnum.arrow) {return false;}// 结束本次绘画self.canvas.onmousemove = null;self.canvas.onclick = null;this.history.push({method: self.type,params: this.tempParam,color: this.color});this.historyAll.push({method: self.type,params: this.tempParam,color: this.color});this.tempParam = "";this.ctx.closePath();this.ctx.save()})//按键监听this.canvas.addEventListener('keydown', (e) => {let keyCode = e.keyCode || e.which || e.charCode;let ctrlKey = e.ctrlKey;if (ctrlKey) {switch (keyCode) {case 89://ctrl+yself.redo();break;case 90://ctrl+zself.undo();break;}}})}EditTools.prototype.initStyle = function () {this.ctx.strokeStyle = this.color;this.ctx.fillStyle = this.fillColor;this.ctx.lineWidth = this.lineWidth;
}EditTools.prototype.changeColor = function (color) {this.color = color;this.fillColor = color;this.lineWidth = color;this.initStyle();
}/*** 撤销* @param e*/
EditTools.prototype.undo = function (e) {if (this.history.length > 0) {this.ctx.clearRect(0, 0, this.width, this.height);this.history.pop();this.ctx.drawImage(this.img, this.canvasImgPosition.x, this.canvasImgPosition.y);this.reDraw()}
};/*** 重写* @param e*/
EditTools.prototype.redo = function (e) {if (this.history.length < this.historyAll.length) {this.ctx.clearRect(0, 0, this.width, this.height);this.history.push(this.historyAll[this.history.length]);this.ctx.drawImage(this.img, this.canvasImgPosition.x, this.canvasImgPosition.y);this.reDraw()}
};/*** 按照历史记录重新绘制* @param e*/
EditTools.prototype.reDraw = function (e) {// 逐个执行历史动作for (let item of this.history) {this.ctx.strokeStyle = item.color;this.ctx.fillStyle = item.color;//矩形if (item.method === this.typeEnum.rect) {// this.ctx.beginPath();this.ctx.strokeRect(...item.params);// this.ctx.closePath();}//圆if (item.method === this.typeEnum.circ) {this.ctx.beginPath();this.ctx.ellipse(...item.params);this.ctx.stroke();}//画笔if (item.method === this.typeEnum.brush) {this.ctx.beginPath();item.params.forEach(param => {this.ctx.lineTo(...param);this.ctx.stroke();});this.ctx.closePath();}//箭头if (item.method === this.typeEnum.arrow) {this.drawLineArrow(...item.params);}//文字if (item.method === this.typeEnum.words) {this.drawText(...item.params);}}this.ctx.strokeStyle = this.color;this.ctx.fillStyle = this.fillColor;
};//坐标修正
// EditTools.prototype.correctPosition=function(x,y){
//  this.ctx.translate(x,y);
//  this.ctx.drawImage(this.img, this.canvasImgPosition.x-x, this.canvasImgPosition.y-y);
//
// }EditTools.prototype.move = function (e) {this.canvas.setAttribute("ctrl-type", this.typeEnum.move);this.canvas.style.cursor = "move";this.historyAll = [];this.history = [];
};/*** 初始化到上一次的绘制,用于绘制清除痕迹* @param e*/
EditTools.prototype.clearAndUpdateParam = function (...param) {this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);this.ctx.drawImage(this.img, this.canvasImgPosition.x, this.canvasImgPosition.y);this.tempParam = param;this.reDraw()}EditTools.prototype.saveBrushPath = function (...param) {this.tempParam.push(param)
};
EditTools.prototype.saveDrawText = function (...param) {let self=this;this.history.push({method: self.type,params: param,color: self.color});this.historyAll.push({method: self.type,params: param,color: self.color});self.tempParam = "";
}EditTools.prototype.drawRect = function (e) {this.canvas.setAttribute("ctrl-type", this.typeEnum.rect);this.canvas.style.cursor = "crosshair";this.type = this.typeEnum.rect;
};
EditTools.prototype.doRect = function (e) {let that = this;let offstL = this.canvas.offsetLeft;let offstT = this.canvas.offsetTop;let startX = e.clientX - offstL, startY = e.clientY - offstT;this.canvas.onmousemove = function (e) {let param = [startX, startY, e.clientX - offstL - startX, e.clientY - offstT - startY]that.clearAndUpdateParam(...param);that.ctx.strokeRect(...param);};};EditTools.prototype.drawCirc = function (e) {this.canvas.setAttribute("ctrl-type", this.typeEnum.circ);this.canvas.style.cursor = "crosshair";this.type = this.typeEnum.circ;
};
EditTools.prototype.doCirc = function (ev) {let that = this;let start = that.getCanvasPos(that.canvas, ev);this.canvas.onmousemove = function (e) {let mouse = that.getCanvasPos(that.canvas, e);let center = {x: (mouse.x - start.x) / 2 + start.x,y: (mouse.y - start.y) / 2 + start.y,};let radiusX = Math.abs(e.clientX - ev.clientX) / 2;let radiusY = Math.abs(e.clientY - ev.clientY) / 2;//(起点x,起点y,半径x,半径y,旋转的角度,起始角,结果角,顺时针还是逆时针)let param = [center.x, center.y, radiusX, radiusY, 0, 0, Math.PI * 2]that.clearAndUpdateParam(...param);that.ctx.beginPath();that.ctx.ellipse(...param);that.ctx.stroke();};};/*** 获取鼠标在canvas上的坐标* @param canvas* @param event* @returns {{x: number, y: number}}*/
EditTools.prototype.getCanvasPos = function (canvas, event) {let rect = canvas.getBoundingClientRect();return {x: event.clientX - rect.left * (canvas.width / rect.width),y: event.clientY - rect.top * (canvas.height / rect.height)};
};/*** 画笔* @param e*/
EditTools.prototype.brush = function (e) {let that = this;this.canvas.setAttribute("ctrl-type", this.typeEnum.circ);this.canvas.style.cursor = "default";this.type = this.typeEnum.brush;
};EditTools.prototype.doBrush = function (e) {let that = this;let offstL = this.canvas.offsetLeft;let offstT = this.canvas.offsetTop;this.ctx.beginPath();this.canvas.onmousemove = function (e) {that.ctx.lineTo(e.clientX - offstL, e.clientY - offstT);that.ctx.stroke();that.saveBrushPath(e.clientX - offstL, e.clientY - offstT)};
};/*** 箭头* @param e*/
EditTools.prototype.arrow = function (e) {let that = this;this.canvas.setAttribute("ctrl-type", this.typeEnum.arrow);this.canvas.style.cursor = "default";this.type = this.typeEnum.arrow;
};
EditTools.prototype.doArrow = function (e) {let that = this;let offstL = this.canvas.offsetLeft;let offstT = this.canvas.offsetTop;this.canvas.onmousemove = function (ev) {let param = [that.ctx, e.clientX - offstL, e.clientY - offstT, ev.clientX - offstL, ev.clientY - offstT];that.clearAndUpdateParam(...param);that.drawLineArrow(...param)};
};
/**** @param {*canvas context 对象} ctx* @param {*起点横坐标} fromX* @param {*起点纵坐标} fromY* @param {*终点横坐标} toX* @param {*终点纵坐标} toY* 以下注释以终点在坐标第一象限内,且方向为右上方*/
EditTools.prototype.drawLineArrow = function (ctx, fromX, fromY, toX, toY) {let headlen = 0.2 * 1.41 * Math.sqrt((fromX - toX) * (fromX - toX) + (fromY - toY) * (fromY - toY));//箭头头部长度headlen = headlen > 20 ? 20 : headlen;//箭头头部最大值let theta = 30;//自定义箭头线与直线的夹角let arrowX, arrowY;//箭头线终点坐标// 计算各角度和对应的箭头终点坐标let angle = Math.atan2(fromY - toY, fromX - toX) * 180 / Math.PI;let angle1 = (angle + theta) * Math.PI / 180;let angle2 = (angle - theta) * Math.PI / 180;let topX = headlen * Math.cos(angle1);let topY = headlen * Math.sin(angle1);let botX = headlen * Math.cos(angle2);let botY = headlen * Math.sin(angle2);let toLeft = fromX > toX;let toUp = fromY > toY;//箭头最上点arrowX = toX + topX;arrowY = toY + topY;//箭头下拐点let arrowX1 = toX + botX;let arrowY1 = toY + botY;//箭头上拐点let arrowX2 = toUp ? arrowX + 0.25 * Math.abs(arrowX1 - arrowX) : arrowX - 0.25 * Math.abs(arrowX1 - arrowX);let arrowY2 = toLeft ? arrowY - 0.25 * Math.abs(arrowY1 - arrowY) : arrowY + 0.25 * Math.abs(arrowY1 - arrowY);//箭头最下点let arrowX3 = toUp ? arrowX + 0.75 * Math.abs(arrowX1 - arrowX) : arrowX - 0.75 * Math.abs(arrowX1 - arrowX);let arrowY3 = toLeft ? arrowY - 0.75 * Math.abs(arrowY1 - arrowY) : arrowY + 0.75 * Math.abs(arrowY1 - arrowY);ctx.beginPath();//起点-起点,闭合ctx.moveTo(fromX, fromY);ctx.lineTo(arrowX2, arrowY2);ctx.lineTo(arrowX, arrowY);ctx.lineTo(toX, toY);ctx.lineTo(arrowX1, arrowY1);ctx.lineTo(arrowX3, arrowY3);ctx.lineTo(fromX, fromY);ctx.closePath();ctx.fill();ctx.stroke();};/*** 箭头* @param e*/
EditTools.prototype.words = function (e) {let that = this;this.canvas.setAttribute("ctrl-type", this.typeEnum.words);this.canvas.style.cursor = "text";this.type = this.typeEnum.words;};
EditTools.prototype.doWords = function (ev) {let that = this;let x = ev.offsetX, y = ev.offsetY;this.createTextInput(this.canvas, x, y, function (setX, setY, text,maxWidth) {let param=[that.ctx,text,setX, setY,maxWidth]that.drawText(...param);that.saveDrawText(...param);})}EditTools.prototype.drawText=function(ctx,t,x,y,w){let that=this;//参数说明let chr = t.split("")let temp = ""let row = []for (let a = 0; a<chr.length;a++){if(/\r|\r\n|\n/.test(chr[a]) ){chr[a]="";row.push(temp);temp = chr[a];}else if(ctx.measureText(temp).width < w && ctx.measureText(temp+(chr[a])).width <= w){temp += chr[a];}else{row.push(temp);temp = chr[a];}}row.push(temp)for(let b=0;b<row.length;b++){ctx.font = that.fontSize+"px emoji";ctx.fillText(row[b],x,y+(b+1)*that.fontSize);//每行字体y坐标间隔20}}/*** 创建文字输入框*/
EditTools.prototype.createTextInput = function (canvas, startX, startY, onInputOver) {let that = this;if ($("._textInput").length > 0) {return false}let rect = canvas.getBoundingClientRect();let textareaMaxW = that.canvas.width - startX - 12;let textareaMaxH = that.canvas.height - startY - 18;//原生js元素操作实在麻烦,改用jqlet textarea = $("<textarea class='_textInput'></textarea>");textarea.css({position: "absolute",left: rect.left + startX,top: rect.top + startY - 15,outline: "none",border: "2px solid " + that.color,resize: "none",padding: "0 5px","font-size": that.fontSize,color: that.color,width: 80,"max-width": textareaMaxW,"font-family": "emoji",height: 30}).blur(function () {onInputOver && typeof onInputOver == "function"&& onInputOver(startX, startY - 15 , $(this).val(),textareaMaxW);$(this).remove();}).bind('input propertychange', function () {let text = $(this).val();let maxRow = 0text.split(/\r|\r\n|\n/).forEach(e => {if (e.length > maxRow) {maxRow = e.length}})if (maxRow * that.fontSize > $(this).width()) {if (textareaMaxW > $(this).width()) {$(this).css("width", maxRow * that.fontSize + that.fontSize)}} else if ($(this).width() - maxRow * that.fontSize > that.fontSize) {$(this).css("width", maxRow * that.fontSize + that.fontSize)}$(this).css("height", $(this)[0].scrollHeight)})$("body").append(textarea)setTimeout(function () {textarea.focus();})};// EditTools.prototype.doArrow = function (e) {
//
// };export {EditTools}

没有接触过桌面程序的开发,vue和electron也是刚学,谁能想到我是个搞java的后端狗呢

当中的思路和方法基本都是自己琢磨出来的,在此发文也仅作为自己的学习笔记。其中也有很多明显的问题,需要后面慢慢的调整

方法很笨,别杠别喷,杠就是你对!!

======================================================

2023年2月15日补充:

preload.js

dist目录下新建文件 preload.js 保证渲染线程能正常调用

global.electron = require('electron');
window.ipcRenderer = require('electron').ipcRenderer;
window.Menu = require('electron').Menu;
window.remote = require('electron').remote;
window.Elapp = require('electron').app;
window.protocol = require('electron').protocol;
window.BrowserWindow = require('electron').BrowserWindow;
window.desktopCapturer = require('electron').desktopCapturer;
window.electronScreen = require('electron').screen;
window.clipboard = require('electron').clipboard;
window.nativeImage = require('electron').nativeImage;

electron仿微信截图工具(初学者的尝试笔记)相关推荐

  1. C# 绘制箭头的方法,仿微信截图的箭头

    C# 绘制箭头的方法,仿微信截图的箭头 效果见下图,实际上还是有区别的,箭头的起点处微信的是圆端,而我实现的是尖端. 说说我的实现吧,实现方法其实是划线,线的两端都要设置端点样式.看代码: Point ...

  2. android仿微信图片编辑器,electron/vue可编辑框contenteditable|仿微信截图

    基于Electron+vue实现div可编辑contenteditable插入表情|electron-vue截图功能 为了避免使用 vue 手动建立起 electron 应用程序.electron-v ...

  3. Python调用微信截图工具

    之前看到网上有人做了一个多功能截图工具,调用的是QQ的截图DLL,原地址: http://www.open-open.com/lib/view/open1331393882327.html http: ...

  4. electron仿微信客户端—设计与实现(已开源)

    基于electorn构建的仿微信PC客户端 之前一直对web开发跨平台应用比较感兴趣,一次无意中看到 electorn ,觉得挺有意思,想写个小项目用于学习和练手 项目地址:jwchat-码云 效果对 ...

  5. 伪造微信截图工具(改)

    大纲  本程序希望解决的问题是大学中社团外联常见的扩散要求,即需要为赞助商的产品发朋友圈,同学找到你让你转发的时候又不好直接拒绝,就可以通过个办法伪造一下朋友圈截图.注:自己拉的外联的话该转发还是转发 ...

  6. 借助云开发,利用订阅消息,云函数路由实现小程序好友一对一聊天,添加好友等仿微信功能

    微信小程序借助云开发,利用订阅消息,云函数路由实现小程序好友一对一聊天,添加好友等仿微信功能 这篇文章已经进行了更新,请点此进行查看 仿微信好友聊天 主要功能有 后续可能补充功能 详细介绍 注意 保存 ...

  7. 微信开发者工具-HBuilderX

    环境 HBuilderX.2.0.0.20190610.full.zip Windows10 微信开发者工具1.02.1905151 配置 解压运行 HBuilderX运行后,点击工具-设置-运行配置 ...

  8. electron-vue仿微信聊天客户端|electron聊天应用

    使用Electron+Vue+electron-vue+vuex+Node+vue-video-player等技术构建开发的高仿微信客户端聊天室,实现了消息发送/表情,光标处插入表情,图片/视频预览, ...

  9. Android仿微信添加联系人列表,内附有截图和demo源码

    最新demo地址,仿微信添加联系人WXAddPersonDemo 分享一个Android仿微信选择联系人页面 之前做的App主要是工具类的,而且公司的产品经理也喜欢在App里设计很多自定义控件,所以比 ...

最新文章

  1. 百度自动驾驶新突破:获首批T4牌照,升级Apollo 5.0,将进行复杂城市场景路测...
  2. js 将时间戳转为日期格式
  3. b站上java和python视频可以吗_b站有哪些好的java视频?
  4. 如何获取 sql server 最新补丁
  5. linux/unix编程手册-61_64
  6. Vue 2.0 v-for 响应式key, index及item.id参数对v-bind:key值造成差异研究
  7. 奉献一个窗口置顶的小工具
  8. 马斯克:挑战纽北赛道的Model S配有7个座椅
  9. Python垃圾回收(gc)拖累了程序执行性能?
  10. 对Table_locks_immediate值的理解
  11. 《成语接龙》之成语表
  12. 测试方法-等价类划分法
  13. UA用Mode-Driven的使用笔记
  14. loadrunner11无法启动ie浏览器问题
  15. GNU C++ 智能指针4-- 解析_Sp_counted_ptr类
  16. 单词倒排 与 IP整数转换
  17. 黄山的正宗徽菜和新鲜景区
  18. 基于MTK平台的Android预制语音信箱号码
  19. 音频信号处理(二)语音信号采集处理与基音周期
  20. web常见性能优化总结(浏览器渲染过程详解)

热门文章

  1. 为什么程序员和产品经理水火不容? | 每日趣闻
  2. 【前端之旅】HTML大总结
  3. 咖啡烘焙饕餮盛宴——洛阳新都汇有你想要的感觉
  4. “游方之旅”的纯美爱情
  5. pytorch中的reshape()、view()、nn.flatten()和flatten()
  6. 树状数组两种基本的模式
  7. 交通预见未来(1):循环神经网络之LSTM,不只有七秒钟的记忆
  8. java 图片移动代码,如何较好的移动图片
  9. android控制板
  10. 一看就懂的 安装完ubuntu 18.04后要做的事情和使用教程