帮算法做做数据标注的工作,想想能不能用前端实现,在成品中集成数据标注这个功能
目标:每个点的坐标和图形类型
1、布局
<div class="img-box"
@mousedown.stop="mousedownImg"
@mousemove.stop="mouseMoveImg"
@mouseout.stop="mouseOutImg"
:style="{'cursor': moveFlag ? 'pointer' : 'default'}">
<img src="../assets/img/1.jpeg" ref="canvasImg" width="100%" id="targetImg" >
<canvas id="canvas" class="canvas-box" ></canvas>
<div class="sml-box" v-show="moveFlag" ref="smlBox"
:style="{'left': smlBoxLf + 'px', 'top': smlBoxTop + 'px'}">
<img src="../assets/img/1.jpeg" class="sml-img" ref="smlImg"
:style="{'left': smlImgLf + 'px', 'top': smlImgTop + 'px'}">
<span class="green-line01"></span>
<span class="green-line02"></span>
</div>
</div>
图片设置了宽度100%,高度自适应
canvas的宽高根据图片的宽高来
但是canvas有默认宽高,300x150,可利用js、css设置,但是css设置,画出的图形会有锯齿会虚,所以用js设置
2、鼠标操作
计算图片在浏览器可视区的位置,posiX、posiY
鼠标点击事件 mousedownImg (e){}:
计算点击位置距离图片左上角的距离:
接下来的逻辑:
判断是否是第一次画图
第一次画:添加点,画圈
不是第一次画:判断最后一个图形是否闭合
闭合:重新开始添加图形,添加点,画圈
不闭合:判断添加的点是否和这个图形上的第一个点重合
不重合:继续添加点画圈、线
重合:画线
if (!this.downFlag) {
// 第一次画图
let lineObj = {
points: [],
type: '',
isClose: false
}
this.lineList.push(lineObj)
this.lineList[0].points.push({
x: this.pointX,
y: this.pointY
})
this.drawArc(this.lineList[0].points[0].x, this.lineList[0].points[0].y)
} else {
// 判断最后一个图形是否闭合
if (this.lineList[this.lineList.length - 1].isClose) {
// 重新开始添加图形点
let lineObj = {
points: [],
type: '',
isClose: false
}
this.lineList.push(lineObj)
let len = this.lineList.length
this.lineList[len - 1].points.push({
x: this.pointX,
y: this.pointY
})
this.drawArc(this.lineList[len - 1].points[0].x, this.lineList[len - 1].points[0].y)
} else {
// 最后一条线上继续添加点
// 判断是否和起始点重合
if (this.equalStartPoint(this.pointX, this.pointY)) {
// 重合
let num = this.lineList.length
this.lineList[num - 1].points.push({
x: this.lineList[num - 1].points[0].x,
y: this.lineList[num - 1].points[0].y
})
let lineObj = this.lineList[num - 1]
let n = this.lineList[num - 1].points.length
this.drawLine(lineObj.points[n - 2].x, lineObj.points[n - 2].y, lineObj.points[n - 1].x, lineObj.points[n - 1].y)
this.dialogVisible = true this.nowLine = num - 1
} else {
// 不重合
let num = this.lineList.length
this.lineList[num - 1].points.push({
x: this.pointX,
y: this.pointY
})
let n = this.lineList[num - 1].points.length
let lineObj = this.lineList[num - 1]
this.drawLine(lineObj.points[n - 2].x, lineObj.points[n - 2].y, lineObj.points[n - 1].x, lineObj.points[n - 1].y)
this.drawArc(lineObj.points[n - 1].x, lineObj.points[n - 1].y)
}
}
}
watch监听lineList
lineList: {
handler (newVal, oldVal) {
if (newVal.length > 0) {
this.downFlag = true
} else {
this.downFlag = false
}
},
deep: true
}
画圈:
drawArc (x, y) {
this.context.strokeStyle = 'blue'
this.context.beginPath()
this.context.arc(x, y, this.roundrr, 360, Math.PI * 2, true)
this.context.closePath()
this.context.stroke()
}
画线:
drawLine (startX, startY, endX, endY) {
this.context.strokeStyle = 'blue'
this.context.lineWidth = this.lineWid
this.context.moveTo(startX, startY)
this.context.lineTo(endX, endY)
this.context.stroke()
}
判断两点重合:
equalStartPoint (x, y) {
let point = this.lineList[this.lineList.length - 1].points
if (Math.abs((x - point[0].x) * (x - point[0].x)) + Math.abs((y - point[0].y) * (y - point[0].y)) <= this.roundrr * this.roundrr) {
this.lineList[this.lineList.length - 1].isClose = true
return true
} else {
this.lineList[this.lineList.length - 1].isClose = false
return false
}
}
鼠标移动事件mouseMoveImg (e) {} :
鼠标在图片界限内移动,光标附近都会出现一个放大镜模式的功能(灵感来自于微信截图),便于点击画图。
考虑的问题:
1、边界处理
2、为了使图形闭合,最后一点与起始点重合难度问题,解决办法是当鼠标移动到起始点上,会出现一个高光,在此点上点击就能重合闭合
mouseMoveImg (e) {
this.moveFlag = true
let pointX = e.clientX - this.posiX
let pointY = e.clientY - this.posiY
// 大图的left,top
this.smlImgLf = -(this.smlImgScl * pointX) + 50
this.smlImgTop = -(this.smlImgScl * pointY) + 50
// 漂浮窗的left,top
if (pointX > this.canvasWid - 120) {
this.smlBoxLf = e.clientX - this.posiX - 120
} else {
this.smlBoxLf = e.clientX - this.posiX + 20
}
if (pointY > this.canvashig - 120) {
this.smlBoxTop = e.clientY - this.posiY - 120
} else {
this.smlBoxTop = e.clientY - this.posiY + 20
}
if (this.downFlag) {
let pointXSml = e.clientX - this.posiX
let pointYSml = e.clientY - this.posiY
let point = this.lineList[this.lineList.length - 1].points
if (Math.abs((pointXSml - point[0].x) * (pointXSml - point[0].x)) + Math.abs((pointYSml - point[0].y) * (pointYSml - point[0].y)) <= this.roundrr * this.roundrr && !this.lineList[this.lineList.length - 1].isClose) { if (!this.whiteFlage) {
this.drawArcWhite(point[0].x, point[0].y)
}
}else {
this.clearrect()
}
}
}
高光白点:
drawArcWhite (x, y) {
this.context.fillStyle = '#FFF'
this.context.beginPath()
this.context.arc(x, y, this.whiteRound, 360, Math.PI * 2, true)
this.context.closePath()
this.context.fill()
this.whiteFlage = true
}
根据数据lineList重新绘制图形:
clearrect () {
this.context.clearRect(0, 0, this.canvasWid, this.canvashig)
this.drawOneLine(this.lineList)
this.whiteFlage = false
}
因为lineList中可能存在一个完整封闭的图形,所以有:
drawOneLine (lineData) {
let len = lineData.length
for (let i = 0; i < len; i++) {
let num = lineData[i].points.length
lineData[i].points.forEach((item, index) => {
if (index === 0) {
this.drawArc(item.x, item.y)
} else if (index === num) {
this.drawLine(lineData[i].points[index - 1].x, lineData[i].points[index - 1].y, item.x, item.y)
} else {
this.drawLine(lineData[i].points[index - 1].x, lineData[i].points[index - 1].y, item.x, item.y)
this.drawArc(item.x, item.y)
}
})
}
}
鼠标移出事件:
mouseOutImg () {
this.moveFlag = false
}
增加了一个撤回操作(删除上一步的点):
resove () {
let len = this.lineList.length
if (this.lineList[len - 1].isClose) {
this.lineList[len - 1].isClose = false
}
if (this.lineList[len - 1].points.length === 1) {
this.lineList.pop()
} else {
this.lineList[len - 1].points.pop()
}
this.clearrect()
}
当起始点与结束点重合后,会有弹出框弹出对图形类型进行编辑:
绘制图形的同时,会展示图形相关信息,例如编号、类型,也可以对图形进行相关操作,例如删除,编辑类型。
删除操作:
标注图形后,就可以将lineList中的数据拿来用了!
此工具是根据labelme数据标注工具来模拟的,实现大部分功能,能在网页上进行图片标注,目前还有一个功能没有实现,就是拖动点,就在我编辑这篇文章的过程中,想到了一些方法,应该可以实现,继续完善功能吧!
地图事件后,已经好久不看rm了,不遗憾!
努力工作,努力生活,开心每一天呀!附上一张傻狗,哈哈哈