赞
踩
提示:canvas画图,画矩形,圆形,直线,曲线可拖拽移动
test.html
<!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.length;i++){ let path = pathList[i] if(activePath.id != path.id){ botCtx[path.shape](path.path); arr.push(path); }else{ topCtx[path.shape](path.path); } } arr.push(activePath); pathList = arr; } //画椭圆形 const drawEllipse = (x,y,radiusX,radiusY)=>{ //清除topCtx画布 topCtx.clearRect(0,0,canvasWidth,canvasHeight); topCtx.beginPath(); let path = new Path2D(); // 椭圆 path.ellipse(x,y,radiusX,radiusY,0,0, Math.PI*2,true); topCtx.stroke(path); pathObj = { type:'ellipse', shape:'stroke', path, x, y, radiusX, radiusY, lineWidth:topCtx.lineWidth||lineWidth, strokeStyle:topCtx.strokeStyle||strokeStyle }; } //画矩形 const drawRect = (x,y,width,height)=>{ //清除topCtx画布 topCtx.clearRect(0,0,canvasWidth,canvasHeight); topCtx.beginPath(); let path = new Path2D(); // 矩形 path.rect(x,y,width,height); topCtx.stroke(path); pathObj = { type:'rect', shape:'stroke', path, x, y, width, height, lineWidth:topCtx.lineWidth||lineWidth, strokeStyle:topCtx.strokeStyle||strokeStyle }; } //橡皮擦 const drawEraser = (x,y)=>{ //橡皮擦圆形半径 const radius = lineWidth/2; botCtx.beginPath(); for(let i=0;i<radius*2;i++){ //勾股定理高h let h = Math.abs( radius - i); //i>radius h = i-radius; i<radius h = radius - i //勾股定理l let l = Math.sqrt(radius*radius -h*h); //矩形高度 let rectHeight = 1; //矩形宽度 let rectWidth = 2*l; //矩形X let rectX = x-l; //矩形Y let rectY = y-radius + i; botCtx.clearRect(rectX, rectY, rectWidth, rectHeight); } } //画透明度直线 const drawLine = (startX,startY,x,y)=>{ if(!isDown)return; //清空当前画布内容 topCtx.clearRect(0,0,canvasWidth,canvasHeight); //必须每次都beginPath 不然会卡 topCtx.beginPath(); let path = new Path2D(); path.moveTo(startX,startY); path.lineTo(x,y); topCtx.stroke(path); pathObj = { type:'line', shape:'stroke', path, x, y, startX, startY, lineWidth:topCtx.lineWidth||lineWidth, strokeStyle:topCtx.strokeStyle||strokeStyle }; } //画带透明度涂鸦 const drawCurve = (drawPointsParams)=>{ // drawPoints.push({x,y}); if(!drawPointsParams||drawPointsParams.length<1)return //清空当前画布内容 topCtx.clearRect(0,0,canvasWidth,canvasHeight); //必须每次都beginPath 不然会卡 topCtx.beginPath(); let path = new Path2D(); path.moveTo(drawPointsParams[0].x,drawPointsParams[0].y); for(let i=1;i<drawPointsParams.length;i++){ path.lineTo(drawPointsParams[i].x,drawPointsParams[i].y); } topCtx.stroke(path); pathObj = { type:'curve', shape:'stroke', path, drawPoints:drawPointsParams, lineWidth:topCtx.lineWidth||lineWidth, strokeStyle:topCtx.strokeStyle||strokeStyle }; } //切换操作 const changeType = (type,that)=>{ // if(drawType == type) return; let tools = document.getElementsByClassName('img_tool'); for(let i=0;i<tools.length;i++){ let ele = tools[i]; if(ele.classList.contains('img_tool_active'))ele.classList.remove('img_tool_active'); } that.classList.add('img_tool_active'); drawType = type; //撤销 if(drawType == 'revoke'){ if(historyIndex>0){ historyIndex--; drawImage(historyList[historyIndex]); } //恢复 }else if(drawType == 'restore'){ if(historyIndex<historyList.length - 1){ historyIndex++; drawImage(historyList[historyIndex]); } } } const drawImage = (img)=>{ botCtx.clearRect(0,0,canvasWidth,canvasHeight); botCtx.drawImage(img,0,0); } //canvas添加鼠标事件 topCan.addEventListener('mousedown',mousedown); topCan.addEventListener('mousemove',mousemove); topCan.addEventListener('mouseup',mouseup); //全局添加鼠标抬起事件 document.addEventListener('mouseup',(e)=>{ let x = (e||window.event).offsetX; let y = (e||window.event).offsetY; let classList = (e.target || {}).classList || []; if(classList.contains('img_tool'))return; if(!isCanUp){ isDown = false; // topCan内容画到botCan上 if(isDrag){ isDrag = false; activePath = null; if(isDragMove){ isDragMove = false; pathList.pop(); }else{ pathObj = pathList.pop(); } topToBot(); return } if(drawType == 'line'&&!isDrag){ let clientX = topCan.getBoundingClientRect().x; let clientY = topCan.getBoundingClientRect().y; drawLine(startPoint.x,startPoint.y,x-clientX,y-clientY); } // topCan内容画到botCan上 topToBot(); } }); //全局添加鼠标移动事件 document.addEventListener('mousemove',(e)=>{ if(isMove)return isMove = false; let x = (e||window.event).offsetX; let y = (e||window.event).offsetY; if(drawType == 'line'&&!isDrag){ let clientX = topCan.getBoundingClientRect().x; let clientY = topCan.getBoundingClientRect().y; drawLine(startPoint.x,startPoint.y,x-clientX,y-clientY); } }); </script> </body> </html>
踩坑路漫漫长@~@
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。