vue js 图像标注 --- canvas 实现
圖像標注
- 需求
- 打點式實現(xiàn)思路
- 矩形框實現(xiàn)思路
- 右鍵彈出刪除和添加備注菜單
- 刪除選中內容
- 更新備注
項目中碰到一個需要對圖像標注的功能,查了好幾個插件,但是感覺用起來有點頗為復雜而且和自己需要的功能不完全一致,于是就自己用canvas寫了一個簡單的:
需求
- 兩種標注方式,一種是打點式標注,一種是矩形框標注
- 打點式:單擊圖像打點,把每一個點用線連接起來,最后雙擊結束,形成一個閉環(huán),并把所有坐標點記錄下來傳給后端
- 矩形框:和其他標注工具一樣,鼠標隨意拖動,形成一個矩形框,并把四個角的坐標點記錄下來傳給后端
- 在繪制的內容上右鍵高亮選中該內容并彈出菜單,可以刪除,可以更新備注
- 使用打點式標注,鼠標變畫筆;使用矩形框標注,鼠標變十字
打點式實現(xiàn)思路
- 創(chuàng)建canvas 并繪制和圖片一樣大的背景
- 使用click事件獲取點擊的坐標,用fillRect方法繪制一個直徑4像素的小矩形,作為點(也可以做圓形)
- 把每次點擊的坐標存儲起來,點下一個坐標時,使用moveTo,lineTo方法連線
- 雙擊時把最后一個坐標和第一個連起來
注意: 雙擊事件會觸發(fā)兩次單擊事件,需要處理一下,網(wǎng)上找的方法:
click(){clearTimeout(timer)timer = setTimeout(()=>{// xxx},200)}dbclick(){clearTimeout(timer)// xxxx}打點式標注部分代碼
this.ctx.drawImage(this.image,0,0,this.w,this.h)// 繪制矩形小點 this.ctx.fillStyle="#FF0000"; this.ctx.fillRect(e.offsetX - 2, e.offsetY - 2, 4, 4);//連線 tempPointArr是儲存坐標點的數(shù)組,雙擊形成閉環(huán)后清空 this.ctx.beginPath() this.ctx.strokeStyle= '#FF0000' this.ctx.moveTo(this.tempPointArr[this.tempPointArr.length - 2].x,this.tempPointArr[this.tempPointArr.length - 2].y); this.ctx.lineTo(this.tempPointArr[this.tempPointArr.length - 1].x,this.tempPointArr[this.tempPointArr.length - 1].y); this.ctx.stroke();矩形框實現(xiàn)思路
- 使用mousedown mousemove mouseup 來完成
- 在mousedown事件獲取開始的坐標
- mousemove事件使用strokeRect畫矩形,但要注意先清空,再畫新的,否則會導致有很多矩形框
- mouseup事件清空開始坐標,完成一個矩形框的繪制
鼠標按下時記錄當前canvas 內容,在每一次鼠標拖動時先還原之前干凈的canvas內容,再畫新的矩形,達到先清空再畫新矩形的目的
重點代碼:
mousedown(){this.imgData = this.ctx.getImageData(0,0,this.w,this.h) } mousemove(){this.ctx.putImageData(this.imgData,0,0) }矩形框標注部分代碼
mousedown(){this.startPoint = {x:e.offsetX,y:e.offsetY}this.imgData = this.ctx.getImageData(0,0,this.w,this.h) }mousemove(){this.ctx.putImageData(this.imgData,0,0)this.ctx.beginPath()this.ctx.fillStyle = "rgba(255,0,0,0.1)";this.ctx.strokeStyle = '#FF0000';this.ctx.strokeRect(this.startPoint.x,this.startPoint.y,e.offsetX - this.startPoint.x,e.offsetY - this.startPoint.y); }mouseup(){this.startPoint = null }右鍵彈出刪除和添加備注菜單
在有內容的地方才彈出,沒有內容的地方不彈
主要使用坐標點去判斷在不在某一個范圍內,之前把每一個繪制的內容都存儲成數(shù)組
思路:右鍵的坐標點必須滿足大于某個x點,小于另一個x點,大于某個y點,小于另一個y點,則表示在這個內容區(qū)域內
部分代碼
const index = this.pointData.findIndex((item)=>{let leftX = falselet rightX = falselet topY = falselet bottomY = falseitem.point.forEach(p=>{if(e.offsetX > p.x){leftX = true}if(e.offsetX < p.x){rightX = true}if(e.offsetY < p.y){bottomY = true}if(e.offsetY > p.y){topY = true}})return leftX && rightX && topY && bottomY}) if(index > -1){/**xxxxxx*/this.fillRect(this.pointData[index].point) //給這個區(qū)域填充背景色,表示高亮 }刪除選中內容
思路:使用橡皮擦模擬實現(xiàn)刪除效果,canvas下面要有多一張背景圖,否則的話會直接擦成白色;
使用完后再使用其他功能切記要把globalCompositeOperation屬性從destination-out改回默認值 source-over
部分代碼
this.ctx.globalCompositeOperation = 'destination-out' //重點 /** ...重新繪制一次要刪除的內容 */ this.ctx.globalCompositeOperation = 'source-over'更新備注
使用fillText方法添加文本
在添加新的文本前先清除掉之前寫的文本,主要使用clearRect方法
部分代碼
let textW = this.ctx.measureText(this.textObj[this.selectedIndex]).width this.ctx.clearRect(this.pointData[this.selectedIndex].point[0].x + 5,this.pointData[this.selectedIndex].point[0].y - 8 - 12 ,textW,13) this.ctx.font="12px Arial" this.ctx.fillStyle ='#FF0000' this.ctx.fillText(this.note,this.pointData[this.selectedIndex].point[0].x + 5,this.pointData[this.selectedIndex].point[0].y - 8)大致思路到此為止
多次使用geiImageData putImageData 來實現(xiàn)保存某一步的內容,再顯示;
也可以利用這個實現(xiàn)撤銷的功能
如果需要完整的代碼可以去git 上下載一下,也希望有大佬可以幫忙優(yōu)化一下代碼,感謝~~~
最后附上地址:
https://gitee.com/cuijinrong/project-gather/blob/master/src/components/marker.vue
總結
以上是生活随笔為你收集整理的vue js 图像标注 --- canvas 实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux原子锁原理,了解Linux的锁
- 下一篇: 如何看电影学英语