【前端】使用canvas做一个可绘制矩形的画布(带有移动,缩放,删除)
CrazyPanda发表于:2024-04-07 16:08:20浏览:345次
效果如下
下面直接看代码吧,可以直接复制运行
<!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>
猜你喜欢
- 【前端】vite和webpack的区别是什么
- 区别:1、webpack服务器启动速度比vite慢;由于vite启动的时候不需要打包,也就无需分析模块依赖、编译,所以启动速度非常快。2、vite热更新比webpack快;vite在hrm方面,当某个模块内容改变时,让浏览器去重新请求该模块即可。3、vite用esbuild预构建依赖,而webpack基于node。4、vite的生态不及webpack,加载器、插件不够丰富。本教程操作环境:windows7系统、vue3版,DELL G3电脑。写在开头最近的vite比较火,而且发布了2.0版本,v
- 【HTML】箩筐地图的使用,设置考勤范围
- 接上篇【PHP】html使用高德地图设置考勤范围,项目换掉了高德地图,替换成了箩筐地图,继续实现考勤打卡范围设置。需求:系统需要考勤功能,并在WEB端设置考勤范围,用于员工手机端在此范围内打卡签到,WEB端需要设置考勤地点以及考勤范围。 上篇高德地图已经实现了此功能,现在要换成箩筐地图实现此功能 基本思路:绘制地图->根据地址获取坐标->根据坐标绘制圆形范围->根据半径自适应显示圆形范围1 箩筐地图箩筐地图开放平台https://testlbs.luokuang.com/ 同样
- 【前端】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
- 【前端】微信小程序跳转公众号的三种方式
- 最近因为项目需要,要在小程序页面添加按钮,点击跳转公众号对应页面,目前没有直接点击按钮从小程序跳转到公众号页面的方法,但也有变相的实现方法,最后采用小程序webview内嵌公众号页面的方法来实现相关功能,在此记录一下:相关参考:小程序跳转公众号的三种方法 | 微信开放社区 (qq.com)开放能力 / official-account (qq.com)方法: 1: 公众号组件<official-account></official-account>
- 【前端】前端实现文件下载自动打开预览的解决方法
- 问题:前端使用React开发,想要下载阿里云OSS存储的视频文件,使用了<a>标签,点击后就会在浏览器打开预览,查找到了几个解决办法,在此记录一下。一、 下载文件的三种通用方式1、使用iframe实现只需要传一个文件下载地址的url即可 downloadFile = (url) => { //下载方法 console.log(url)
- 【前端】PHP开发者的Vue与React入门指南
- PHP是一种常用的服务器端脚本语言,用于开发动态网页和网站。许多PHP开发者经常需要学习前端框架来提升他们的技能,并且Vue.js和React.js是两个当前非常热门的前端框架。本文将为PHP开发者提供一份Vue.js和React.js的入门指南,带有具体的代码示例,帮助他们快速了解这两个框架的基本概念和用法。1. Vue.js入门指南Vue.js是一款轻量级的JavaScript框架,用于构建交互式的用户界面。下面是一个简单的Vue.js示例,展示了如何创建一个基本的Vue组件并进行数据绑定:
- 【前端】JavaScript判断数组对象是否含有某个值的方法(6种)
- 【JavaScript基础语法】web前端判断数组对象是否含有某个值的方法(6种)知识回调场景复现实现方式(6种)利用循环遍历数组元素利用some,filter方法利用array.indexOf方法利用array.includes方法利用array.find方法利用set中has方法本期小结知识回调文章内容文章链接vue3 antd table表格的增删改查(一)input输入框根据关键字搜索【后台管理系统纯前端filter过滤】https://blog.csdn.net/XSL_HR/arti
栏目分类全部>