欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

画布绘图,可拖动和移动绘制矩形、圆形、直线和曲线

最编程 2024-03-31 09:26:33
...
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>canvas跟随鼠标移动画透明线</title> <style> div,canvas,img{ user-select: none; } .my_canvas,.bg_img{ position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%); } .cf{ content: ''; display: block; overflow: hidden; clear: both; } .fl{ float: left; } .fr{ float: right; } .bg_img{ width: 674px; height: 495px; background: #ddd; } .img_tools{ position: absolute; top: 20px; left: 50%; transform: translateX(-50%); border: 1px solid #eee; border-radius: 64px; height: 64px; line-height: 64px; box-sizing: border-box; padding: 15px 20px 0; } .img_tool{ height: 32px; line-height: 32px; color: #000; font-size: 14px; text-align: center; width: 80px; border: 1px solid #ddd; border-radius: 32px; margin-right: 10px; cursor: pointer; position: relative; } .img_tool_active{ color: #409EFF; border: 1px solid #409EFF; } .show_history{ position: absolute; bottom:0; left: 50%; transform: translateX(-50%); } .show_history>img{ width: 120px; margin-right: 10px; border: 1px solid #eee; border-radius: 4px; } .canvas_text{ width: 120px; height: 32px; line-height: 32px; position: absolute; top: 0; left: 0; border: 1px solid #c0c0c0; border-radius: 4px; font-size: 16px; outline: none; background: none; display: none; font-family: Arial, Helvetica, sans-serif; padding-left: 0; letter-spacing: 0; } </style> </head> <body> <div class="bg_img"></div> <canvas id="myCanvasBot" class="my_canvas" width="674" height="495"></canvas> <canvas id="myCanvasTop" class="my_canvas" width="674" height="495"></canvas> <div class="img_tools cf"> <div class="img_tool fl" onclick="changeType('curve',this)">涂鸦</div> <div class="img_tool fl" onclick="changeType('line',this)">直线</div> <div class="img_tool fl img_tool_active" onclick="changeType('rect',this)">矩形</div> <div class="img_tool fl" onclick="changeType('ellipse',this)">圆形</div> <!-- <div class="img_tool fl" onclick="changeType('eraser',this)">橡皮擦</div> --> <!-- <div class="img_tool fl" onclick="changeType('text',this)">文字</div> --> <!-- <div class="img_tool fl" onclick="changeType('revoke',this)">撤销</div> --> <!-- <div class="img_tool fl" onclick="changeType('restore',this)">恢复</div> --> </div> <input id="canvasText" autofocus class="canvas_text" type="text"> <div id="showHistory" class="show_history"></div> <script> const canvasWidth = 674; const canvasHeight = 495; //底层canvas const botCan = document.getElementById('myCanvasBot'); //顶层canvas const topCan = document.getElementById('myCanvasTop'); //底层画布 const botCtx = botCan.getContext('2d'); //顶层画布 const topCtx = topCan.getContext('2d'); //鼠标是否按下 是否移动 let isDown = false,isMove = false; //鼠标是否在canvas上抬起 let isCanUp = false; //需要画图的轨迹 let drawPoints = []; //起始点x,y let startPoint = { x:0, y:0 }; //图片历史 let historyList = []; //空历史 historyList.push(new Image()) //当前绘画历史index let historyIndex = -1; //icon历史 // let partHistory = []; //操作类型 let drawType = 'rect'; //画线宽度 const lineWidth = 10; //文字大小 const fontSize = 16; //画线颜色 let strokeStyle = 'rgba(255,0,0,0.6)'; //path2D图形列表 let pathList = []; //path2D单个图形 let pathObj = null; //path2D的唯一标识 let pathId = 0; //当前被激活的path2D let activePath = null; //是否为拖拽行为 let isDrag = false; //拖拽是否移动 let isDragMove = false; //是否为改变尺寸行为 isResize = false; //改变尺寸点list let pointsList = []; //拖拽修改尺寸的点 let activePoint = null; //文字输入框init const canvasText = document.getElementById('canvasText'); canvasText.style.display = 'none'; canvasText.style.lineHeight = '32px'; canvasText.style.height = '32px'; canvasText.style.display = 'none'; canvasText.style.color = 'none'; canvasText.addEventListener('blur',()=>{ topCtx.font = fontSize + 'px Arial, Helvetica, sans-serif'; let h = parseFloat(canvasText.style.height); topCtx.fillText(canvasText.value, startPoint.x+1, startPoint.y+h/2+fontSize/2-1); canvasText.style.display = 'none'; canvasText.value = ''; topToBot(); }) //起始点x,y let textPoint = { x:0, y:0 }; //鼠标按下 const mousedown = (e)=>{ isDown = true; let x = (e||window.event).offsetX; let y = (e||window.event).offsetY; if(canvasText.style.display == 'none')startPoint = {x,y}; //检测是否点击到图形 activePath = isPointInPath(x,y); if(activePath){ isDrag = true; topCtx.strokeStyle = topCtx.fillStyle = botCtx.strokeStyle = botCtx.fillStyle = activePath.strokeStyle||strokeStyle; topCtx.lineWidth = botCtx.lineWidth = activePath.lineWidth||lineWidth; switch (activePath.type){ case 'rect': makePathActive(); break; case 'ellipse': makePathActive(); break; case 'line': makePathActive(); break; case 'curve': makePathActive(); break; } return; } if(drawType == 'text'){ textPoint = { x:x+topCan.offsetLeft-canvasWidth/2, y:y+topCan.offsetTop-canvasHeight/2 }; // canvasText.style.height = 32 + 'px'; canvasText.style.top = textPoint.y+'px'; canvasText.style.left = textPoint.x+'px'; canvasText.style.display = 'block'; canvasText.style.fontSize = fontSize + 'px'; canvasText.style.color = strokeStyle; setTimeout(()=>{ canvasText.focus(); },100) } if(drawType == 'curve'){ drawPoints = []; drawPoints.push({x,y}); } topCtx.strokeStyle = topCtx.fillStyle = botCtx.strokeStyle = botCtx.fillStyle = strokeStyle; topCtx.lineWidth = botCtx.lineWidth = lineWidth; topCtx.lineCap = topCtx.lineJoin = botCtx.lineCap = botCtx.lineJoin = 'round'; } //鼠标移动 const mousemove = (e)=>{ let x = (e||window.event).offsetX; let y = (e||window.event).offsetY; let distanceX = 0; let distanceY = 0; if(isDown){ isMove = true; if(isDrag){ isDragMove = true; switch(activePath.type){ case 'curve': distanceX = x - startPoint.x; distanceY = y - startPoint.y; let newPoints = []; for(let i=0;i<activePath.drawPoints.length;i++){ let drawPoint = activePath.drawPoints[i]; newPoints.push({x:drawPoint.x + distanceX,y:drawPoint.y + distanceY}); } drawCurve(newPoints); break; case 'line': distanceX = x - startPoint.x; distanceY = y - startPoint.y; drawLine(activePath.startX + distanceX,activePath.startY + distanceY,activePath.x + distanceX,activePath.y + distanceY,); break; case 'eraser': // drawEraser(x,y); break; case 'rect': // xy 为当前point的坐标 // startPoint为点击的矩形上点 查看当前point.x点距离startPoint.x移动了多少 point.y点距离startPoint.y移动了多少 drawRect(activePath.x + (x - startPoint.x),activePath.y + (y - startPoint.y),activePath.width,activePath.height); break; case 'ellipse': // drawEllipse(x,y); drawEllipse(activePath.x + (x - startPoint.x),activePath.y + (y - startPoint.y),activePath.radiusX,activePath.radiusY); break; } return; } switch(drawType){ case 'curve': drawPoints.push({x,y}); drawCurve(drawPoints); break; case 'line': drawLine(startPoint.x,startPoint.y,x,y); break; case 'eraser': drawEraser(x,y); break; case 'rect': // drawRect(x,y); drawRect(startPoint.x, startPoint.y, x-startPoint.x, y - startPoint.y); break; case 'ellipse': drawEllipse((x+startPoint.x)/2, (y+startPoint.y)/2, Math.abs((x-startPoint.x)/2), Math.abs((y-startPoint.y)/2),0,0, Math.PI*2,true); break; } } } //鼠标抬起 const mouseup = (e)=>{ isCanUp = true; if(isDown){ isDown = false // topCan内容画到botCan上 if(isDrag){ isDrag = false; activePath = null; if(isDragMove){ isDragMove = false; pathList.pop(); }else{ pathObj = pathList.pop(); } topToBot(); return } if(drawType!='text')topToBot(); } } //topCan内容画到botCan上 const topToBot = ()=>{ if(pathObj){ pathObj.id = pathId++; pathList.push(pathObj); topCtx.clearRect(0,0,canvasWidth,canvasHeight); if(isCanUp)isCanUp=false; botCtx[pathObj.shape](pathObj.path); pathObj = null; } drawPoints = []; isDown = false; isMove = false; } //判断是否点击到图形 const isPointInPath = (x,y)=>{ let PointInPath = null; for(let i=0;i<pathList.length;i++){ let path = pathList[i]; if(botCtx.isPointInStroke(path.path,x,y)){ PointInPath = path; break; } } return PointInPath; } //激活rect图形轮廓 const makePathActive = ()=>{ botCtx.clearRect(0,0,canvasWidth,canvasHeight); let arr = []; for(let i=0;i<pathList<