You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Fabric.js中实现图像弯曲/折叠效果?

问题描述

我希望在Fabric.js中实现图像的“弯曲”效果,具体是调整图像的边角(例如右上角和右下角),使图像呈现出边缘折叠或弯曲的视觉效果。我曾考虑通过多边形叠加图像、调整多边形顶点的方式来实现该效果,但不确定如何在Fabric.js中正确应用这类变换。

请问Fabric.js是否有内置方法实现边缘弯曲/扭曲图像,或者该如何利用多边形叠加来模拟图像弯曲效果?同时我希望解决方案在对象移动或缩放时仍能流畅运行,能否提供实现指导?

以下是我尝试的代码示例:

const addPointsAndLines = (canvas: fabric.Canvas) => {
const r = 6;
const points = [
  { x: 167, y: 200 },
  { x: 500, y: 200 },
  { x: 500, y: 400 },
  { x: 200, y: 400 },
];

const circles: fabric.Circle[] = points.map((pt) => {
  const circle = new fabric.Circle({
    left: pt.x - r,
    top: pt.y - r,
    radius: r,
    fill: "red",
    hasBorders: false,
    hasControls: false,
    selectable: true,
  });
  canvas.add(circle);
  return circle;
});

const polygon = new fabric.Polygon(points, {
  fill: "rgba(128,128,128,0.4)",
  selectable: false,
  evented: false,
});

fabric.Image.fromURL("public/FD1C43.webp").then((img) => {
  const pattern = new fabric.Pattern({
    source: img.getElement(),
    repeat: "no-repeat",
  });

  polygon.set({
    fill: pattern,
  });

  canvas.renderAll();
});

canvas.add(polygon);
canvas.sendObjectToBack(polygon);
polygonRef.current = polygon;

const lines: fabric.Line[] = circles.map((_, i) => {
  const curr = circles[i];
  const next = circles[(i + 1) % circles.length];
  const line = new fabric.Line(
    [curr.left! + r, curr.top! + r, next.left! + r, next.top! + r],
    {
      stroke: "#000",
      strokeWidth: 2,
      strokeDashArray: [6, 4],
      selectable: false,
      evented: false,
    }
  );
  canvas.add(line);
  return line;
});

canvas.on("object:moving", () => {
  circles.forEach((circle, i) => {
    const cx = circle.left + r;
    const cy = circle.top + r;

    polygon.points[i].x = cx;
    polygon.points[i].y = cy;

    const prevLine = lines[(i - 1 + lines.length) % lines.length];
    const nextLine = lines[i];

    prevLine.set({ x2: cx, y2: cy });
    nextLine.set({ x1: cx, y1: cy });
  });

  polygon.set({
    dirty: true,
  });
  polygon.setCoords();
  polygon.setBoundingBox(true);

  canvas.renderAll();
});

canvas.renderAll();
};
解决方案

1. Fabric.js内置方法说明

Fabric.js核心仅支持仿射变换(缩放、旋转、平移、斜切),没有直接实现非仿射的弯曲/扭曲效果的内置方法。这类边缘折叠的视觉效果需要自定义模拟,你选择的多边形叠加方案是可行的方向。

2. 优化多边形+图案填充方案

你的现有代码已经实现了可拖动顶点和多边形更新,但存在关键问题:图像图案的坐标基于画布全局,多边形变形时图案不会同步跟随顶点变化。以下是修复和优化的关键步骤:

关键修改点

  • 同步图案偏移:多边形顶点移动时,调整图案的offsetX和offsetY,让图像纹理匹配多边形顶点位置。
  • 优化渲染性能:减少不必要计算,避免频繁调用高开销方法。

修改后的完整代码

const addPointsAndLines = (canvas: fabric.Canvas) => {
  const r = 6;
  const points = [
    { x: 167, y: 200 },
    { x: 500, y: 200 },
    { x: 500, y: 400 },
    { x: 200, y: 400 },
  ];

  const circles: fabric.Circle[] = points.map((pt) => {
    const circle = new fabric.Circle({
      left: pt.x - r,
      top: pt.y - r,
      radius: r,
      fill: "red",
      hasBorders: false,
      hasControls: false,
      selectable: true,
    });
    canvas.add(circle);
    return circle;
  });

  const polygon = new fabric.Polygon(points, {
    fill: "rgba(128,128,128,0.4)",
    selectable: false,
    evented: false,
  });

  fabric.Image.fromURL("public/FD1C43.webp").then((img) => {
    const pattern = new fabric.Pattern({
      source: img.getElement(),
      repeat: "no-repeat",
      // 初始化图案偏移,匹配多边形初始位置
      offsetX: points[0].x,
      offsetY: points[0].y,
    });

    polygon.set({ fill: pattern });
    canvas.renderAll();
  });

  canvas.add(polygon);
  canvas.sendObjectToBack(polygon);
  const polygonRef = { current: polygon };

  const lines: fabric.Line[] = circles.map((_, i) => {
    const curr = circles[i];
    const next = circles[(i + 1) % circles.length];
    const line = new fabric.Line(
      [curr.left! + r, curr.top! + r, next.left! + r, next.top! + r],
      {
        stroke: "#000",
        strokeWidth: 2,
        strokeDashArray: [6, 4],
        selectable: false,
        evented: false,
      }
    );
    canvas.add(line);
    return line;
  });

  // 使用requestAnimationFrame批量渲染,提升流畅度
  let renderTimeout: number;
  canvas.on("object:moving", () => {
    circles.forEach((circle, i) => {
      const cx = circle.left + r;
      const cy = circle.top + r;

      polygon.points[i].x = cx;
      polygon.points[i].y = cy;

      const prevLine = lines[(i - 1 + lines.length) % lines.length];
      const nextLine = lines[i];

      prevLine.set({ x2: cx, y2: cy });
      nextLine.set({ x1: cx, y1: cy });
    });

    // 更新图案偏移,让图像跟随多边形顶点
    if (polygon.fill instanceof fabric.Pattern) {
      const topLeftPoint = polygon.points[0];
      polygon.fill.offsetX = topLeftPoint.x;
      polygon.fill.offsetY = topLeftPoint.y;
    }

    polygon.set({ dirty: true });
    polygon.setCoords();

    // 避免频繁render,用requestAnimationFrame节流
    if (renderTimeout) cancelAnimationFrame(renderTimeout);
    renderTimeout = requestAnimationFrame(() => {
      canvas.renderAll();
    });
  });

  canvas.renderAll();
};

代码解释

  • 图案偏移同步:将图案的offsetX和offsetY绑定到多边形的左上角顶点,确保图像始终贴合多边形的变形区域。
  • 渲染性能优化:用requestAnimationFrame替代直接调用canvas.renderAll(),避免拖动过程中频繁触发渲染,提升流畅度。
  • 移除不必要计算:去掉setBoundingBox(true),该方法会重新计算多边形边界框,仅在需要精确碰撞检测时使用,拖动过程中可省略。

3. 进阶方案:更真实的弯曲效果

如果需要更逼真的曲面弯曲(而非单纯顶点拉伸),可以考虑:

  • 自定义滤镜:基于Fabric.js的fabric.Image.filters扩展,使用Canvas的getImageData和putImageData手动修改像素坐标,实现透视扭曲。
  • WebGL渲染:借助Fabric.js的WebGL支持,编写自定义Shader实现GPU加速的弯曲效果,适合复杂场景。

内容的提问来源于stack exchange,提问作者Bogdan Galata

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 05:25:58