fabricjs + react 案例

一、案例

和 react 结合的使用案例:https://github.com/saninmersion/react-context-fabricjs

二、常见功能用法

以下代码都将采用 useContext 管理 canvas

调整 canvas 画板背景颜色

export const Menu: FC = () => {
  const { canvas } = useContext(FabricContext);

	const handelSetBackgroundColor = useCallback(
    (color: string) => {
      if (canvas) {
        canvas.backgroundColor = color;
        canvas.renderAll();
      }
    },
    [canvas]
  );

	return <Button onClick={()=> handelChangeBackgroundColor("#f9f9f9")}>调整背景颜色</Button>
}

实现遮罩功能

遮罩效果:

超出虚线部分的图片或内容是不显示的。遮罩的实现是借助裁剪功能实现,相关文档:http://fabricjs.com/clippath-part3

添加图片时指定一个正方形的裁剪区,图片就只能在该区域内展示。

const clipPath = new fabric.Rect({
        top: 150,
        left: 200,
        hasControls: false,
        height: 300,
        fill: "transparent",
        width: 200,
        absolutePositioned: true,
});

fabric.Image.fromURL(url, (image)=> {
	image.clipPath = clipPath;
	canvas.add(image);
});

从上面代码可以看到正方形的裁剪区并没有添加到 canvas 进行渲染,所以不会有 border 样式,因此还要添加一个大小、位置相同的带边框的正方形。

const strokeWidth = 2;
const clipBorder = new fabric.Rect({
      top: 150 + strokeWidth,
      left: 200 + strokeWidth,
      width: 200 - strokeWidth * 2,
      height: 300 - strokeWidth * 2,
      fill: "transparent",
      stroke: "#cccccc",
      strokeWidth,
      strokeDashArray: [6, 6], // 数值越大,虚线越稀疏
      absolutePositioned: true,
      selectable: false,
			hoverCursor: "default",
});

canvas.add(clipBorder);

另外一种方式是将裁剪区和裁剪区边框封为一个图案组渲染到 canvas 上。

首先定义方形遮罩类:

class ClipRect extends fabric.Group {
  private _clipPath: fabric.Rect;

  constructor(options: fabric.IRectOptions = {}) {
    const strokeWidth = 2;

		// 方形遮罩
    const clipPath = new fabric.Rect({
      ...(options ? options : {}),
      fill: "transparent",
      absolutePositioned: true,
      selectable: false,
    });

		// 遮罩边框
    const clipBorder = new fabric.Rect({
      ...options,
      top: (options.top || 0) + strokeWidth,
      left: (options.left || 0) + strokeWidth,
      width: (options.width || 0) - strokeWidth * 2,
      height: (options.height || 0) - strokeWidth * 2,
      fill: "transparent",
      stroke: "#cccccc",
      strokeWidth,
      strokeDashArray: [6, 6],
      rx: 4,
      ry: 4,
      absolutePositioned: true,
      selectable: false,
    });

    super([clipBorder, clipPath], pick(options, ["selectable"]));

    this._clipPath = clipPath;
  }

  // 向外暴漏一个拿到遮罩的方法
	getClipPath() {
    return this._clipPath;
  }
}

初始化时添加到 canvas

const clipRect = new ClipRect({
        top: 185,
        left: 200,
        width: 200,
        height: 200,
        originX: "center",
        originY: "center",
});

canvase.add(clipRect);

添加新图片时将图片的裁剪区设置为之前暴漏的那个方形遮罩

const handleAddImage = useCallback(async () => {
    if (!canvas) {
      return;
    }

    const image = await Image.fromURLAsync("<http://fabricjs.com/lib/pug.jpg>", {
      clipPath: (
        canvas.getObjects().find((item) => item instanceof ClipRect) as ClipRect
      )?.getClipPath(),
    });

    canvas.add(image);
}, [canvas]);

使添加的图片具有原物品的纹路

一件衣服的产品图片分为多图层,将一个带有衣服褶皱纹路的透明图片置于用户添加的图片之上时,用户添加的图片上也会有褶皱效果。将褶皱层图片置于最顶部图层方法:

canvas.setOverlayImage(frontImage); // frontImage 始终置于最顶层,不受新添加的图案影响

canvas.bringToFront(frontImage); // 好像不生效! 

添加仅在遮罩范围内显示的文字

同理添加文字时设置裁剪区即可

清除

使用 clear 将清除所有图案。

const handelReset = useCallback(() => {
    if (canvas) {
      canvas.clear();

      // 重绘初始画面
    }
}, [canvas]);

保存为 JSON 数据

const handleToJSON = useCallback(
    async (event) => {
      if (!canvas) {
        return;
      }

      console.log(canvas.toJSON());
    },
    [canvas]
);

关于 回退/前进

例子: https://github.com/abhi06991/Undo-Redo-Fabricjs

使用 localStorage 存储记录例子 https://github.com/paulcredmond/fabric-undoredo

关于 “回退 / 前进” 的讨论:https://github.com/fabricjs/fabric.js/issues/23

展示评论