600字范文,内容丰富有趣,生活中的好帮手!
600字范文 > VUE3 实现前台图片标注添加矩形框 图片放大 缩小 鼠标滚轮缩放

VUE3 实现前台图片标注添加矩形框 图片放大 缩小 鼠标滚轮缩放

时间:2021-03-28 02:41:26

相关推荐

VUE3 实现前台图片标注添加矩形框 图片放大 缩小 鼠标滚轮缩放

VUE3 实现前台图片标注

功能包括:

鼠标左键拖动添加矩形框标记区域,鼠标点击已绘制的矩形; 选中矩形,并绘制不同选中效果; 鼠标在已绘制的矩形中按住左键拖动,选中并移动矩形;选中矩形后鼠标在选中矩形四个角按住拖动,可缩放矩形;选中矩形后点击键盘删除按键,可删除矩形;获取矩形框位置坐标大小;图片缩放功能、鼠标滚轮缩放

效果如图:

<template><div id="content" ><div id="imgContent" @mousewheel="rollImg()"><img id="bigImg" src="../assets/goManage/testing.png" /><canvas id="canvas" ref="markCanvasRef"></canvas></div></div><el-button type="primary" style="margin: 20px;" @click="saveData">保存数据</el-button><el-button type="primary" style="margin: 20px;" @click="scaleLarge">放大</el-button><el-button type="primary" style="margin: 20px;" @click="scaleSmall">缩小</el-button></template><script lang="ts" setup>import {draw} from "../utils/draw"; // 矩形绘制方法import {ref,reactive,onMounted,nextTick} from 'vue'onMounted(()=>{initCanvas(); // 画布初始化})// 标记内容数组const markList = ref([// {x: 151, y: 384, w: 320, h: 86}])const markCanvasRef = ref(null);const bigImgRef = ref(null);//画布初始化const initCanvas = ()=> {setTimeout(()=>{nextTick(() => {// 初始化canvas宽高let cav = markCanvasRef.value;console.log(document.getElementById('imgContent').clientHeight);cav.width = document.getElementById('imgContent').offsetWidth;cav.height = document.getElementById('imgContent').offsetHeight;console.log(cav);let ctx = cav.getContext('2d');ctx.strokeStyle = 'blue';cav.style.cursor = 'crosshair';// 计算使用变量let list = markList.value; // 画框数据集合, 用于服务端返回的数据显示和绘制的矩形保存// // 若服务端保存的为百分比则此处需计算实际座标, 直接使用实际座标可省略// list.forEach(function (value, index, array) {//let newValue = {// x: value.x * cav.width,// y: value.y * cav.height,// w: value.w * cav.width,// h: value.h * cav.height,//}//list.splice(index, 1, newValue)// })// 若list长度不为0, 则显示已标记框if (list.length !== 0) {list.forEach(function (value, index, array) {// 遍历绘制所有标记框ctx.rect(value.x, value.y, value.w, value.h);ctx.stroke();});}// 调用封装的绘制方法draw(cav, list);})},1000)}const saveData =()=>{console.log("矩形数据",markList.value);}//放大const scaleLarge = ()=>{var zoom = parseInt(document.getElementById('bigImg').style.zoom) || 100;zoom += 10;document.getElementById('bigImg').style.zoom = zoom + '%';}//缩小const scaleSmall = ()=>{var zoom = parseInt(document.getElementById('bigImg').style.zoom) || 100;zoom -= 10;document.getElementById('bigImg').style.zoom = zoom + '%';}//滚轮缩放const rollImg = ()=>{/* 获取当前页面的缩放比若未设置zoom缩放比,则为默认100%,即1,原图大小*/console.log(document.getElementById('bigImg').style);var zoom = parseInt(document.getElementById('bigImg').style.zoom) || 100;/* event.wheelDelta 获取滚轮滚动值并将滚动值叠加给缩放比zoom wheelDelta统一为±120,其中正数表示为向上滚动,负数表示向下滚动 */zoom += event.wheelDelta / 12;/* 最小范围 和 最大范围 的图片缩放尺度 */if (zoom >= 100 && zoom <250) {document.getElementById('bigImg').style.zoom = zoom + "%";}return false;}</script><style lang="less" scoped>#content {width: 800px;height: 600px;overflow: auto;#imgContent{display: inline-block;position: relative;margin: 0 auto;height: auto;width: auto;}canvas {position: absolute;top: 0;left: 0;z-index: 10;width: 100%;height: 100%;}}</style>

draw.js

/*** 画布中绘制矩形* 参数: cav-画布对象 list-矩形数组 i-选中矩形下标**//* 操作执行方法分发 */export function draw(cav, list, i) {// 画布初始化let ctx = cav.getContext('2d');ctx.strokeStyle = 'blue';ctx.lineWidth = 2;// 变量初始化let sX = 0; // 鼠标X坐标let sY = 0; // 鼠标Y坐标/**鼠标移动进行第一层判断, 区分情况: 无矩形, 已有矩形无选中, 已有选中矩形*/cav.onmousemove = function (em) {sX = em.offsetX;sY = em.offsetY;let iem = undefined; // 鼠标移动时临时存储当前鼠标所在矩形的下标if (list.length === 0) {// **** 无矩形 ****// 绘制新矩形newDraw(cav, ctx, list);} else if (i === undefined) {// **** 已有矩形无选中 ****// 判断鼠标位置list.forEach(function (value, index, array) {if (value.w > 0 && value.h > 0 && sX > value.x && sX < value.x + value.w && sY > value.y && sY < value.y + value.h) {// 鼠标在右下方向生成的矩形中iem = index;judgeDraw(cav, ctx, list, iem);}if (value.w < 0 && value.h > 0 && sX < value.x && sX > value.x + value.w && sY > value.y && sY < value.y + value.h) {// 鼠标在左下方向生成的矩形中iem = index;judgeDraw(cav, ctx, list, iem);}if (value.w > 0 && value.h < 0 && sX > value.x && sX < value.x + value.w && sY < value.y && sY > value.y + value.h) {// 鼠标在右上方向生成的矩形中iem = index;judgeDraw(cav, ctx, list, iem);}if (value.w < 0 && value.h < 0 && sX < value.x && sX > value.x + value.w && sY < value.y && sY > value.y + value.h) {// 鼠标在左上方向生成的矩形中iem = index;judgeDraw(cav, ctx, list, iem);}if (iem === undefined) {// 鼠标不在矩形中newDraw(cav, ctx, list);}})} else {// **** 已有选中矩形 ****// 判断鼠标位置for (let index = 0; index < list.length; index++) {let value = list[index];if (sX < value.x + 5 && sX > value.x - 5 && sY < value.y + 5 && sY > value.y - 5) {// *** 鼠标在起点角 ***if (index === i) {changeDraw(cav, ctx, list, i, 1);break;}} else if (sX < value.x + value.w + 5 && sX > value.x + value.w - 5 && sY < value.y + 5 && sY > value.y - 5) {// *** 鼠标在起点横向角 ***if (index === i) {changeDraw(cav, ctx, list, i, 2);break;}} else if (sX < value.x + 5 && sX > value.x - 5 && sY < value.y + value.h + 5 && sY > value.y + value.h - 5) {// *** 鼠标在起点纵向角 ***if (index === i) {changeDraw(cav, ctx, list, i, 3);break;}} else if (sX < value.x + value.w + 5 && sX > value.x + value.w - 5 && sY < value.y + value.h + 5 && sY > value.y + value.h - 5) {// *** 鼠标在终点角 ***if (index === i) {changeDraw(cav, ctx, list, i, 4);break;}} else if (value.w > 0 && value.h > 0 && sX > value.x && sX < value.x + value.w && sY > value.y && sY < value.y + value.h) {// *** 鼠标在右下方向生成的矩形中 ***iem = indexjudgeDraw(cav, ctx, list, index);break;} else if (value.w < 0 && value.h > 0 && sX < value.x && sX > value.x + value.w && sY > value.y && sY < value.y + value.h) {// *** 鼠标在左下方向生成的矩形中 ***iem = indexjudgeDraw(cav, ctx, list, index);break;} else if (value.w > 0 && value.h < 0 && sX > value.x && sX < value.x + value.w && sY < value.y && sY > value.y + value.h) {// *** 鼠标在右上方向生成的矩形中 ***iem = indexjudgeDraw(cav, ctx, list, index);break;} else if (value.w < 0 && value.h < 0 && sX < value.x && sX > value.x + value.w && sY < value.y && sY > value.y + value.h) {// *** 鼠标在左上方向生成的矩形中 ***iem = indexjudgeDraw(cav, ctx, list, index);break;} else {if (iem === undefined) {// *** 鼠标不在矩形中 ***newDraw(cav, ctx, list);}}}}/* 鼠标移出画布区域时保存选中矩形下标(如有) */cav.onmouseout = function (eo) {if (i !== undefined) {// 初始化draw(cav, list, i);}};}// console.log(cav, list, i);return list;}/* 编辑矩形四个角 */function changeDraw(cav, ctx, list, i, site) {cav.style.cursor = 'pointer'// site: 操作矩形角的位置, 1-起点 2-起点横向 3-起点纵向 4-终点let mark = list[i];/* 按下鼠标左键 */cav.onmousedown = function (ed) {// 保存鼠标落下位置的X, Y坐标, firefox中鼠标移动后ed.offsetX ed.offsetY会变成 0, 需要使用临时参数存储起来let sX = ed.offsetX; // 起点X坐标let sY = ed.offsetY; // 起点Y坐标/* 移动鼠标 */cav.onmousemove = function (em) {// 计算绘制数据let iframe = {}switch (site) {case 1:iframe = {x: em.offsetX,y: em.offsetY,w: mark.w - (em.offsetX - sX),h: mark.h - (em.offsetY - sY)}break;case 2:iframe = {x: mark.x,y: mark.y + (em.offsetY - sY),w: mark.w + (em.offsetX - sX),h: mark.h - (em.offsetY - sY)}break;case 3:iframe = {x: mark.x + (em.offsetX - sX),y: mark.y,w: mark.w - (em.offsetX - sX),h: mark.h + (em.offsetY - sY)}break;case 4:iframe = {x: mark.x,y: mark.y,w: mark.w + (em.offsetX - sX),h: mark.h + (em.offsetY - sY)}break;}list.splice(i, 1, iframe);// 重新绘制reDraw(cav, ctx, list, i);}/* 鼠标离开矩形区 */cav.onmouseout = function (eo) {// 重新绘制reDraw(cav, ctx, list);// 初始化draw(cav, list)};/* 监听键盘, 点击后可以控制删除, 由于移动矩形事件已经监听了onmousemove, 所以在移动矩形方法中仍有一次调用 */delDraw(cav, ctx, list, i);}}/* 绘制新矩形 */function newDraw(cav, ctx, list) {cav.style.cursor = 'crosshair'// 初始化变量let start = false; // 画框状态, false时不执行画框操作let sX = 0; // 起点X坐标let sY = 0; // 起点Y坐标/* 按下鼠标左键 */cav.onmousedown = function (ed) {/* 使用变量 */start = true;sX = ed.offsetX;sY = ed.offsetY;/* 重置按键监听, 防止选中取消后仍可删除 */delDraw(cav, ctx, list, null)/* 鼠标移动 */cav.onmousemove = function (em) {if (start) {// 重新绘制reDraw(cav, ctx, list);// 设置边框为虚线ctx.beginPath();ctx.setLineDash([8, 4]);ctx.rect(sX, sY, em.offsetX - sX, em.offsetY - sY);ctx.stroke();}}/* 鼠标抬起 */cav.onmouseup = function (eu) {if (start && Math.abs(eu.offsetX - sX) > 10 && Math.abs(eu.offsetY - sY) > 10) {// 改变矩形数组let frame = {x: sX, y: sY, w: eu.offsetX - sX, h: eu.offsetY - sY};list.push(frame);// 重新绘制reDraw(cav, ctx, list);// 改变画框状态start = false// 初始化draw(cav, list)} else {// 重新绘制reDraw(cav, ctx, list);// 改变画框状态start = false// 初始化draw(cav, list)}};/* 鼠标离开矩形区 */cav.onmouseout = function (eo) {if (start && Math.abs(eo.offsetX - sX) > 10 && Math.abs(eo.offsetY - sY) > 10) {// 改变矩形数组let frame = {x: sX, y: sY, w: eo.offsetX - sX, h: eo.offsetY - sY};list.push(frame);// 重新绘制reDraw(cav, ctx, list);// 改变画框状态start = false;// 初始化draw(cav, list)} else {// 重新绘制reDraw(cav, ctx, list);// 改变画框状态start = false// 初始化draw(cav, list)}};}}/* 选中矩形, 重绘矩形, 并分发后续事件 */function judgeDraw(cav, ctx, list, iem) {cav.style.cursor = 'default';// 初始化变量let sX = 0; // 起点X坐标let sY = 0; // 起点Y坐标/* 按下鼠标左键 */cav.onmousedown = function (ed) {sX = ed.offsetX;sY = ed.offsetY;// 更改选中状态, 重绘矩形reDraw(cav, ctx, list, iem);/* 当仅点击选中矩形便抬起鼠标后, 重新初始化画布 */cav.onmouseup = function () {// 重绘矩形reDraw(cav, ctx, list, iem);// 初始化draw(cav, list, iem);};/* 按住拖动鼠标, 移动选中矩形*/moveDraw(cav, ctx, list, iem, sX, sY);/* 监听键盘, 点击后可以控制删除, 由于移动矩形事件已经监听了onmousemove, 所以在移动矩形方法中仍有一次调用 */delDraw(cav, ctx, list, iem);}}/* 移动矩形 */function moveDraw(cav, ctx, list, i, sX, sY) {let mark = list[i]cav.onmousemove = function (em) {let iframe = {x: mark.x + (em.offsetX - sX),y: mark.y + (em.offsetY - sY),w: mark.w,h: mark.h}list.splice(i, 1, iframe);/* 监听键盘, 使矩形在移动后仍可删除, 在点击未移动过的矩形时仍有一次监听 */delDraw(cav, ctx, list, i);// 重新绘制reDraw(cav, ctx, list, i);}cav.onmouseup = function () {// 重绘矩形reDraw(cav, ctx, list, i);// 初始化draw(cav, list, i);};}/* 删除矩形 */function delDraw(cav, ctx, list, i) {/* 按键事件 */if (i === null) {// i为null时阻止按键监听事件冒泡document.onkeydown = function (k) {return false;}} else {// 监听按键事件document.onkeydown = function (k) {let key = k.keyCode || k.which;if ((key == 46 || key == 8 ) && i !== null) {if (list.length >= 1) {// 删除数组元素list.splice(i, 1);// 重绘矩形reDraw(cav, ctx, list);} else {/* 矩形数组长度为0, 已将矩形框全部删除 */ctx.clearRect(0, 0, cav.width, cav.height);}// 重置监听状态, 防止删除完毕后, 按键监听不消失delDraw(cav, ctx, list, null)// 重绘矩形reDraw(cav, ctx, list);// 初始化draw(cav, list);}}}}/* 重绘所有矩形 */function reDraw(cav, ctx, list, i) {ctx.setLineDash([8, 0]); // 设置边框为实线ctx.clearRect(0, 0, cav.width, cav.height);// 绘制未选中部分list.forEach(function (value, index, array) {if (i === undefined || index != i) {ctx.beginPath();ctx.strokeStyle = 'blue';ctx.rect(value.x, value.y, value.w, value.h);ctx.stroke();}});// 绘制已选中部分list.forEach(function (value, index, array) {if (index === i) {/* 绘制方框 */ctx.beginPath();ctx.strokeStyle = 'red';ctx.rect(value.x, value.y, value.w, value.h);ctx.fillStyle = 'RGBA(102,102,102,0.2)'ctx.fillRect(value.x, value.y, value.w, value.h);ctx.stroke();// 绘制四个角的圆圈ctx.beginPath();ctx.strokeStyle = 'red';ctx.arc(value.x, value.y, 4, 0, Math.PI * 2)ctx.fillStyle = "red";ctx.fill();// 画起点实心圆ctx.stroke();ctx.beginPath();ctx.arc(value.x, value.y + value.h, 4, 0, Math.PI * 2);ctx.fillStyle = "red";ctx.fill();// 画起点纵向实心圆ctx.stroke();ctx.beginPath();ctx.arc(value.x + value.w, value.y + value.h, 4, 0, Math.PI * 2);ctx.fillStyle = "red";ctx.fill();// 画起点横向实心圆ctx.stroke();ctx.beginPath();ctx.arc(value.x + value.w, value.y, 4, 0, Math.PI * 2);ctx.fillStyle = "red";ctx.fill();// 画终点实心圆ctx.stroke();}})}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。