一 :圖片標注功能的實現簡介
1:需要標注的圖片放在底層
2:svg層正好覆蓋需要標注的圖片
3:由svg來畫出相應的軌跡
注釋: svg的坐標系:
1:以左上角為坐標系的原點(0,0)
2:X 軸的正方向向右,從 0,0 點開始向右, x 逐漸增大。Y 軸的正方向向下,從 0,0 點開始向下, y 逐 漸增大
3:坐標以像素為單位
二:標注的效果圖
三: 核心代碼
HTML:

1 <div class="Labeling-canvas"> 2 <div class="image"> 3 <img src="@/assets/404.png" ref="imgLabel" id="labelValue"/><!-- 需要標注的圖片 --> 4 </div> 5 <svg class="svglabel" ref="svglabel" @mousedown="mouseDown($event)" @mousemove="mouseMove($event)" @mouseup="onmouseup($event)" :style="{width: 100 + '%', height: 100 + '%' }"> 6 <g v-for="(rectData, index) in svgLabelShow" :key="index"> 7 <rect v-if="rectData" :x="getXPostion(rectData,27,1)" :y="rectData.ulY" :width="27" :height="26" 8 style="fill: #00FFFF; stroke-opacity: 1; stroke: #00FFFF; stroke-dasharray: none; stroke-width: 1;"> 9 </rect> 10 <text v-if="rectData" :x='getXPostion(rectData,18,8)' :y='parseFloat(rectData.ulY)+16'font-size='16'> 11 {{index+1}} 12 </text> 13 <rect v-if="rectData" :x="getXPostion(rectData,27,1)" :y="parseFloat(rectData.ulY)+26" :width="27" :height="24" 14 style="fill: #FFFFFF; stroke-opacity: 1; stroke: #00FFFF; stroke-dasharray: none; stroke-width: 1;"></rect> 15 <image v-if="rectData" @click="deleteLabel(svgLabelShow,index)" :x='getXPostion(rectData,19,7)' :y='parseFloat(rectData.ulY)+29' width='13' 16 height='14' xlink:href='@/assets/delete.svg'></image> 17 <rect v-if="rectData" :x="rectData.ulX" :y="rectData.ulY" :width="rectData.width" :height="rectData.height" 18 style="fill: none; stroke-opacity: 1; stroke: #00FFFF; stroke-dasharray: none; stroke-width: 1;"></rect> 19 </g> 20 </svg> 21 </div>
VUE:

1 data () { 2 return { 3 isclick: true, 4 drawColor: '#00FFFF', 5 drawElement: null, //當前移動的對象 6 svgLabelShow: [],//顯示數據,需按比例轉換 7 } 8 }, 9 methods: { 10 //mouseDown,mouseMove,onmouseup 新建標注核心代碼 11 mouseDown (e) { 12 if (!this.isclick) {//防止點擊過快 13 return false; 14 } else { 15 this.clickQuick(); 16 } 17 //e.button==0 沒有按鍵或者是沒有初始化 18 if (e.button == 0) {//當前為畫圖操作 19 const currentX = e.offsetX;//offsetX,offsetY相對於帶有定位的父盒子的x,y坐標 20 const currentY = e.offsetY; 21 this.drawElement = document.createElementNS('http://www.w3.org/2000/svg', 'rect'); 22 this.drawElement.setAttribute('x', currentX); 23 this.drawElement.setAttribute('y', currentY); 24 this.drawElement.setAttribute('width', 0); 25 this.drawElement.setAttribute('height', 0); 26 this.drawElement.dataset.dataType = 'markRect'; 27 this.$refs.svglabel.appendChild(this.drawElement); 28 this.drawElementStyle(this.drawElement); 29 } 30 }, 31 mouseMove (e) { 32 if (this.drawElement == null || this.corpusActionType != 'label') { 33 return false; 34 } 35 if (e.button == 0) {//當前為畫圖操作 36 const currentX = e.offsetX; 37 const currentY = e.offsetY; 38 const startX = this.drawElement.getAttribute('x'); 39 const startY = this.drawElement.getAttribute('y'); 40 const rectX =startX; 41 const rectY =startY; 42 const rectWith = Math.abs(currentX - startX); 43 const rectHeight = Math.abs(currentY - startY); 44 this.drawElement.setAttribute('x', rectX); 45 this.drawElement.setAttribute('y', rectY); 46 this.drawElement.setAttribute('width', rectWith); 47 this.drawElement.setAttribute('height', rectHeight); 48 } 49 }, 50 onmouseup (e) { 51 if (this.drawElement == null) { 52 return; 53 } 54 if (e.button == 0) { 55 const rectX = this.drawElement.getAttribute('x'); 56 const rectY = this.drawElement.getAttribute('y'); 57 const rectWidth = this.drawElement.getAttribute('width'); 58 const rectHeight = this.drawElement.getAttribute('height'); 59 if (rectWidth == 0 || rectHeight == 0 || !rectX || !rectY) {//不符合條件的矩形取消 60 this.drawElement.parentNode.removeChild(this.drawElement); 61 this.drawElement = null; 62 } else { 63 this.svgLabelShow.push({ulX: rectX, ulY: rectY, width: rectWidth, height: rectHeight, labelName: 'table'}); 64 console.log('this.svgLabelShow', this.svgLabelShow); 65 this.drawElementStyle(e.target); 66 this.drawElement.parentNode.removeChild(this.drawElement); 67 this.drawElement = null; 68 } 69 } 70 }, 71 //設置標注框的樣式 0:正常狀態 1: 高亮狀態 2: 可編輯狀態 72 drawElementStyle (drawElement) { 73 drawElement.style.fill = 'none'; 74 drawElement.style.strokeOpacity = 1; 75 drawElement.style.stroke = this.drawColor; 76 drawElement.style.strokeDasharray = 'none'; 77 drawElement.style.strokeWidth = 1; 78 }, 79 clickQuick () { 80 if (this.isclick) { 81 this.isclick = false; 82 setTimeout(() => { 83 this.isclick = true; 84 }, 500); 85 } 86 }, 87 getXPostion (labelData, xPostion, left) { 88 if (parseFloat(labelData.ulX) < 27) { 89 return parseFloat(labelData.ulX) + parseFloat(labelData.width) + left; 90 } else { 91 return parseFloat(labelData.ulX) - xPostion; 92 } 93 }, 94 }