vue实现一个矩形标记区域(rectangle marker)的方法
代码地址:vue-rectangle-marker
一、前言
一些cms系统经常会用到区域标记功能,所以写了个用vue实现的矩形标记区域,包含拖拽、放大缩小、重置功能。
二、实现结果
1.初始
2.标记
三、代码实现
<template><div class='rectangle-marker'><div class='mark-wrap'><img ref='backImg' :src='https://www.haobala.com/bcjs/imgUrl' alt='响应式图像' @load='onload'><div : @mousemove='mouseMove'@mousedown='mouseDown' @mouseup='mouseUp'><div ref='box' v-if='boxVisible' : class='box':style='{ width: boxW + ’px’, height: boxH + ’px’, left: boxL + ’px’, top: boxT + ’px’ }'><div @mousedown='onUpleftbtn'></div><div @mousedown='onUpRightbtn'></div><div @mousedown='onDownleftbtn'></div><div @mousedown='onDownRightbtn'></div></div></div><transition name='fade'><div v-if='showBtns && !markFlag' @mouseleave='mouseLeave'><button @click='mark'>mark</button> <button @click='reset'>reset</button></div></transition></div></div></template><script>export default {name: ’rectangleMarker’,data() {return {imgW: 0,imgH: 0,showBtns: true,markFlag: false,// 鼠标事件属性dragging: false,startX: undefined,startY: undefined,diffX: undefined,diffY: undefined,obj: null, //当前操作对象box: null, //要处理的对象backImgRect: null,boxId: ’’,boxW: 0,boxH: 0,boxL: 0,boxT: 0,boxVisible: false}},props: {imgUrl: {type: String,required: true,default: ’’},disabled: {type: Boolean,default: false},value: {type: Array,default: function () {return []}}},methods: {onload() {let rect = this.$refs.backImg.getBoundingClientRect()this.backImgRect = {height: rect.height,width: rect.width}// console.log('initConfig -> this.backImgRect', this.backImgRect)if (this.value === ’’ || this.value === undefined || this.value === null || (Array.isArray(this.value) && this.value.length === 0)) {return}this.initData(this.value)},mouseLeave() {this.showBtns = false},mark() {this.markFlag = true},reset() {this.boxVisible = falsethis.boxId = ’’this.boxH = 0this.boxW = 0this.boxL = 0this.boxT = 0},initData(data) {if (data === ’’ || data === undefined || data === null || (Array.isArray(data) && data.length === 0)) {return}this.boxId = ’changeBox’this.boxL = data[0][0] * this.backImgRect.widththis.boxT = data[0][1] * this.backImgRect.heightthis.boxH = (data[3][1] - data[0][1]) * this.backImgRect.heightthis.boxW = (data[1][0] - data[0][0]) * this.backImgRect.widththis.boxVisible = true},mouseDown(e) {if (!this.markFlag && !this.boxVisible) {return}this.startX = e.offsetX;this.startY = e.offsetY;// 如果鼠标在 box 上被按下if (e.target.className.match(/box/)) {// 允许拖动this.dragging = true;// 设置当前 box 的 id 为 movingBoxif (this.boxId !== ’movingBox’) {this.boxId = ’movingBox’}// 计算坐标差值this.diffX = this.startXthis.diffY = this.startY} else {if (this.boxId === ’changeBox’) {return}this.boxId = ’activeBox’this.boxT = this.startYthis.boxL = this.startXthis.boxVisible = true}},mouseMove(e) {if (!this.markFlag && !this.boxVisible) {if (!this.backImgRect) {return}let toRight = this.backImgRect.width - e.offsetXlet toTop = e.offsetYif (toRight <= 100 && toTop <= 40) {this.showBtns = true}return}let toRight = this.backImgRect.width - e.offsetXlet toTop = e.offsetYif (toRight <= 100 && toTop <= 40) {this.showBtns = truereturn}// 更新 box 尺寸if (this.boxId === ’activeBox’) {this.boxW = e.offsetX - this.startXthis.boxH = e.offsetY - this.startY}// 移动,更新 box 坐标if (this.boxId === ’movingBox’ && this.dragging) {let realTop = (e.offsetY + e.target.offsetTop - this.diffY) > 0 ? (e.offsetY + e.target.offsetTop -this.diffY) : 0let realLeft = (e.offsetX + e.target.offsetLeft - this.diffX) > 0 ? (e.offsetX + e.target.offsetLeft -this.diffX) : 0let maxTop = this.backImgRect.height - this.$refs.box.offsetHeightlet maxLeft = this.backImgRect.width - this.$refs.box.offsetWidthrealTop = realTop >= maxTop ? maxTop : realToprealLeft = realLeft >= maxLeft ? maxLeft : realLeftthis.boxT = realTop;this.boxL = realLeft;}if (this.obj) {e = e || window.event;var location = {x: e.x || e.offsetX,y: e.y || e.offsetY}switch (this.obj.operateType) {case 'nw':this.move(’n’, location, this.$refs.box);this.move(’w’, location, this.$refs.box);break;case 'ne':this.move(’n’, location, this.$refs.box);this.move(’e’, location, this.$refs.box);break;case 'sw':this.move(’s’, location, this.$refs.box);this.move(’w’, location, this.$refs.box);break;case 'se':this.move(’s’, location, this.$refs.box);this.move(’e’, location, this.$refs.box);break;case 'move':this.move(’move’, location, this.box);break;}}},mouseUp() {if (!this.markFlag && !this.boxVisible) {return}// 禁止拖动this.dragging = false;if (this.boxId === ’activeBox’) {if (this.$refs.box) {this.boxId = ’changeBox’if (this.$refs.box.offsetWidth < 3 || this.$refs.box.offsetHeight < 3) {this.boxVisible = falsethis.boxId = ’’}}} else {if (this.$refs.box && this.boxId === ’movingBox’) {this.boxId = ’changeBox’if (this.$refs.box.offsetWidth < 3 || this.$refs.box.offsetHeight < 3) {this.boxVisible = falsethis.boxId = ’’}}}if (this.boxVisible) {this.getHotData()document.body.style.cursor = 'auto';this.obj = null;this.markFlag = false} else {this.markFlag = true}},getHotData() {let target = this.$refs.boxif (target) {let {offsetTop,offsetLeft} = targetlet {width: WIDTH,height: HEIGHT} = this.backImgRectlet {width,height} = target.getBoundingClientRect()// 矩形区域 角点位置(百分比)let data = [[this.toFixed6(offsetLeft, WIDTH), this.toFixed6(offsetTop, HEIGHT)],[this.toFixed6(offsetLeft + width, WIDTH), this.toFixed6(offsetTop, HEIGHT)],[this.toFixed6(offsetLeft + width, WIDTH), this.toFixed6(offsetTop + height, HEIGHT)],[this.toFixed6(offsetLeft, WIDTH), this.toFixed6(offsetTop + height, HEIGHT)]]// 矩形中点let centerPoint = [this.toFixed6(offsetLeft + 0.5 * width, WIDTH),this.toFixed6(offsetTop + 0.5 * height, HEIGHT)]let hotData = {data,centerPoint}console.log('getHotData -> hotData', hotData)console.log(JSON.stringify(hotData));}},toFixed6(v1, v2) {return (v1 / v2).toFixed(6)},move(type, location, tarobj) {switch (type) {case ’n’: {let add_length = this.clickY - location.y;this.clickY = location.y;let length = parseInt(tarobj.style.height) + add_length;tarobj.style.height = length + 'px';let realTop = this.clickY > 0 ? this.clickY : 0let maxTop = this.backImgRect.height - parseInt(tarobj.style.height)realTop = realTop >= maxTop ? maxTop : realToptarobj.style.top = realTop + 'px';break;}case ’s’: {let add_length = this.clickY - location.y;this.clickY = location.y;let length = parseInt(tarobj.style.height) - add_length;let maxHeight = this.backImgRect.height - parseInt(tarobj.style.top)let realHeight = length > maxHeight ? maxHeight : lengthtarobj.style.height = realHeight + 'px';break;}case ’w’: {var add_length = this.clickX - location.x;this.clickX = location.x;let length = parseInt(tarobj.style.width) + add_length;tarobj.style.width = length + 'px';let realLeft = this.clickX > 0 ? this.clickX : 0let maxLeft = this.backImgRect.width - parseInt(tarobj.style.width)realLeft = realLeft >= maxLeft ? maxLeft : realLefttarobj.style.left = realLeft + 'px';break;}case ’e’: {let add_length = this.clickX - location.x;this.clickX = location.x;let length = parseInt(tarobj.style.width) - add_length;let maxWidth = this.backImgRect.width - parseInt(tarobj.style.left)let realWidth = length > maxWidth ? maxWidth : lengthtarobj.style.width = realWidth + 'px';break;}}},onUpleftbtn(e) {e.stopPropagation();this.onDragDown(e, 'nw');},onUpRightbtn(e) {e.stopPropagation();this.onDragDown(e, 'ne');},onDownleftbtn(e) {e.stopPropagation();this.onDragDown(e, 'sw');},onDownRightbtn(e) {e.stopPropagation();this.onDragDown(e, 'se');},onDragDown(e, type) {e = e || window.event;this.clickX = e.x || e.offsetX;this.clickY = e.y || e.offsetY;this.obj = window;this.obj.operateType = type;this.box = this.$refs.box;return false;}},}</script><style lang='less' scoped>.rectangle-marker {width: 100%;height: 100%;display: flex;flex-direction: column;align-items: center;.mark-wrap {position: relative;.img-responsive {display: inline-block;max-width: 100%;max-height: 100%;}.draw-rect {position: absolute;top: 0;left: 0;bottom: 0;right: 0;width: 100%;height: 100%;z-index: 99;user-select: none;&.no-event {pointer-events: none;}}}.act-box {margin-top: 10px;display: flex;}.act-btns {position: absolute;right: 0;top: 0;z-index: 199;padding: 0 10px;height: 40px;width: 100px;display: flex;align-items: center;justify-content: center;}.fade-enter-active {animation: hide-and-show .5s;}.fade-leave-active {animation: hide-and-show .5s reverse;}@keyframes hide-and-show {0% {opacity: 0;}100% {opacity: 1;}}}</style><style lang='less'>.rectangle-marker {.box {position: absolute;width: 0px;height: 0px;opacity: 0.5;z-index: 149;cursor: move;border: 1px solid #f00;.upleftbtn,.uprightbtn,.downleftbtn,.downrightbtn {width: 10px;height: 10px;border: 1px solid steelblue;position: absolute;z-index: 5;background: whitesmoke;border-radius: 10px;}.upleftbtn {top: -5px;left: -5px;cursor: nw-resize;}.uprightbtn {top: -5px;right: -5px;cursor: ne-resize;}.downleftbtn {left: -5px;bottom: -5px;cursor: sw-resize;}.downrightbtn {right: -5px;bottom: -5px;cursor: se-resize;}}}</style> 背景图传入,图片自适应处理。 定义drag标记为,添加开始标记、重置按钮。 创建box区域,不同状态(change、moving、active),对应不同id。 box可移动距离,计算边界。 四角放大缩小的功能。 生成结果,精确到6位小数,这样可以使得复原标记区域的时候误差最小。
四、觉得有帮助的,麻烦给个赞哦,谢谢!
以上就是vue实现一个矩形标记区域(rectangle marker)的方法的详细内容,更多关于vue实现矩形标记区域的资料请关注好吧啦网其它相关文章!
相关文章: