做这个组件的初衷,是基于AI组的标注识别,传送一张图片以及图片上的一些坐标,返回对应的识别结果,前端要做的就是基于一张图片,在图片上绘制出相应的标注框,并将标注框对应的坐标以及宽高传送给后端进行识别,这是最基础的需求。在图片上进行绘制,首先想到的是用canvas
,cancas
强大的功能能让我们在图片上为所欲为,原生的canvas
api众多且繁杂,上手不易,fabric
是一个基于canvas的强大的框架,提供一种类似面向对象的方法来编写canva,在原生canvas之上提供了交互式对象模型,通过简洁的api就可以在画布上进行丰富的操作。因此选择fabric
来作为基础框架。
fabric
是基于canvas进行的api封装,可以实现绘制矩形、圆、椭圆、文本等一些基础图形,同时支持画笔自定义图形,fabric
的优点在于它对生成的canvas画布进行了良好的封装,包括对画布以及画布上的对象进行调整,监听画布和对象的各种事件,使得画布交互逻辑变得简单易上手。fabric
的官网详细地列出了fabric的各种参数以及api,由于Fabric.js是国外的框架,文档为全英文,且相关示例少,所以建议配合源码使用
此处参考:https://github.com/EmilyZhang123/vue-label-me
首先组件从外部接收图片链接
props:{
imgData: String // 图片链接
}
watch
监听imageData
变化,并生成画布
watch:{
imageData(val){
if(val){
this.fabricCanvas() // 生成画布
}
}
}
fabricCanvas
事件主要是初始化fabric,并将图片设置成画布的背景图片,以便后续在画布上添加标注框
<template>
<div id="canvax-box">
<canvas id="label-canvas" :width="width" :height="height">
</div>
</template>
<script>
export default{
methods:{
fabricCanvas(){
if(this.fabricObj){ // 如果画布已经存在,清空画布重新绘制
this.fabricObj.clear()
} else {
this.fabricObj = new fabric.Canvas('lavel-canvas',{
// 此处设置画布的初始属性
uniformScaling: false, // 等比例缩放
enableRetinaScaling: false,
selection: false // 禁止组选择
}
}
let Shape
const image = new Image()
image.src = this.imageData
image.setAttribute('crossOrigin','anonymous') // 允许跨域访问
image.onload = () => {
// 将canvas的width和height设置成图片的原始width,height
this.width = image.width
this.height = image.height
this.fabricObj.setWidth(this.width)
this.fabricObj.setHeight(this.height)
// 将图片放置在外部容器中
let boxWidth = document.getElementById('canvas-box').offsetWidth
let boxHeight = document.getElementById('canvas-box').offsetHeight
let scaleX = boxWidth / image.width
let scaleY = boxHeight / image.height
// 确定缩放因子
this.scale = scaleX > scaleY ? scaleX : scaleY
document.querySelector('.canvas-container').style.width = this.width * this.scale + 'px'
document.querySelector('.canvas-container').style.height = this.height * this.scale + 'px'
document.querySelector('#label-canvas').style.width = this.width * this.scale + 'px'
document.querySelector('#label-canvas').style.height = this.height * this.scale + 'px'
document.querySelector('.upper-canvas').style.width = this.width * this.scale + 'px'
document.querySelector('.upper-canvas').style.height = this.height * this.scale + 'px'
Shape = new fabric.Image(image)
this.fabric.setBackgroundImage(Shape,
this.fabricObj.renderAll.bind(this.fabricObj),
{
opaity: 1,
angle: 0
}
)
this.$nextTick(()=>{
this.fabricObj.renderAll() // 重新渲染画布
})
}
}
}
}
</script>
fabric
提供了一系列的事件帮助我们来很好的对画布进行各种操作此次主要用到以下几个事件
watch:{
imageData(val){
if(val){
this.fabricCanvas() // 生成画布
this.fabricObjEvent() // 监听画布事件
}
}
}
标注画框主要用到的是上述中的mouse:down
:画笔落下;mouse:move
画框;mouse:up
画笔抬起事件
在调整画框之前,首先要将画布设置为可选择
如果想要修改画框的默认选中样式,可修改画框的对应参数即可
调整画框主要用到上述的object:moving
:对象移动;object:modified
:对象调整;
handleObjectMoving(){
// 阻止对象移动到画布外面
let padding = 0; // 内容距离画布的空白宽度,主动设置
var obj = e.target;
if (obj.currentHeight > obj.canvas.height - padding * 2 ||
obj.currentWidth > obj.canvas.width - padding * 2) {
return;
}
obj.setCoords();
if (obj.getBoundingRect().top < padding || obj.getBoundingRect().left < padding) {
obj.top = Math.max(obj.top, obj.top - obj.getBoundingRect().top + padding);
obj.left = Math.max(obj.left, obj.left - obj.getBoundingRect().left + padding);
}
if (obj.getBoundingRect().top + obj.getBoundingRect().height > obj.canvas.height - padding || obj.getBoundingRect().left + obj.getBoundingRect().width > obj.canvas.width - padding) {
obj.top = Math.min(
obj.top,
obj.canvas.height - obj.getBoundingRect().height + obj.top - obj.getBoundingRect().top - padding
);
obj.left = Math.min(
obj.left,
obj.canvas.width - obj.getBoundingRect().width + obj.left - obj.getBoundingRect().left - padding
);
}
}
handleObjectModified(e){
this.$emit('objectModified',e.target)
}
画框被选中时,可抛出选中事件
// rect setRect()方法中生成的画框
rect.on('selected',(e)=>{
this.$emit('objectSelected', e.target)
})
调用fabric
的remove事件即可
this.fabricObj.remove(item)
clearAllMark(){
const objects = this.fabricObj.getObjects()
for(let i in objects){
this.fabricObj.remove(i)
}
this.$emit('clearAllMark')
}
此处参考 https://github.com/Dark2017/vue-dark-photo
使用css的transform
来对画布进行放大缩小和拖拽操作
2. 缩小
3. 还原
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。