IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> electron仿微信截图工具(初学者的尝试笔记) -> 正文阅读

[JavaScript知识库]electron仿微信截图工具(初学者的尝试笔记)

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

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

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

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

效果图:

?

上正菜:

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

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

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

//main.js


const {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, // 是否完整支持node
			contextIsolation: 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="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 = imageData
				that.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 = img
				this.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=this
				this.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=color
                this.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+y
					self.redo();
					break;
				case 90://ctrl+z
					self.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元素操作实在麻烦,改用jq
	let 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 = 0
		text.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的后端狗呢

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

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

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-10-08 20:32:10  更:2022-10-08 20:35:16 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/17 15:33:42-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码