vue+svg.js 实现图像标记
vue+svg.js 实现图像标记vue+svg.js 实现图像标记安装svg.js及相关插件在vue项目中引入创建画布画图/做标记清空画布清空resize删除某一子节点全局快捷键的监听vue+svg.js 实现图像标记本文讲的是如何使用svg.js实现图像的标记。在本文中,将使用前端框架vue及element UI搭配实现功能。本文不再赘述如何搭建vue+element ui框架,框架在本文..
·
vue+svg.js 实现图像标记
本文讲的是如何使用svg.js实现图像的标记。在本文中,将使用前端框架vue及element UI搭配实现功能。本文不再赘述如何搭建vue+element ui框架,框架在本文只是辅助作用,用户可以在其他框架中实现等同功能。本文中标记的实现需要依赖多个svg的插件,请至svg.js官网自行了解。
功能需求:
- svg画图,实现矩形、多边形画图;
- 图形可以resize,draggable;
- 画布可以zoom in & zoom out;
- 画图结束时弹出输入框,用户输入数据;
- 删除图形标记,删除画布;
- 监听键盘事件,实现快捷操作。
页面预览:
安装svg.js及相关插件
在项目中安装本项目中需要用的依赖,详细请参照官方文档。
以node为例:
svg.js npm install svg.js
svg.draw.js npm install svg.draw.js
svg.select.js npm install svg.select.js
svg.resize.js npm install svg.resize.js
在vue项目中引入
完成以下两步就可以在项目中以this.$svg使用SVG啦
- 在项目中新建文件svg.js,输入以下代码;
import svgJS from 'svg.js'
import 'svg.draw.js'
import 'svg.panzoom.js'
import 'svg.draggable.js'
import 'svg.draggy.js'
import 'svg.select.js'
import 'svg.resize.js'
export default {
install: Vue => {
Vue.prototype.$svg = svgJS
}
}
- 在main.js中引入;
import svgJs from './utils/svg'
Vue.use(svgJs)
创建画布
在需要标记的画面中创建画布。
<div id="myDrawing" class="draw" />
// create canvas
createCanvas(currentImg) {
const _this = this
const width = document.getElementById('myDrawing').clientWidth
const persent = width / currentImg.width
document.getElementsByClassName('label-area')[0].style.height = currentImg.height * persent + 'px'
document.getElementsByClassName('label-area')[0].style.maxHeight = '85%'
this.oDom = this.$svg('myDrawing').size('100%', '100%')
this.mainImage = this.oDom.image(currentImg.url)
this.mainImage.loaded(function(loader) {
_this.oDom.size('100%', '100%')
_this.opt.constraint.minX = 0
_this.opt.constraint.minY = 0
_this.opt.constraint.maxX = loader.width
_this.opt.constraint.maxY = loader.height
_this.oG = _this.oDom.group().add(this)
_this.oDom.zoom(persent, { x: 0, y: 0 })
const pzoom = _this.oDom.panZoom({ zoomMin: 0.2, zoomMax: 5 })
_this.oDom.on('panStart', function(e) {
console.log(e)
if (_this.zoomIn) {
console.log(pzoom.zoom())
pzoom.zoom() + 0.5 < 5 ? pzoom.zoom(pzoom.zoom() + 0.5, new SVG.Point(e.detail.event.layerX/2, e.detail.event.layerY/2)) : ''
} else if (_this.zoomOut) {
pzoom.zoom() - 0.5 > 0.2 ? pzoom.zoom(pzoom.zoom() - 0.5, new SVG.Point(e.detail.event.layerX/2, e.detail.event.layerY/2)) : ''
}
})
_this.oDom.on('panEnd', function(e) {
})
_this.oDom.on('zoom', function(lvl, focus) {
console.log(pzoom.zoom())
})
if (_this.alldata.length !== 0 && _this.alldata !== null) {
_this.showGraphic(_this.alldata)
}
_this.loading = false
})
this.initTagNameList()
this.keyDownListener()
},
画图/做标记
html
<div id="shape" class="label-shape">
<el-tooltip v-if="hasSVG" class="item" effect="dark" content="矩形(R)" placement="top">
<svg width="45" height="25" style="margin:10px 0;float:left;" version="1.1" xmlns="http://www.w3.org/2000/svg">
<rect x="0" y="0" width="45" height="25" style="fill:#e7f5ff;stroke:#96cffb;stroke-width:2px;cursor:pointer;" @click="changeType(1)" />
</svg>
</el-tooltip>
<el-tooltip v-if="hasSVG && currentNode.data.labelType === '1'" class="item" effect="dark" content="多边形" placement="top">
<svg width="50" height="25" style="margin:10px 15px;float:left;" version="1.1" xmlns="http://www.w3.org/2000/svg">
<polygon points="0,10,25,0,50,10,40,25,10,25" style="fill:#e7f5ff;stroke:#96cffb;stroke-width:1px;cursor:pointer;" @click="changeType(3)" />
</svg>
</el-tooltip>
</div>
画图
// 更改图形
changeType(val) {
this.type = val
// this.hasNode = true
if (!this.hasNode) {
this.hasNode = true
switch (val) {
case 1:
this.drawRect()
break
case 2:
this.drawParallelogram()
break
case 3:
this.drawPoligon()
break
default:
break
}
} else {
this.$message({
showClose: true,
message: '当前节点已存在,请在标注区域进行标注。',
type: 'warning'
})
}
},
// 画矩形
drawRect() {
this.drawing = true
const _this = this
this.disableAllDraggy()
// const rect = this.oG.rect().draw().attr({
// fill: '#9ccbff30',
// stroke: '#409EFF',
// 'stroke-width': '1'
// })
_this.oDom.panZoom(false)
_this.clearResize()
if (this.$store.state.settings.clickLabel) {
const rect = this.oG.rect().draw().attr({
fill: '#9ccbff30',
stroke: '#409EFF',
'stroke-width': '1'
})
rect.on('drawstop', function() {
// remove listener
_this.drawing = false
_this.oDom.panZoom({ zoomMin: 0.2, zoomMax: 5 })
})
_this.commonEvent(1, rect)
} else {
const drawing = this.oG
const rect = drawing.rect().attr({
fill: '#9ccbff30',
stroke: '#409EFF',
'stroke-width': '1'
})
drawing.on('mousedown', function(e) {
// e.stopPropagation()
console.log('监听到鼠标点下事件')
console.log(_this.drawing)
if (_this.drawing) {
rect.draw(e)
}
}, false)
drawing.on('mouseup', function(e) {
rect.draw('stop', e)
console.log('监听到鼠标抬起事件')
// _this.drawing = false
// _this.oDom.panZoom({ zoomMin: 0.2, zoomMax: 5 })
}, false)
rect.on('drawstop', function() {
// remove listener
_this.drawing = false
_this.oDom.panZoom({ zoomMin: 0.2, zoomMax: 5 })
})
_this.commonEvent(1, rect)
}
},
// 画平行四边形
drawParallelogram() {
const _this = this
const rect = this.oG.rect().draw().attr({
fill: '#9ccbff30',
stroke: '#409EFF',
'stroke-width': '1'
})
// rect.transform({skewX: -45})
// rect.rotate(45);
// rect.rect().draw().attr({fill: 'red'}).transform({skewX: -45})
// .scale(4)
// .rotation(45);
_this.commonEvent(1, rect)
},
// 画多边形
drawPoligon() {
const polygon = this.oG.polygon().draw().attr({
fill: '#9ccbff30',
stroke: '#409EFF',
'stroke-width': '1'
})
this.commonEvent(3, polygon)
},
// 画已存在数据
showGraphic(data) {
const _this = this
for (const i in data) {
const polygon = this.oG.polygon(data[i].points).fill('#9ccbff30').attr({
stroke: '#409EFF',
'stroke-width': '1',
id: data[i].id
})
_this.$svg.get(data[i].id).draggy(_this.opt.constraint)
_this.commonEvent(2, polygon)
_this.addResize(polygon)
}
_this.clearResize()
},
公共事件
// common type = 1 :rect 2 : origin poligon 3: poligon
commonEvent(type, polygon) {
const _this = this
// polygon.draggy(_this.opt.constraint)
polygon.click(function(e) {
// e.stopPropagation()
_this.currentChild = polygon
_this.clearResize()
if (!this.drawing) {
if (document.getElementById(polygon.node.id + 'g')) {
document.getElementById(polygon.node.id + 'g').style.display = 'block'
}
}
// if (document.getElementById(polygon.node.id + 'g')) {
// document.getElementById(polygon.node.id + 'g').style.display = 'block'
// }
// polygon.selectize().resize(_this.opt)
// document.getElementById(_this.oG.node.id).lastChild.id = polygon.node.id + 'g'
// document.getElementById(polygon.node.id + 'g').style.display = 'block'
_this.selectAreaAndTag(polygon.node.id, _this.alldata, _this.tagNameList)
// 去除hightLight
// for (let i in this.alldata) {
// if (this.alldata[i].id === item.id) {
// this.$svg.get(this.alldata[i].id).fill('#9ccbff30')
// } else {
// this.$svg.get(this.alldata[i].id).fill('#9ccbff30')
// }
// }
})
polygon.on('dblclick', function(e) {
_this.currentChild = polygon
_this.getNameInfo(_this.currentChild.node.id)
_this.dialogVisble = true
})
polygon.on('mouseover', function(e) {
// 去除其他节点hightLight
for (const i in _this.alldata) {
_this.$svg.get(_this.alldata[i].id).fill('#9ccbff30')
}
polygon.attr({
fill: '#2d94ff7d',
cursor: 'pointer'
})
})
polygon.on('mouseout', function(e) {
polygon.attr({
fill: '#9ccbff30'
})
})
polygon.on('drawstart', function(event) {
_this.currentChild = polygon
if (type === 3) {
document.addEventListener('contextmenu', function(e) {
e.preventDefault()
polygon.draw('done')
polygon.off('drawstart')
return false
})
}
})
polygon.on('drawstop', function(event) {
_this.changeData(type, polygon)
_this.dialogVisble = true
_this.hasNode = false
_this.allDragable()
})
polygon.on('resizedone', function(e) {
_this.changeData(type, polygon)
_this.$forceUpdate()
})
polygon.on('dragstart', function(e) {
_this.oDom.panZoom(false)
e.stopPropagation()
})
polygon.on('dragend', function(e) {
_this.oDom.panZoom({ zoomMin: 0.2, zoomMax: 5 })
e.stopPropagation()
_this.changeData(type, polygon)
_this.$forceUpdate()
})
},
清空画布
// 清空画布
clearSVG() {
if (this.hasSVG) {
// document.querySelector('svg').innerHTML = ''
var self = document.getElementById('myDrawing')
var parent = self.parentElement
parent.removeChild(self)
var para = document.createElement('div')
para.id = 'myDrawing'
para.setAttribute('class', 'draw')
para.setAttribute('style', 'width:100%;height:100%')
var labelArea = document.getElementById('labelArea')
labelArea.appendChild(para)
this.oG = this.oDom.group()
this.alldata = []
this.tagNameList = []
this.hasSVG = false
}
},
清空resize
// 清空画布
clearResize() {
var id2 = document.getElementById(this.oG.node.id)
var id2Div = id2.getElementsByTagName('g')
var length = id2Div.length
for (var i = 0; i < length; i++) {
id2Div[i].style.display = 'none'
}
},
删除某一子节点
// 清空画布
deleteChild() {
const _this = this
const child = document.getElementById(this.currentChild.node.id)
document.getElementById(_this.oG.node.id).removeChild(child)
_this.clearResize()
for (const i in _this.alldata) {
if (_this.alldata[i].id === this.currentChild.node.id) {
_this.alldata.splice(i, 1)
}
}
_this.dialogVisble = false
},
全局快捷键的监听
// 全局快捷键的监听
keyDownListener(str, rect, poligon) {
var _this = this
// 0:down 1:up
document.onkeydown = function(e) {
const key = window.event.keyCode
console.log(key)
_this.keyDownEvent(key, e, 0)
}
document.onkeyup = function(e) {
const key = window.event.keyCode
_this.keyDownEvent(key, e, 1)
}
},
// 键盘快捷键事件 82:R 80:P 90:Z 88:X
keyDownEvent(key, e, val) {
if (!this.dialogVisble && val === 0 && this.hasSVG) {
switch (key) {
case 82:
this.changeType(1)
break
case 80:
if (this.currentNode.data.labelType === 1) this.changeType(3)
break
case 90:
this.zoomIn = true
break
case 88:
this.zoomOut = true
break
default:
break
}
} else if (!this.dialogVisble && val === 1 && this.hasSVG) {
switch (key) {
case 90:
this.zoomIn = false
break
case 88:
this.zoomOut = false
break
default:
break
}
}
}
更多推荐
已为社区贡献1条内容
所有评论(0)