fabric.js

canvas原生方法之上提供简单而强大的对象模型。fabric.js负责画布状态和渲染,并让我们直接使用绘制后的“对象”。

Posted by page on April 23, 2024

Fabric

官方文档 中文教程

安装

npm i fabric -S

创建实例

let canvas = new fabric.Canvas('c'); // id selector string
// 或
let $canvas = document.querySelector('#c');
let canvas = new fabric.Canvas($canvas);
// 或创建实例后追加到doc
const $canvas = document.createElement('canvas');
const canvas = new fabric.Canvas($canvas);
document.body.appendChild(canvas);

实例参数

const canvas = new fabric.Canvas($canvas, {
  width,
  height,
  selection: false, // 禁用画布框选操作
  hoverCursor: 'default',
  freeDrawingCursor: 'none',
  isDrawingMode: true,
  ...
});

画布对象

canvas.getObjects():获取画布对象集合,支持传入 type 类型 image/rect...

canvas.dispose():移除画布实例,释放内存

canvas.toDataURL():画布转为DataURL,支持自定义 width/height,top/left,format

ctx.renderAll():更新所有画布

背景

// 背景图
const img = new fabric.Image($img, { ... }); // const img = url;
canvas.setBackgroundImage(img, canvas.renderAll.bind(canvas));
// 背景色:十六进制,RGB或RGBA颜色
canvas.setBackgroundColor("#000000", canvas.renderAll.bind(canvas));

// 清除背景
canvas.setBackgroundColor(''); // 清空背景色
canvas.setBackgroundImage(null); // 清空背景图

// 根据背景图宽高自适应覆盖
const zoom = canvas.getZoom();
let scaleX = canvas.width / img.width;
let scaleY = canvas.height / img.height;
const scale = Number((Math.max(scaleX, scaleY) / zoom).toFixed(1));
img.set({
  scaleX: scale,
  scaleY: scale,
  left: (canvas.width / zoom) >> 1,
  top: (canvas.height / zoom) >> 1,
  originX: 'center',
  originY: 'center',
});

宽高

canvas.setDimensions({
  width: originWidth,
  height: originHeight,
});
ctx.width
ctx.height

缩放

canvas.setZoom(1); // 画布内容缩放
canvas.getZoom(1);

框选

默认 fabric 画布(canvas)支持通过鼠标进行框选对象交互操作

new fabric.Canvas($canvas, {
  width,
  height,
  selection: false, // 禁止框选选中
  hoverCursor: 'default',
  freeDrawingCursor: 'none',
});

容器

canvas.wrapperEl 返回 fabric画布的容器dom,即 old-canvas 和 new-canvas 父元素

事件

常用事件

ctx.on("before:path:created")

ctx.on("path:created")

自定义事件

监听事件

ctx.on('history:change', () => { ... });

派发事件

canvas.fire('history:change', { record });

基础对象

基本属性

设置对象属性

img.set('selectable', false); // 设置单个对象属性

// 设置对象多个属性
this.eraser.set({
  width,
  height,
  dirty: true, // 下次需要更新的
  erasable: false,
  fill: '#000000',
  stroke, opacity
});

selectable:对象默认可选中(true),如可拖拽以及resize

图片

链接图片

fabric.Image.fromURL(
  imageUrl,
  function (img) {
    img.scaleToWidth(canvas.width);
    img.scaleToHeight(canvas.height);
    img.set('selectable', false);
    canvas.add(img);
    canvas.renderAll();
  },
  { crossOrigin: 'Anonymous' },
);

: 不同当前域的图片链接须声明 crossOrigin: 'Anonymous',否则造成 toDataURL 失败等错误

矩形

let rect = new fabric.Rect({
  left: 100,
  top: 100,
  fill: 'red',
  stroke: '#ffffff',
  width: 20,
  height: 20
});
canvas.add(rect);

路径

修改路径属性

drawCtx.on('before:path:created', async ({ path }) => {
  let whitePath = fabric.util.object.clone(path);
  whitePath.stroke = '#ffffff';
  whitePath.set({ globalCompositeOperation: 'source-over' }); // 擦除的路径
});

自由绘画

设置 fabric canvas(画布)为 isDrawingMode 为 true,开启自由绘画模式

canvas.set('isDrawingMode', true); // 开启绘画
canvas.set('isDrawingMode', false); // 禁用绘画
canvas.renderAll();

canvas.set('freeDrawingCursor', 'none'); // 绘画时画布cursor 

PencilBrush

默认绘画模式下即PencilBrush画笔实例

支持设置画笔半径、颜色等

canvas.freeDrawingBrush.width = 24;
canvas.freeDrawingBrush.color = "#c0c0c0";

const pencilBrush = new fabric.PencilBrush(canvas);
canvas.freeDrawingBrush = pencilBrush;

画笔width自适应:当画布缩放后,画笔 radius 不会根据缩放等比调整;需根据 radiusNumber 和 scaleRatio 更改重新设置

drawCtx.freeDrawingBrush.width = parseInt(radiusNumber.value / scaleRatio);

EraserBrush

擦除画笔,支持对canvas内容擦除与修补,需额外引入 EraserBrush 类

除了 Custom Fabric build 方式引入,fabric-eraser-brush

const eraserBrush = new fabric.EraserBrush(canvas);
eraserBrush.width = canvas.freeDrawingBrush.width;
// eraserBrush.inverted = true 反向擦除(修补)状态
canvas.freeDrawingBrush = eraserBrush;

通过对象/画布 erasable 属性控制是否可擦除,默认为true;

背景擦除:对于 canvas 背景图和背景色擦除是生效的,但修补状态(inverted)无法实时绘画路径反馈,改用 image/rect 添加到画布实现

默认擦除:实现某个内容对象使其默认被擦除状态。可通过应用 destination-out

// 默认被擦除路径
let path = new fabric.Path(
  `M 0 0 L ${img.width} 0 L ${img.width} ${img.height} L 0 ${img.height} z`,
);
path.set('globalCompositeOperation', 'destination-out');
fabric.EraserBrush.prototype._addPathToObjectEraser(img, path);