[使用 fabric 遇到的坑]

346 阅读2分钟

遇到问题

  1. google 看能否找到解决方案
  2. gpt 问
  3. 在 github issues 根据对应关键字找
  4. 在 github 项目里面找,别人实现的项目,看是否有相似功能

踩坑

  1. 加视频的时候,元素框添加成功,对应视频却加载不出来

    原因: 由于使用的是 西瓜视频 xgplayer,没有设置宽高导致渲染不出来

找到对应的 issues github.com/fabricjs/fa…

添加视频示例:fabricjs.com/video-eleme…

  const addVideo = (w: number, h: number) => {
    if (!canvasRef.current) return;
    let canvas = canvasRef.current;
    // 下面需要设置宽高,不然视频渲染不出来
    const videoElement = document.querySelector("video") as HTMLVideoElement;
    videoElement.width = w; // 设置宽度
    videoElement.height = h; // 设置高度const video1 = new fabric.Image(videoElement, {
      left: 0,
      top: 0,
      objectCaching: false,
      width: w,
      height: h,
    });
​
    canvas?.add(video1);
    // 设置层级
    canvas.moveTo(video1, 0);
    video1.getElement?.().play();
    fabric.util.requestAnimFrame(function render() {
      canvas.renderAll();
      fabric.util.requestAnimFrame(render);
    });
  };

其他工具函数

/**
 * 根据 ID 在 Fabric.js 画布中查找元素。
 *
 * @param {fabric.Canvas} canvas - Fabric.js 画布对象。
 * @param {string} id - 要查找的元素的 ID。
 * @returns {fabric.Object | undefined} - 返回找到的元素,如果找不到则返回 undefined。
 */
export const findFabricElementById = (canvas: fabric.Canvas, id: string) => {
  return canvas
    ?.getObjects()
    ?.find((fabricElement: any) => fabricElement?.id === id);
};
​
/**
 * 从画布上移除指定元素,并执行回调函数。
 *
 * @param {fabric.Canvas} canvas - Fabric.js 画布对象。
 * @param {string} elementId - 要移除元素的 ID。
 * @param {() => void} [onRemoved] - 移除元素后执行的回调函数。
 */
export const removeFabricElementById = (
  canvas: fabric.Canvas,
  elementId: string,
  onRemoved?: () => void,
) => {
  if (!canvas) return;
​
  let element = findFabricElementById(canvas, elementId);
  if (element) {
    canvas.remove(element);
    onRemoved && onRemoved();
  }
};
​
/**
 * 更新指定 ID 的 Fabric 元素
 * @param canvas - Fabric.js 画布实例,代表整个绘图区域
 * @param elementId - 要更新的元素的唯一标识符
 * @param onUpdate - 可选参数,一个回调函数,将在元素更新后执行
 */
export const updateFabricElementById = (
  canvas: fabric.Canvas,
  elementId: string,
  onUpdate?: (element: fabric.Object) => void,
) => {
  if (!canvas) return;
​
  // 通过元素 ID 查找要更新的元素
  let element = findFabricElementById(canvas, elementId);
  if (element) {
    // 如果提供了 onUpdate 回调函数,调用它并传递更新后的元素
    onUpdate && onUpdate(element);
    // 重新渲染整个画布
    canvas?.renderAll();
  }
};
​
/**
 * 控制画布上所有对象的可见性。
 *
 * @param {fabric.Canvas} canvas - Fabric.js 画布对象。
 * @param {boolean} isVisible - 如果为 `true`,则将所有对象设置为可见;如果为 `false`,则将所有对象设置为隐藏。
 */
export const controlAllObjectsVisibility = (
  canvas: fabric.Canvas,
  isVisible: boolean,
) => {
  const objects = canvas.getObjects();
​
  objects.forEach((object: fabric.Object) => {
    object.set("visible", isVisible);
  });
};
​
/**
 * 获取指定区域的 Textbox 元素的 Base64 图片。
 *
 * @param {fabric.Canvas} canvas - Fabric.js 画布对象。
 * @param {number} width - 指定区域的宽度。
 * @param {number} height - 指定区域的高度。
 * @param {number} x - 指定区域的左上角 x 坐标。
 * @param {number} y - 指定区域的左上角 y 坐标。
 * @returns {string} - Base64 图片字符串。
 */
export const getAreaTextBoxesBase64 = (
  canvas?: fabric.Canvas,
  // @ts-ignore
  width: number,
  height: number,
  x: number,
  y: number,
) => {
  if (!canvas) return "";
​
  // 隐藏除 Textbox 外的其他元素
  let objects = canvas.getObjects();
  objects.forEach((object: fabric.Object) => {
    if (!(object instanceof fabric.Textbox)) {
      object.set("visible", false);
    }
  });
​
  // 设置画布的裁剪区域
  // @ts-ignoreƒ
  canvas?.set({
    clipTo: (ctx: CanvasRenderingContext2D) => {
      ctx.rect(x, y, width, height);
    },
  });
​
  // 获取指定区域的 Base64 图片
  let canvasBase64 = canvas.toDataURL({
    format: "png", // 指定输出格式
    width: width,
    height: height,
    left: x, // 左上角 x 坐标
    top: y, // 左上角 y 坐标
  });
  controlAllObjectsVisibility(canvas, true);
  // 渲染画布
  canvas.renderAll();
​
  return canvasBase64 || "";
};