需求:
默认后台返回的数据渲染到画布上,然后用户可以编辑重新画线,并且可以点击要移除的线条进行移除。
现在做的交互是选中需要移除的线条高亮显示,然后双击进行移除。
<div id="app"> <canvas id="myCanvas" width="600px" height="380px" class="canvas" @mousedown="drawLineMousedown($event)" @mousemove="drawLineMousemove($event)" @mouseup="drawFinish()" @click="select($event)" @dblclick="deleteLine($event)"> </canvas> <input type="button" class="pencil" value="铅笔" @click="addClick($event)"> <input type="button" class="clearBoard" value="清屏" @click="clearBoard()"> <input type="button" class="delete" value="橡皮擦" @click="addClick($event)"> </div>
new Vue({ el: "#app", data: { baseline: [ { idx: 0, x: 10, y: 300, }, { idx: 1, x: 310, y: 300, } ], contour: [ { "idx": 0, "x": 10, "y": 300 }, { "idx": 1, "x": 70, "y": 230 }, { "idx": 2, "x": 130, "y": 150 }, { "idx": 3, "x": 190, "y": 150 }, { "idx": 4, "x": 250, "y": 230 }, { "idx": 5, "x": 310, "y": 300 } ], junctions: [ { x: null, y: null, points: [ { idx: 1, x: 111, y: 111 }, { idx: 2, x: 233, y: 323 }, { idx: 3, x: 422, y: 435 } ] } ], temPoints:[], isDraw:false, idx:0, className:"", isSelect:"" }, mounted:function(){ this.drawBaseLine(this.baseline); //this.drawContour(this.contour); //this.drawBaseLine(this.contour); this.drawLine(this.junctions); }, methods: { //单击按钮,确定编辑方式 addClick(e){ //获取当前按钮 this.className = e.target.className; }, //清屏 clearBoard(){ const myCanvas = document.getElementById("myCanvas"); const ctx = myCanvas.getContext("2d"); let canvasW = ctx.canvas.clientWidth; let canvasH = ctx.canvas.clientHeight; this.junctions = []; ctx.clearRect(0, 0, canvasW, canvasH); this.drawBaseLine(this.baseline); this.drawContour(this.contour); }, //绘制基准线 drawBaseLine(baseline){ const myCanvas = document.getElementById("myCanvas"); const ctx = myCanvas.getContext("2d"); if(baseline.length > 0){ for(let i = 0; i < baseline.length; i++){ if (i == 0) { ctx.beginPath(); ctx.strokeStyle = 'black'; ctx.moveTo(baseline[i].x, baseline[i].y); } else if (i == baseline.length - 1) { ctx.lineTo(baseline[i].x, baseline[i].y); ctx.stroke(); } else { ctx.lineTo(baseline[i].x, baseline[i].y); } } } }, //绘制轮廓线 drawContour(contour){}, //画线 junctions 多段线集合 drawLine(junctions) { const myCanvas = document.getElementById("myCanvas"); const ctx = myCanvas.getContext("2d"); if(junctions.length > 0){ for(let i = 0; i < junctions.length; i++){ let points = junctions[i].points; for (let j = 0; j < points.length; j++) { if (j == 0) { ctx.beginPath(); if(this.isSelect === i){ ctx.strokeStyle = 'red'; }else{ ctx.strokeStyle = 'black'; } ctx.moveTo(points[j].x, points[j].y); } else if (j == points.length - 1) { ctx.lineTo(points[j].x, points[j].y); ctx.stroke(); } else { ctx.lineTo(points[j].x, points[j].y); } } } } }, //鼠标摁下获取焦点 drawLineMousedown(e){ if(this.className == 'pencil'){ //画线 let idx = this.idx; const myCanvas = document.getElementById("myCanvas"); const ctx = myCanvas.getContext("2d"); let offsetLeft = ctx.canvas.offsetLeft;// 计算左边偏移量 let offsetTop = ctx.canvas.offsetTop;// 计算顶部偏移量 let x = e.clientX - offsetLeft,y = e.clientY - offsetTop; ctx.beginPath(); ctx.strokeStyle = 'black'; ctx.lineTo(x,y); this.isDraw = true; //添加数据 this.temPoints.push({idx,x,y}); this.idx ++; } }, //鼠标移动 drawLineMousemove(e){ if(this.className == 'pencil'){ //画线 if(this.isDraw){ let idx = this.idx ++; const myCanvas = document.getElementById("myCanvas"); const ctx = myCanvas.getContext("2d"); let offsetLeft = ctx.canvas.offsetLeft; let offsetTop = ctx.canvas.offsetTop; let x = e.clientX - offsetLeft,y = e.clientY - offsetTop; ctx.lineTo(x,y); ctx.stroke(); //添加数据 this.temPoints.push({idx,x,y}); } } }, //鼠标抬起 drawFinish(){ if(this.className == 'pencil'){ //结束画线 const myCanvas = document.getElementById("myCanvas"); const ctx = myCanvas.getContext("2d"); ctx.closePath(); this.isDraw = false; //将数据存入集合,清空原有数据 let points = this.temPoints; this.junctions.push({points}); this.temPoints = []; this.idx = 0; } }, //删除线 deleteLine(e){ if(this.className == 'delete'){ const myCanvas = document.getElementById("myCanvas"); const ctx = myCanvas.getContext("2d"); let canvasW = ctx.canvas.clientWidth; let canvasH = ctx.canvas.clientHeight; //删除数据 this.junctions.splice(this.isSelect, 1); this.isSelect = ""; //清空画板重新绘图 ctx.clearRect(0, 0, canvasW, canvasH); this.drawBaseLine(this.baseline); this.drawContour(this.contour); this.drawLine(this.junctions); } }, //选择要删除的线 select(e){ if(this.className == 'delete'){ const myCanvas = document.getElementById("myCanvas"); const ctx = myCanvas.getContext("2d"); let canvasW = ctx.canvas.clientWidth; let canvasH = ctx.canvas.clientHeight; let offsetLeft = ctx.canvas.offsetLeft;// 计算左边偏移量 let offsetTop = ctx.canvas.offsetTop;// 计算顶部偏移量 let x = e.clientX - offsetLeft,y = e.clientY - offsetTop; let p = {x:x,y:y}; // console.log("p:",p); // console.log("offsetLeft:",offsetLeft); // console.log("ctx:",ctx); let result = this.pointInSegments(p,this.junctions); // console.log("result:",result); if(result.isonLine){ //在线上 //修改数据 this.isSelect = result.index; //清空画板重新绘图 ctx.clearRect(0, 0, canvasW, canvasH); this.drawBaseLine(this.baseline); this.drawContour(this.contour); this.drawLine(this.junctions); } } }, //判断是否选中线 pointInSegments(p, junctions) { let isonLine = false,index = ""; if(junctions.length>0){ for (let i = 0; i < junctions.length; i++ ){ let points = junctions[i].points; if(points.length>1){ for (let j = 1; j < points.length; j++ ){ let pi = points[j-1]; let pj = points[j]; if (this.isOnLine(pi, pj, p)) { isonLine = true; index = i; return {isonLine,index}; } } } } } return {isonLine,index}; }, isOnLine(p1, p2, p){ let minX = Math.min(p1.x, p2.x); // 较小的X轴坐标值 let maxX = Math.max(p1.x, p2.x); // 较大的X轴坐标值 let minY = Math.min(p1.y, p2.y); // 较小的Y轴坐标值 let maxY = Math.max(p1.y, p2.y); // 较大的Y轴坐标值 let offset = 10; //偏移量 if (p1.y === p2.y) { // 水平线 if ((p.x >= minX && p.x <= maxX) && (p.y >= minY - offset && p.y <= maxY + offset)) { return true; } return false; }else if (p1.x === p2.x) { // 垂直线 if ((p.y >= minY && p.y <= maxY) && (p.x >= minX - offset && p.x <= maxX + offset)) { return true; } return false; }else{ // 斜线 (先判断点是否进入可接受大范围(矩形),然后再根据直线上的交叉点进行小范围比较) if ((p.x >= minX && p.x <= maxX) && (p.y >= minY - offset && p.y <= maxY + offset)) { //求Y轴坐标 //方法1:根据tanθ= y/x = y1/x1, 即y = (y1/x1)*x (该方法有局限性,垂直线(p2.x - p1.x)=0,不能用) //var y = ((p2.y - p1.y) / (p2.x - p1.x)) * (px - p1.x); //方法2:先求弧度hudu,根据cosθ=x/r, r=x/cosθ,求得r,再根据sinθ=y/r, y=sinθ*r, 求得y let hudu = Math.atan2(p2.y - p1.y, p2.x - p1.x); // 直线的弧度(倾斜度) // 用三角函数计出直线上的交叉点 let r = (p.x - p1.x) / Math.cos(hudu); // 直角三角形的斜边(或理解成圆的半径) let y = Math.sin(hudu) * r; // Y轴坐标 let pm = { x: p.x, y: p1.y + y }; // 直线上的交叉点 if ((Math.abs(p.x - pm.x) <= offset) && (Math.abs(p.y - pm.y) <= offset)) { return true; } } return false; } }, } });