【前端】使用canvas做一个可绘制矩形的画布(带有移动,缩放,删除)
CrazyPanda发表于:2024-04-07 16:08:20浏览:358次
效果如下
下面直接看代码吧,可以直接复制运行
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> * { padding: 0; margin: 0; } html, body { height: 100vh; width: 100vw; background-color: #ececec; } body { display: flex; justify-content: center; align-items: center; } /* 图像容器 */ .image_box { position: relative; height: 600px; width: 600px; } img, .canvas_dom { height: 100%; width: 100%; position: absolute; } .canvas_dom { background-color: rgba(0, 0, 0, .2); cursor: crosshair; } </style> </head> <body> <div> <!-- canvas 必须指定宽高,不然绘制出来的矩形会模糊和错误。 --> <canvas id="canvasDom" width="600" height="600"> no canvas! Your Browser is not support canvas. Please change your browser. </canvas> </div> </body> </html> <script> // canvas对象 let canvasDom = document.getElementById('canvasDom') let ctx2d = canvasDom.getContext("2d"); // 保存矩形数据 let recArrs = [] // 记录当前的信息 let curObj = { isRightClick: false, // 鼠标右键按下标识 radious: 4, // 范围误差值 recSize: 5, // 移动小框的大小 index: -1, // 当前矩形框的index side: 0, // 边界值 resize: false, // 是否拖拽大小 draw: false, // 是否画图 drag: false, // 是否拖动 x: 0, // 画图的起始x y: 0, // 画图的起始y startX: 0, // x轴开始位置 startY: 0, // y轴开始位置 } // 获取canvas的宽高 const canvasHeight = canvasDom.offsetHeight const canvasWidth = canvasDom.offsetWidth console.log('高度--', canvasHeight); console.log('宽度--', canvasWidth); // 注册事件 canvasDom.addEventListener('mousemove', moveFunction) canvasDom.addEventListener('mousedown', downFunction) canvasDom.addEventListener('mouseup', upFunction) canvasDom.addEventListener('mouseout', outFunction) canvasDom.addEventListener('contextmenu', contextMenuFunction) // 禁止鼠标右键 function contextMenuFunction(e) { e.preventDefault(); return false } // 鼠标移出画布 function outFunction(e) { let x = e.clientX; let y = e.clientY; let left = canvasDom.offsetLeft let top = canvasDom.offsetTop let width = canvasDom.offsetWidth let height = canvasDom.offsetHeight let limitX = left + width let limitY = top + height if(x < left || x > limitX || y < top || y > limitY ) { console.log('鼠标移出范围, 清除canvas,重新绘制'); clearCanvas() drawOldRect() } } // 鼠标移动事件 function moveFunction(e) { // console.log('鼠标移动', e); // 需要清除之前的辅助线 clearCanvas() // 画辅助线 drawRuler(e) // 清空辅助线和矩形数据后,这里重绘 drawOldRect() // 画矩形 drawRect(e) // 移动或缩放 moveOrScale(e) } // 移动矩形框/缩放矩形框 function moveOrScale(e) { let index = getEventIndex(e.offsetX, e.offsetY) let side = 0 if(index > -1) { side = getEventArea(index, e.offsetX, e.offsetY) // 画移动小框 if(side > 0) { drawLitRecs(index) } } // 鼠标样式 changeResizeCursor(side); // 如果在移动 moveRec(e) // 如果在缩放 reSizeRec(e) } // 移动 function moveRec(e) { if(curObj.drag && recArrs[curObj.index]) { let x = curObj.startX + e.offsetX - curObj.x let y = curObj.startY + e.offsetY - curObj.y let minX = canvasDom.offsetLeft let maxX = canvasDom.offsetLeft + canvasDom.offsetWidth - recArrs[curObj.index].w let minY = canvasDom.offsetTop let maxY = canvasDom.offsetTop + canvasDom.offsetHeight - recArrs[curObj.index].h if(x < minX) { x = minX } if( x > maxX) { x = maxX } if(y < minY) { y = minY } if( y > maxY) { y = maxY } recArrs[curObj.index].x = x recArrs[curObj.index].y = y } } // 缩放 function reSizeRec(e) { const { side, index, recSize } = curObj const rec = recArrs[index] if(curObj.resize && rec) { const temX = rec.x; const temY = rec.y; const ex = e.offsetX const ey = e.offsetY if (side < 4 && temX + rec.w - ex > recSize) { rec.x = ex; } if ( (side == 1 || side == 4 || side == 7) && temY + rec.h - ey > recSize ) { rec.y = ey; } if (side < 4) { if (temX + rec.w - ex > recSize) { rec.w = temX + rec.w - ex; } } else if (side < 7) { if (ex - temX > recSize) { rec.w = ex - temX; } } if (side == 1 || side == 4 || side == 7) { if (temY + rec.h - ey > recSize) { rec.h = temY + rec.h - ey; } } else if (side == 3 || side == 6 || side == 8) { if (ey - temY > recSize) { rec.h = ey - temY; } } } } // 鼠标移动,画辅助线 function drawRuler(e) { // 开始一条路径 ctx2d.beginPath(); // 填充色 ctx2d.strokeStyle = "red"; // 路径宽度 ctx2d.lineWidth = 1; // 移动到 鼠标的 x位置, y位置 0(竖线的起点) ctx2d.moveTo(e.offsetX, 0); // lineTo() 方法添加一个新点,(竖线的终点) ctx2d.lineTo(e.offsetX, canvasHeight); // 移动到(x: 0, y:鼠标的位置)(横线的起点) ctx2d.moveTo(0, e.offsetY); // lineTo() 方法添加一个新点,(横线的终点) ctx2d.lineTo(canvasWidth, e.offsetY); ctx2d.stroke(); } // 清除canvas function clearCanvas() { ctx2d.clearRect(0, 0, canvasWidth, canvasHeight); } // 鼠标按下并移动,画矩形 function drawRect(e) { if (curObj.draw) { ctx2d.strokeRect( curObj.x, curObj.y, e.offsetX - curObj.x, e.offsetY - curObj.y ); } } // 画初始数据 function drawOldRect() { if (!recArrs.length) return for (var i = 0; i < recArrs.length; i++) { // >2的判断是为了防止误触画出来的数据 if(recArrs[i].w > 2 && recArrs[i].h > 2) { ctx2d.beginPath(); ctx2d.lineWidth = 2; // 矩形框的线条宽度 ctx2d.strokeStyle = "rgb(255, 0, 0)"; // 矩形框的线条颜色 ctx2d.strokeRect(recArrs[i].x, recArrs[i].y, recArrs[i].w, recArrs[i].h); // 矩形框 // 如果有文本信息,填充文本信息 if (recArrs[i].text) { ctx2d.fillStyle = "purple"; ctx2d.font = "14px 微软雅黑"; ctx2d.lineWidth = 1; ctx2d.strokeStyle = "rgb(255,0,0)"; ctx2d.strokeText( recArrs[i].text, (recArrs[i].x + recArrs[i].w / 2) - (recArrs[i].text.length / 2) * 16, recArrs[i].y - 20 < 0 ? recArrs[i].y + recArrs[i].h + 20 : recArrs[i].y - 10 ); } } } } // 鼠标按下事件 function downFunction(e) { console.log('鼠标按下', e); // e.button 0 鼠标左键 1 鼠标滚轮 2 鼠标右键 curObj.isRightClick = e.button > 1 console.log('是否右键', curObj.isRightClick); // 赋值 x,y 轴起始数据 curObj.x = e.offsetX curObj.y = e.offsetY // 判断是否落在的矩形框上 //得到落点所在框的序数 curObj.index = getEventIndex(curObj.x, curObj.y) console.log('落点矩形', curObj.index); // 如果是鼠标右键 TODO... if (curObj.isRightClick) { } else { // 鼠标左键 // 如果鼠标落点不在矩形内,画矩形 if(curObj.index === -1) { curObj.draw = true }else { // 落点在矩形内 //移动或者放缩 curObj.startX = recArrs[curObj.index].x; curObj.startY = recArrs[curObj.index].y; //得到落点在一个框中的区域 curObj.side = getEventArea(curObj.index, curObj.x, curObj.y); console.log("curObj.side", curObj.side); if (curObj.side < 9) { //准备缩放 console.log("在缩放"); curObj.resize = true; } else { //准备拖动 console.log("在拖动"); curObj.drag = true; } // 画移动小框 drawLitRecs(curObj.index); } } //判断小框类型 changeResizeCursor(curObj.side); } // 修改鼠标样式 function changeResizeCursor(side) { switch (side) { case 0: canvasDom.style.cursor = "crosshair"; break; case 1: canvasDom.style.cursor = "se-resize"; break; case 2: canvasDom.style.cursor = "e-resize"; break; case 3: canvasDom.style.cursor = "ne-resize"; break; case 4: canvasDom.style.cursor = "sw-resize"; break; case 5: canvasDom.style.cursor = "w-resize"; break; case 6: canvasDom.style.cursor = "nw-resize"; break; case 7: canvasDom.style.cursor = "s-resize"; break; case 8: canvasDom.style.cursor = "n-resize"; break; case 9: canvasDom.style.cursor = "move"; break; default: canvasDom.style.cursor = "default"; } } //得到落点在一个框中的区域 function getEventArea(index, x, y) { const { radious } = curObj const data = recArrs[index] console.log('recArrs', recArrs) console.log('data', data) if (x > data.x - radious && x < data.x + radious) { if (y > data.y - radious && y < data.y + radious) { return 1; } else if (y > data.y + radious && y < data.y + data.h - radious) { return 2; } else if ( y > data.y + data.h - radious && y < data.y + data.h + radious ) { return 3; } } else if ( x > data.x + data.w - radious && x < data.x + data.w + radious ) { if (y > data.y - radious && y < data.y + radious) { return 4; } else if (y > data.y + radious && y < data.y + data.h - radious) { return 5; } else if ( y > data.y + data.h - radious && y < data.y + data.h + radious ) { return 6; } } else { if ( y > data.y - radious && y < data.y + radious && x > data.x + radious && x < data.x + data.w - radious ) { return 7; } else if ( y > data.y + data.h - radious && y < data.y + data.h + radious && x > data.x + radious && x < data.x + data.w - radious ) { return 8; } else { return 9; } } } //把一个框的左上角坐标和宽高输入,得到8个坐标,左3,右3中2 function prepareLitRecs(index) { const data = recArrs[index] var li = []; li[0] = [data.x, data.y]; li[1] = [data.x, data.y + data.h / 2]; li[2] = [data.x, data.y + data.h]; li[3] = [data.x + data.w, data.y]; li[4] = [data.x + data.w, data.y + data.h / 2]; li[5] = [data.x + data.w, data.y + data.h]; li[6] = [data.x + data.w / 2, data.y]; li[7] = [data.x + data.w / 2, data.y + data.h]; return li; } //画移动时的小框,data为矩形框9个点的坐标 function drawLitRecs(index) { const data = prepareLitRecs(index) const { recSize } = curObj for (var i = 0; i < data.length; i++) { ctx2d.strokeRect( data[i][0] - recSize / 2, data[i][1] - recSize / 2, recSize, recSize, ); } } // 获取鼠标落点所在矩形的index, -1 表示没有落在任何框内 function getEventIndex(x, y) { if (!recArrs.length) return -1 for (var i = 0; i < recArrs.length; i++) { const limitX = x > recArrs[i].x - curObj.radious const limitW = x < recArrs[i].x + recArrs[i].w + curObj.radious const limitY = y > recArrs[i].y - curObj.radious const limitH = y < recArrs[i].y + recArrs[i].h + curObj.radious // 有在范围内的,返回index if (limitX && limitY && limitW && limitH) { return i; } // 没有返回 -1 if (i == recArrs.length - 1) { return -1; } } } // 鼠标抬起事件 function upFunction(e) { console.log('鼠标抬起', e); if(curObj.isRightClick) { if(curObj.index !== -1) { // 删除,重绘 recArrs.splice(curObj.index, 1) clearCanvas() drawOldRect() } curObj.isRightClick = false; return } curObj.resize = false curObj.drag = false // 如果是画图 if (curObj.draw) { addToRecs(e) curObj.draw = false; } console.log('鼠标抬起-当前矩形框', recArrs); } // 添加矩形 function addToRecs(e) { let rec = { x: curObj.x > e.offsetX ? e.offsetX : curObj.x, // x点 y: curObj.y > e.offsetY ? e.offsetY : curObj.y, // y点 w: Math.abs(e.offsetX - curObj.x), // 宽 h: Math.abs(e.offsetY - curObj.y), // 高 text: '矩形框' + recArrs.length, // 默认填充文本 type: 1, // 类型 // 其他需要的数据自行添加... }; // 防止误触 if(rec.w > 2 && rec.h > 2) { recArrs.push(rec) console.log('recArrs', recArrs); } } </script>
在上面的基础上增加截图功能。效果如下:
代码,可直接更换图片运行查看效果
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> * { padding: 0; margin: 0; } html, body { height: 100vh; width: 100vw; background-color: #ececec; } body { display: flex; justify-content: center; align-items: center; } /* 图像容器 */ .image_box { position: relative; height: 800px; width: 800px; } img, .canvas_dom { height: 100%; width: 100%; position: absolute; } .canvas_dom { background-color: rgba(0, 0, 0, .2); cursor: crosshair; } .cut-box { height: 800px; width: 800px; overflow: auto; background-color: bisque; margin-left: 20px; } .cut-img { height: 200px; width: 200px; position: relative; display: inline-block; } </style> </head> <body> <div> <!-- TODO 自行更换图片 --> <!-- <img src="./test.png" alt="" id="imgDom"> --> <!-- canvas 必须指定宽高,不然绘制出来的矩形会模糊和错误。 --> <canvas id="canvasDom" width="800" height="800"> no canvas! Your Browser is not support canvas. Please change your browser. </canvas> </div> <div> </div> </body> </html> <script> // 放置截图的容器 let cutDom = document.querySelector('.cut-box') // 图像 let imgDom = document.getElementById('imgDom') // canvas对象 let canvasDom = document.getElementById('canvasDom') let ctx2d = canvasDom.getContext("2d"); // 保存矩形数据 let recArrs = [] // 记录当前的信息 let curObj = { isRightClick: false, // 鼠标右键按下标识 radious: 4, // 范围误差值 recSize: 5, // 移动小框的大小 index: -1, // 当前矩形框的index side: 0, // 边界值 resize: false, // 是否拖拽大小 draw: false, // 是否画图 drag: false, // 是否拖动 x: 0, // 画图的起始x y: 0, // 画图的起始y startX: 0, // x轴开始位置 startY: 0, // y轴开始位置 } // 获取canvas的宽高 const canvasHeight = canvasDom.offsetHeight const canvasWidth = canvasDom.offsetWidth console.log('高度--', canvasHeight); console.log('宽度--', canvasWidth); // 注册事件 canvasDom.addEventListener('mousemove', moveFunction) canvasDom.addEventListener('mousedown', downFunction) canvasDom.addEventListener('mouseup', upFunction) canvasDom.addEventListener('mouseout', outFunction) canvasDom.addEventListener('contextmenu', contextMenuFunction) // 禁止鼠标右键 function contextMenuFunction(e) { e.preventDefault(); return false } // 鼠标移出画布 function outFunction(e) { let x = e.clientX; let y = e.clientY; let left = canvasDom.offsetLeft let top = canvasDom.offsetTop let width = canvasDom.offsetWidth let height = canvasDom.offsetHeight let limitX = left + width let limitY = top + height if(x < left || x > limitX || y < top || y > limitY ) { console.log('鼠标移出范围, 清除canvas,重新绘制'); clearCanvas() drawOldRect() } } // 鼠标移动事件 function moveFunction(e) { // console.log('鼠标移动', e); // 需要清除之前的辅助线 clearCanvas() // 画辅助线 drawRuler(e) // 清空辅助线和矩形数据后,这里重绘 drawOldRect() // 画矩形 drawRect(e) // 移动或缩放 moveOrScale(e) } // 移动矩形框/缩放矩形框 function moveOrScale(e) { let index = getEventIndex(e.offsetX, e.offsetY) let side = 0 if(index > -1) { side = getEventArea(index, e.offsetX, e.offsetY) // 画移动小框 if(side > 0) { drawLitRecs(index) } } // 鼠标样式 changeResizeCursor(side); // 如果在移动 moveRec(e) // 如果在缩放 reSizeRec(e) } // 移动 function moveRec(e) { let rec = recArrs[curObj.index] if(curObj.drag && rec) { let x = curObj.startX + e.offsetX - curObj.x let y = curObj.startY + e.offsetY - curObj.y let minX = canvasDom.offsetLeft let maxX = canvasDom.offsetLeft + canvasDom.offsetWidth - rec.w let minY = canvasDom.offsetTop let maxY = canvasDom.offsetTop + canvasDom.offsetHeight - rec.h if(x < minX) { x = minX } if( x > maxX) { x = maxX } if(y < minY) { y = minY } if( y > maxY) { y = maxY } rec.x = x rec.y = y // 截图 let { scleX, scleY } = getImgScale() let scx = rec.x / scleX let scy = rec.y / scleY let h = rec.h / scleY let w = rec.w / scleX rec.cutImgUrl = exportBase64(scx, scy, w, h, w, h) console.log('recArrs', recArrs); // 显示 freshImg() } } // 缩放 function reSizeRec(e) { const { side, index, recSize } = curObj const rec = recArrs[index] if(curObj.resize && rec) { const temX = rec.x; const temY = rec.y; const ex = e.offsetX const ey = e.offsetY if (side < 4 && temX + rec.w - ex > recSize) { rec.x = ex; } if ( (side == 1 || side == 4 || side == 7) && temY + rec.h - ey > recSize ) { rec.y = ey; } if (side < 4) { if (temX + rec.w - ex > recSize) { rec.w = temX + rec.w - ex; } } else if (side < 7) { if (ex - temX > recSize) { rec.w = ex - temX; } } if (side == 1 || side == 4 || side == 7) { if (temY + rec.h - ey > recSize) { rec.h = temY + rec.h - ey; } } else if (side == 3 || side == 6 || side == 8) { if (ey - temY > recSize) { rec.h = ey - temY; } } // 截图 let { scleX, scleY } = getImgScale() let x = rec.x / scleX let y = rec.y / scleY let h = rec.h / scleY let w = rec.w / scleX rec.cutImgUrl = exportBase64(x, y, w, h, w, h) console.log('recArrs', recArrs); // 显示 freshImg() } } // 鼠标移动,画辅助线 function drawRuler(e) { // 开始一条路径 ctx2d.beginPath(); // 填充色 ctx2d.strokeStyle = "red"; // 路径宽度 ctx2d.lineWidth = 1; // 移动到 鼠标的 x位置, y位置 0(竖线的起点) ctx2d.moveTo(e.offsetX, 0); // lineTo() 方法添加一个新点,(竖线的终点) ctx2d.lineTo(e.offsetX, canvasHeight); // 移动到(x: 0, y:鼠标的位置)(横线的起点) ctx2d.moveTo(0, e.offsetY); // lineTo() 方法添加一个新点,(横线的终点) ctx2d.lineTo(canvasWidth, e.offsetY); ctx2d.stroke(); } // 清除canvas function clearCanvas() { ctx2d.clearRect(0, 0, canvasWidth, canvasHeight); } // 鼠标按下并移动,画矩形 function drawRect(e) { if (curObj.draw) { ctx2d.strokeRect( curObj.x, curObj.y, e.offsetX - curObj.x, e.offsetY - curObj.y ); } } // 画初始数据 function drawOldRect() { if (!recArrs.length) return for (var i = 0; i < recArrs.length; i++) { // >2的判断是为了防止误触画出来的数据 if(recArrs[i].w > 2 && recArrs[i].h > 2) { ctx2d.beginPath(); ctx2d.lineWidth = 2; // 矩形框的线条宽度 ctx2d.strokeStyle = "rgb(255, 0, 0)"; // 矩形框的线条颜色 ctx2d.strokeRect(recArrs[i].x, recArrs[i].y, recArrs[i].w, recArrs[i].h); // 矩形框 // 如果有文本信息,填充文本信息 if (recArrs[i].text) { ctx2d.fillStyle = "purple"; ctx2d.font = "14px 微软雅黑"; ctx2d.lineWidth = 1; ctx2d.strokeStyle = "rgb(255,0,0)"; ctx2d.strokeText( recArrs[i].text, (recArrs[i].x + recArrs[i].w / 2) - (recArrs[i].text.length / 2) * 16, recArrs[i].y - 20 < 0 ? recArrs[i].y + recArrs[i].h + 20 : recArrs[i].y - 10 ); } } } } // 鼠标按下事件 function downFunction(e) { console.log('鼠标按下', e); // e.button 0 鼠标左键 1 鼠标滚轮 2 鼠标右键 curObj.isRightClick = e.button > 1 console.log('是否右键', curObj.isRightClick); // 赋值 x,y 轴起始数据 curObj.x = e.offsetX curObj.y = e.offsetY // 判断是否落在的矩形框上 //得到落点所在框的序数 curObj.index = getEventIndex(curObj.x, curObj.y) console.log('落点矩形', curObj.index); // 如果是鼠标右键 TODO... if (curObj.isRightClick) { } else { // 鼠标左键 // 如果鼠标落点不在矩形内,画矩形 if(curObj.index === -1) { curObj.draw = true }else { // 落点在矩形内 //移动或者放缩 curObj.startX = recArrs[curObj.index].x; curObj.startY = recArrs[curObj.index].y; //得到落点在一个框中的区域 curObj.side = getEventArea(curObj.index, curObj.x, curObj.y); console.log("curObj.side", curObj.side); if (curObj.side < 9) { //准备缩放 console.log("在缩放"); curObj.resize = true; } else { //准备拖动 console.log("在拖动"); curObj.drag = true; } // 画移动小框 drawLitRecs(curObj.index); } } //判断小框类型 changeResizeCursor(curObj.side); } // 修改鼠标样式 function changeResizeCursor(side) { switch (side) { case 0: canvasDom.style.cursor = "crosshair"; break; case 1: canvasDom.style.cursor = "se-resize"; break; case 2: canvasDom.style.cursor = "e-resize"; break; case 3: canvasDom.style.cursor = "ne-resize"; break; case 4: canvasDom.style.cursor = "sw-resize"; break; case 5: canvasDom.style.cursor = "w-resize"; break; case 6: canvasDom.style.cursor = "nw-resize"; break; case 7: canvasDom.style.cursor = "s-resize"; break; case 8: canvasDom.style.cursor = "n-resize"; break; case 9: canvasDom.style.cursor = "move"; break; default: canvasDom.style.cursor = "default"; } } //得到落点在一个框中的区域 function getEventArea(index, x, y) { const { radious } = curObj const data = recArrs[index] console.log('recArrs', recArrs) console.log('data', data) if (x > data.x - radious && x < data.x + radious) { if (y > data.y - radious && y < data.y + radious) { return 1; } else if (y > data.y + radious && y < data.y + data.h - radious) { return 2; } else if ( y > data.y + data.h - radious && y < data.y + data.h + radious ) { return 3; } } else if ( x > data.x + data.w - radious && x < data.x + data.w + radious ) { if (y > data.y - radious && y < data.y + radious) { return 4; } else if (y > data.y + radious && y < data.y + data.h - radious) { return 5; } else if ( y > data.y + data.h - radious && y < data.y + data.h + radious ) { return 6; } } else { if ( y > data.y - radious && y < data.y + radious && x > data.x + radious && x < data.x + data.w - radious ) { return 7; } else if ( y > data.y + data.h - radious && y < data.y + data.h + radious && x > data.x + radious && x < data.x + data.w - radious ) { return 8; } else { return 9; } } } //把一个框的左上角坐标和宽高输入,得到8个坐标,左3,右3中2 function prepareLitRecs(index) { const data = recArrs[index] var li = []; li[0] = [data.x, data.y]; li[1] = [data.x, data.y + data.h / 2]; li[2] = [data.x, data.y + data.h]; li[3] = [data.x + data.w, data.y]; li[4] = [data.x + data.w, data.y + data.h / 2]; li[5] = [data.x + data.w, data.y + data.h]; li[6] = [data.x + data.w / 2, data.y]; li[7] = [data.x + data.w / 2, data.y + data.h]; return li; } //画移动时的小框,data为矩形框9个点的坐标 function drawLitRecs(index) { const data = prepareLitRecs(index) const { recSize } = curObj for (var i = 0; i < data.length; i++) { ctx2d.strokeRect( data[i][0] - recSize / 2, data[i][1] - recSize / 2, recSize, recSize, ); } } // 获取鼠标落点所在矩形的index, -1 表示没有落在任何框内 function getEventIndex(x, y) { if (!recArrs.length) return -1 for (var i = 0; i < recArrs.length; i++) { const limitX = x > recArrs[i].x - curObj.radious const limitW = x < recArrs[i].x + recArrs[i].w + curObj.radious const limitY = y > recArrs[i].y - curObj.radious const limitH = y < recArrs[i].y + recArrs[i].h + curObj.radious // 有在范围内的,返回index if (limitX && limitY && limitW && limitH) { return i; } // 没有返回 -1 if (i == recArrs.length - 1) { return -1; } } } // 鼠标抬起事件 function upFunction(e) { console.log('鼠标抬起', e); if(curObj.isRightClick) { if(curObj.index !== -1) { // 删除,重绘 recArrs.splice(curObj.index, 1) clearCanvas() drawOldRect() // 显示 freshImg() } curObj.isRightClick = false; return } curObj.resize = false curObj.drag = false // 如果是画图 if (curObj.draw) { addToRecs(e) curObj.draw = false; } console.log('鼠标抬起-当前矩形框', recArrs); } // 添加矩形 function addToRecs(e) { let rec = { x: curObj.x > e.offsetX ? e.offsetX : curObj.x, // x点 y: curObj.y > e.offsetY ? e.offsetY : curObj.y, // y点 w: Math.abs(e.offsetX - curObj.x), // 宽 h: Math.abs(e.offsetY - curObj.y), // 高 text: '矩形框' + recArrs.length, // 默认填充文本 type: 1, // 类型 // 其他需要的数据自行添加... }; // 防止误触 if(rec.w > 2 && rec.h > 2) { // 添加截图 let { scleX, scleY } = getImgScale() let x = rec.x / scleX let y = rec.y / scleY let h = rec.h / scleY let w = rec.w / scleX rec.cutImgUrl = exportBase64(x, y, w, h, w, h) recArrs.push(rec) console.log('recArrs', recArrs); // 显示 freshImg() } } // 获取当前图片的缩放比例 function getImgScale() { // 图片原始大小 let imgDefWidth = imgDom.naturalWidth let imgDefHeight = imgDom.naturalHeight // 图片的实际渲染尺寸 let imgRenderWidth = imgDom.offsetWidth let imgRenderHeight = imgDom.offsetHeight // 图片x轴和y轴的缩放比例 let scleX = imgRenderWidth / imgDefWidth let scleY = imgRenderHeight / imgDefHeight console.log('X轴缩放比例', scleX); console.log('Y轴缩放比例', scleY); return { scleX, scleY } } // 将所框的数据转为图片地址 /** * * @param {剪切的x轴开始坐标,相对于原图} x * @param {剪切的y轴开始坐标,相对于原图} y * @param {剪切的宽,相对于原图} sWidth * @param {剪切的高,相对于原图} sHeight * @param {输出的宽,默认按照剪切框大小输出} rW * @param {输出的高,默认按照剪切框大小输出} rH */ function exportBase64(x, y, sWidth, sHeight, rW = 200, rH = 200) { // 创建一个新的canvas let canvasElement = document.createElement("canvas"); canvasElement.width = `${rW}` canvasElement.height = `${rH}` let canvasContext = canvasElement.getContext("2d"); canvasContext.drawImage(imgDom, x, y, sWidth, sHeight, 0, 0, rW, rH); let cutPicUrl = canvasElement.toDataURL("image/jpeg"); return cutPicUrl } function freshImg() { // 先清除 cutDom.innerHTML = '' recArrs.forEach( rec => { let item = document.createElement('img') item.src = rec.cutImgUrl item.className = 'cut-img' cutDom.appendChild(item) }) } </script>
猜你喜欢
- 【前端】全栈软件开发工程师需要具备哪些技能
- 全栈软件开发工程师需要具备前端、后端和数据库等多方面的技能,以便能够在整个应用开发周期中承担各种任务。以下是典型的全栈软件开发工程师的技能栈:一、前端技能:1.HTML/CSS/JavaScript:构建网页的基本技能,负责页面结构、样式和交互。2.前端框架:掌握至少一种前端框架,如 React.js、Vue.js、Angular,用于构建可维护的、高性能的前端应用。3.前端工具:使用构建工具(Webpack、Parcel)、包管理工具(npm、Yarn)、代码规范工具(ESLint、Prett
- 【前端】支付宝和微信小程序普通二维码跳转二合一
- 业务需要:系统支持微信小程序和支付宝小程序,现在要扫码跳转到对应小程序,要使用一个二维码,并能携带业务参数,用户使用微信扫码,就能打开微信小程序并获取参数,使用支付宝扫码,就能打开支付宝小程序,并获取参数一、 微信小程序普通二维码跳转实现1、在微信公众平台-开发管理-开发设置中找到【扫描普通链接二维码打开小程序】去配置二维码链接地址2、配置要求二维码规则:配置为二维码对应的链接,其中最后要加/,这样才可以动态传参。扫描后面参数不同的二维码都能跳转。这样类似与测试链接中 https:
- 【前端】前端网络安全
- 今天思考下前端源码安全的东西(不是前端安全,只是针对于源码部分)。在我看来,源码安全有两点,一是防止抄袭,二是防止被攻破。实际上讲,前端的代码大多是没有什么可抄袭性,安全更是形同虚设的(任何前端输入都是不能相信的)。但如果还是想防止源码被查看,HTML、CSS并不能做什么,最终都会用露出来(最简单用Chrome开发者工具就可以看到),所以只能针对JS做文件的压缩合并和混淆。关于抄袭其实就前端来讲,代码没有什么好抄袭的,大多人都是抄UI设计(这个是躲不了),还有一些富前端的控件和算法,重要之处还是
- 【前端】快速了解 ES6 新增字符串方法,开箱即用(含案例)
- 文章目录📋前言🎯includes() 方法🎯startsWith() 方法🎯endsWith() 方法🎯repeat() 方法🎯padStart() 方法🎯padEnd() 方法🎯trim() 方法🎯trimStart() 或 trimLeft() 方法🎯trimEnd() 或 trimRight() 方法🎯replaceAll() 方法🎯slice() 方法🎯substring() 方法🎯split() 方法🎯charAt() 方法🎯charCodeAt() 方
- 【前端】vite+vue3+ts 项目安装 Ant Design of Vue方法
- 安装 Ant Design of Vue 和@ant-design/icons-vue图标库npm add ant-design-vue @ant-design/icons-vue安装插件 unplugin-vue-components 配合vite可以自动帮我们引入组件npm add unplugin-vue-components -D配置 vite.config.tsimport {&nbs
- 【Html】H5跳转支付宝小程序的两种方式
- H5跳转支付宝小程序的两种方式,第一种方式还可以打开支付宝生活号网页,只是换成appid换成网页应用appid,page参数换成url参数,url对应的是自己页面的链接
- 【HTML】 html 初始化执行方法
- HTML的初始化执行方法有多种。以下是其中几种常见的方式: 1. JavaScript的DOMContentLoaded事件:当页面加载完成并且所有元素都已经被创建时,会触发该事件。可以通过在JavaScript文件或者内联script标签中编写相应的函数来处理这个事件。示例如下:document.addEventListener("DOMContentLoaded", function() { // 在此处添加需要执行的代
- 【前端】PHP开发者的Vue与React入门指南
- PHP是一种常用的服务器端脚本语言,用于开发动态网页和网站。许多PHP开发者经常需要学习前端框架来提升他们的技能,并且Vue.js和React.js是两个当前非常热门的前端框架。本文将为PHP开发者提供一份Vue.js和React.js的入门指南,带有具体的代码示例,帮助他们快速了解这两个框架的基本概念和用法。1. Vue.js入门指南Vue.js是一款轻量级的JavaScript框架,用于构建交互式的用户界面。下面是一个简单的Vue.js示例,展示了如何创建一个基本的Vue组件并进行数据绑定:
栏目分类全部>