本人是记录了一次功能开发,先上一下效果图
首先博主是一个Java后端的臭弟弟,前端用的不是特别好,因此此标记功能是改造了组件VMarker
(https://vmarker.sagocloud.com/diy/)实现的一些自定义的功能,首先从后台获取要标记的图片,每次仅能标记一张是基本要求。在标记过程中可以点击上一张、下一张切换,切换会保留之前已经标记过的图片(存储在data中不支持页面刷新,可以根据需求存储会话或者本地)。
清空按钮可以清空当前图片已经标记过的;
隐藏则是能切换显示矩形框,隐藏之后可以查看原图片以便观察。
完成标记完成后,点击完成之后可以将保存的标记数据保存至后端。
1.安装VMarker
npm install vue-picture-bd-marker
2.页面制作
标注图片 ({{testForm.sign[this.pageData.currentPage].name}})
{{item.name}}
取 消
确 定
3.data数据
data () {
return {
testForm: { # 需要标记的图片的集合
sign: []
},
currentInfo: { # 当前图片的信息,包含图片原本的高矮胖瘦尺寸
currentBaseImage: '',
rawW: 0,
rawH: 0,
currentW: 0,
currentH: 0,
checked: false, # false表示当前图片还没有标记过
data: [] # 表示图片矩形标记信息
},
pageData: { # 辅助信息,记录图片的编号,判断是不是第一张或者最后一张等
currentPage: 0
},
readOnly: false,
label: [], # 右侧标签集合
selected: { # 当前选中的标签的信息
name: '',
value: '',
index: 0
},
allInfo: [], # 存储过程中的图片的矩形标记信息
imageInfo: [], # 存储图片原始信息
marker: null,
dialogVisible: false, # dialog显示与否
newTab: { # 新增标签信息
name: '',
value: ''
},
isHidden: false, # 切换隐藏功能
searchDataDetail: { # 获取图片信息的参数表单
id: '',
isAdmin: '',
page_no: 0,
page_size: 10,
updateType: '',
startKey: ''
},
labelArr: '', # 初始化的标签信息
dataId: null # 与上面表单的id对应
}
},
- 函数
methods: {
/**
* 图片加载完成后回调, 记录图片当前的大小和原始大小 data={rawW,rawH,currentW,currentH}
*/
onImageLoad (imageInfo) {
if (!this.imageInfo[this.pageData.currentPage]) {
this.imageInfo[this.pageData.currentPage] = imageInfo
}
console.info(this.imageInfo)
},
/**
* 当控件准备完成后回调,参数为 uniqueKey
*/
onReady () {
// let markerDiv = document.getElementsByClassName('vmr-ai-panel')
// let innnerDiv = markerDiv[0].firstChild
// let markerImg = document.getElementsByClassName('vmr-ai-raw-image')
// let markerMask = document.getElementsByClassName('vmr-ai-raw-image-mask')
// markerDiv[0].setAttribute('style', 'position: relative; overflow: hidden; width: 60%; height: 60%;')
// innnerDiv.setAttribute('style', 'position: relative; overflow: hidden;')
// markerImg[0].setAttribute('style', 'display: block; position: absolute; user-select: none; width:700px; height: 500px;')
// markerMask[0].setAttribute('style', 'user-select: none; position: absolute; cursor: crosshair; left: 0px; top: 0px;width:700px; height: 500px;')
},
/**
* 画框后回调,data 和 uniqueKey先不用了
*/
onDrawOne (data, uniqueKey) {
if (!this.selected.name || !this.selected.value) {
this.$message.info('请先设置标签')
this.$refs['aiPanel-editor'].getMarker().clearData()
return
}
let name = data.tagName === '请选择或添加新标签' ? this.selected.name : data.tagName
let tagValue = data.tagName === '请选择或添加新标签' ? this.selected.value : data.tag
this.$refs['aiPanel-editor'].getMarker().setTag({
tagName: name,
tag: tagValue
})
},
/**
* 当选中图片上的标注框时回调,参数为data【标注数据】, uniqueKey
*/
selectOne (data, uniqueKey) {
},
/**
* 当标注框位置或者标框属性发生改动时回调,参数为data【标注数据】, uniqueKey
*/
onUpdated (data, uniqueKey) {
},
/**
* 切换下一张图片
*/
turnNext () {
let marker = this.$refs['aiPanel-editor'].getMarker()
this.saveCurrentPageData(marker)
marker.clearData()
if ((this.pageData.currentPage + 1) < this.testForm.sign.length) {
this.pageData.currentPage += 1
}
this.currentInfo.currentBaseImage = this.testForm.sign[this.pageData.currentPage].src
// 页数+1了之后,如果之前是有data的,就加载进去
if (this.allInfo[this.pageData.currentPage]) {
marker.renderData(this.allInfo[this.pageData.currentPage])
}
},
/**
* 清空当前marker的标记信息
*/
clearAll () {
this.$refs['aiPanel-editor'].getMarker().clearData()
if (this.allInfo[this.pageData.currentPage]) {
this.allInfo[this.pageData.currentPage] = null
}
},
/**
* 切换上一张图片
*/
before () {
let marker = this.$refs['aiPanel-editor'].getMarker()
this.saveCurrentPageData(marker)
marker.clearData()
if (this.pageData.currentPage > 0) {
this.pageData.currentPage -= 1
}
this.currentInfo.currentBaseImage = this.testForm.sign[this.pageData.currentPage].src
// 页数-1了之后,如果之前是有data的,就加载进去
if (this.allInfo[this.pageData.currentPage]) {
marker.renderData(this.allInfo[this.pageData.currentPage])
}
},
/**
* click标签切换,切换当前图标
* @param index
* @param event
*/
handleTabClick (index, event) {
this.selected.index = index
let el = event.currentTarget
this.selected.name = el.getAttribute('name')
this.selected.value = el.getAttribute('code')
},
/**
* 删除标签:1.至少保留一个标签,不能删除最后一个 2.删除系统选中的,需要
*/
handleDeleteTab (index) {
if (this.label.length <= 1) {
this.$message.warning('至少保留一个标签')
return
}
this.label.splice(index, 1)
if (index === this.selected.index) {
this.selected.index = 0
this.selectTab()
}
},
handleAddTab () {
this.newTab.name = ''
this.newTab.value = ''
this.dialogVisible = true
},
/**
* 保存新增的标签
*/
saveTab () {
if (!this.newTab.name || !this.newTab.value) {
this.$message.warning('标签名或标签值不能为空')
return
}
for (let index in this.label) {
let item = this.label[index]
if (item.name === this.newTab.name || item.value === this.newTab.value) {
this.$message.warning('标签名或标签值已存在,请重新输入')
return
}
}
this.label.push({ name: this.newTab.name, value: this.newTab.value })
if (this.label.length === 1) {
this.selected.name = this.label[0].name
this.selected.value = this.label[0].value
}
this.dialogVisible = false
},
/**
* 加载默认的标签
*/
selectTab () {
this.selected.name = this.label[this.selected.index].name
this.selected.value = this.label[this.selected.index].value
},
/**
* 切换隐藏
*/
handleHiddenData () {
let el = document.getElementsByClassName('vmr-ai-raw-image-mask')
let isable = this.isHidden
if (!isable) {
el[0].style['display'] = 'none'
} else {
el[0].style['display'] = 'block'
}
this.isHidden = !isable
console.info('this.isHidden : ' + this.isHidden)
},
/**
* 完成标记,提交标记集合
*/
submitForm () {
this.saveCurrentPageData(null)
let result = []
for (let index in this.allInfo) {
let labelArr = this.allInfo[index]
let image = this.imageInfo[index]
let otherInfo = this.testForm.sign[index]
let param = {}
param.src = otherInfo.src
param.originalUrl = otherInfo.originalUrl
param.name = otherInfo.name
let size = {
width: image.rawW,
height: image.rawH
}
param.size = size
let text = []
if (labelArr && labelArr.length > 0) {
labelArr.forEach(item => {
let oneInfo = {}
oneInfo.id = item.uuid
oneInfo.type = item.tag
oneInfo.name = item.tagName
oneInfo.xmin = parseInt(item.position.x.substring(0, item.position.x.length - 1)) * size.width / 100
oneInfo.ymin = parseInt(item.position.y.substring(0, item.position.y.length - 1)) * size.height / 100
oneInfo.xmax = parseInt(item.position.x1.substring(0, item.position.x1.length - 1)) * size.width / 100
oneInfo.ymax = parseInt(item.position.y1.substring(0, item.position.y1.length - 1)) * size.height / 100
oneInfo.width = (oneInfo.xmax - oneInfo.xmin).toFixed(2)
oneInfo.height = (oneInfo.ymax - oneInfo.ymin).toFixed(2)
text.push(oneInfo)
})
}
param.text = text
result.push(param)
}
let req = {}
req.labelInfoVOList = result
req.dataId = this.dataId
req.labelArr = this.labelArr
completeLabel(req).then(res => {
this.$router.back()
})
},
/**
* 保存当前页面的标记信息当data-allInfo
* @param marker
*/
saveCurrentPageData (marker) {
if (!marker) {
marker = this.$refs['aiPanel-editor'].getMarker()
}
this.allInfo[this.pageData.currentPage] = marker.getData()
}
},
mounted () {
/**
* 刚刚切进来的时候,初始化内容
*/
if (this.$route.query.dataId) {
this.searchDataDetail.id = this.$route.query.dataId
this.searchDataDetail.isAdmin = this.$route.query.ossType
this.dataId = this.$route.query.dataId
} else {
this.$message.error('获取样本失败,请重新尝试')
return
}
this.searchDataDetail.updateType = 'original'
this.searchDataDetail.page_no = 0
this.searchDataDetail.page_size = 100000
// 初始化内容
listOssData(this.searchDataDetail).then(res => {
if (res.code === 100) {
// 加载图片
let dataList = res.result.data
let formats = ['bmp', 'jpg', 'png', 'tif', 'gif', 'pcx', 'tga', 'exif', 'fpx', 'svg', 'psd', 'cdr', 'pcd', 'dxf', 'ufo', 'eps', 'ai', 'raw', 'WMF', 'webp']
dataList.forEach(item => {
let itemName = item.name
let ending = itemName.substring(itemName.lastIndexOf('.') + 1)
if (formats.indexOf(ending) > -1) {
this.testForm.sign.push({ src: item.url, uuid: getUUid(12, 9), name: itemName, originalUrl: item.originalUrl })
}
})
if (this.testForm.sign.length === 0) {
this.$message.info('没有可以标记的内容')
}
this.allInfo = new Array(this.testForm.sign.length)
this.imageInfo = new Array(this.testForm.sign.length)
// 设置初始图片
this.currentInfo.currentBaseImage = this.testForm.sign[this.pageData.currentPage].src
}
}).catch(error => {
console.log(error)
})
/**
* 初始化标签
*/
detailData(this.$route.query.dataId).then(res => {
if (res.result.labelClassify === 1) {
this.labelArr = res.result.labelArr
if (this.labelArr) {
let labels = JSON.parse(this.labelArr)
labels.forEach(item => {
this.label.push(item)
})
this.selectTab()
}
}
})
},
created () {}