Fabric.js 实现数据标注

创建基础项目

为了方便演示,我在初始化画布的时:

1.添加一个背景图,该背景图的尺寸和初始化的画布一样大。
2.默认渲染一个矩形。
3.右侧可以放大缩小画布以及删除选中矩形。
4.点击左侧可以修改标签内容。

Fabric.js 实现数据标注_第1张图片 -----------------------------------------------------------------------------------------------------------------------------------------------

相关API

1.删除元素的2种方法:

  • canvas.remove(...object)
  • new fabric.Control 绑定控制器删除

2.更新标签内容:

  • 获取选中的矩形框点击标签更新。
  • 选中标签后画布上绘制。

3.搜索框支持本地模糊匹配标签。4.isOff字段在代码中起到判断是否绘制的作用,fabric.Object.prototype.selectable = false 在绘制时关闭选中功能前提是未选中状态。封装myFabric类

/* eslint-disable import/no-extraneous-dependencies */
import { fabric } from 'fabric';

// 删除img
const deleteIcon ="data:image/svg+xml,%3C%3Fxml version='1.0' encoding='utf-8'%3F%3E%3C
const deleteIconImg = document.createElement('img');
deleteIconImg.src = deleteIcon;

class myFabric {/** * 构造函数 * @param {object} params 构造函数参数 */constructor(params) {this.canvas = null; // 画布对象this.isOff = true; // 是否开启画画模式this.downPoint = null; // 按下鼠标时的坐标this.upPoint = null; // 松开鼠标时的坐标this.label = '';this.change = null;this.initCanvas(params);}/** * 初始化画布 * @param {object} params{ imgUrl, data } 图片路径,标注数据 */initCanvas(params) {const { imgUrl, data, change } = params;this.change = change;const img = new Image();img.src = imgUrl;img.onload = () => {this.canvas = new fabric.Canvas('canvas');fabric.Object.prototype.transparentCorners = false;fabric.Object.prototype.cornerColor = 'white';fabric.Object.prototype.cornerStyle = 'circle';// console.log(this.canvas, fabric);// 创建删除按钮fabric.Object.prototype.controls.deleteControl = new fabric.Control({x: 0.5,y: -0.5,offsetY: -16,offsetX: 16,cursorStyle: 'pointer',mouseUpHandler: () => {this.delete();},render: (ctx, left, top, styleOverride, fabricObject) => {const size = 24;ctx.save();ctx.translate(left, top);ctx.rotate(fabric.util.degreesToRadians(fabricObject.angle));ctx.drawImage(deleteIconImg, -size / 2, -size / 2, size, size);ctx.restore();},cornerSize: 24,});fabric.Image.fromURL(imgUrl, (imgs) => {this.canvas.setBackgroundImage(imgs, this.canvas.renderAll.bind(this.canvas));});this.canvas.setWidth(img.width);this.canvas.setHeight(img.height);// 选中this.canvas.on('selection:created', (e) => {this.isOff = false;// console.log('选中');});// 取消选中this.canvas.on('selection:cleared', () => {this.isOff = true;// console.log('取消选中');});this.canvas.on('selection:updated', () => {// console.log('选中updated');});// mouse:move// 鼠标在画布上按下this.canvas.on('mouse:down', (e) => {if (this.isOff) {fabric.Object.prototype.selectable = false;}// 鼠标左键按下时,将当前坐标 赋值给 downPoint。{x: xxx, y: xxx} 的格式this.downPoint = e.absolutePointer;// console.log('鼠标左键按下时');});// 松开鼠标左键时this.canvas.on('mouse:up', (e) => {if (this.isOff) {fabric.Object.prototype.selectable = true;}// console.log('松开鼠标左键时');// 同步外部数据// change(this.getAll());// 绘制矩形的模式下,才执行下面的代码// 松开鼠标左键时,将当前坐标 赋值给 upPointthis.upPoint = e.absolutePointer;// 调用 创建矩形 的方法this.createRect();change(this.getAll());}); // 鼠标在画布上松开data.forEach((item) => {this.canvas.add(this.creatGroup(item));this.canvas.renderAll();});// 将矩形添加到画布上};}/** * 创建矩形和text组 * @param {object} item {x,y,w,h,r,label} */creatGroup(item) {const { top, left, width, height, angle } = item;const rect = new fabric.Rect({top,left,width,height,angle,padding: 0,strokeUniform: true,noScaleCache: false,stroke: this.label ? 'lightgreen' : 'red',strokeWidth: 1,fill: 'rgba(0,0,255,0.2)', // 填充色:透明});// 矩形对象const text = new fabric.Textbox(this.label, {top: top + height / 2.3,left,width,height,fontFamily: 'Helvetica',fill: 'white', // 设置字体颜色fontSize: 14,textAlign: 'center',});const group = new fabric.Group([rect, text]);return group;}// 创建矩形createRect() {if (!this.isOff) return;// 如果点击和松开鼠标,都是在同一个坐标点,不会生成矩形if (JSON.stringify(this.downPoint) === JSON.stringify(this.upPoint)) {return;}// 创建矩形// 矩形参数计算const top = Math.min(this.downPoint.y, this.upPoint.y);const left = Math.min(this.downPoint.x, this.upPoint.x);const width = Math.abs(this.downPoint.x - this.upPoint.x);const height = Math.abs(this.downPoint.y - this.upPoint.y);if (width < 2 || height < 2) return;// 将矩形添加到画布上this.canvas.add(this.creatGroup({top,left,width,height,angle: 0,label: this.label,}));this.canvas.renderAll();// 创建完矩形,清空 downPoint 和 upPointthis.downPoint = null;this.upPoint = null;}/** * 画布缩放 * @param {Boolean} type true放大 false缩小 */setZoom(type) {let scale = this.canvas.getZoom();if (type) {scale += 0.1;} else {scale -= 0.1;}this.canvas.setZoom(scale);}/** * 画布上的元素数据 */getAll() {const allTarget = this.canvas.getObjects().map((item) => ({label: item._objects[1].text,width: item.width,height: item.height,left: item.left,top: item.top,angle: item.angle || 0,}));return allTarget;}// 更新标注框文字updateLabel(label) {var active = this.canvas.getActiveObject();if (active) {active.item(0).set({stroke: label ? 'lightgreen' : 'red',});active.item(1).set({text: label,});this.canvas.renderAll();}// active && active.set(active._objects[1], 'text', '444');// active._objects.this.label = label;}// 删除标注框delete() {var active = this.canvas.getActiveObject();if (active) {this.canvas.remove(active);if (active.type == 'activeSelection') {active.getObjects().forEach((x) => this.canvas.remove(x));this.canvas.discardActiveObject().renderAll();}}}// 销毁dispose() {this.canvas.dispose();}
}
export default myFabric; 

vue组件使用示列



 

最后

最近还整理一份JavaScript与ES的笔记,一共25个重要的知识点,对每个知识点都进行了讲解和分析。能帮你快速掌握JavaScript与ES的相关知识,提升工作效率。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

你可能感兴趣的:(javascript,fabric,前端)