PAYATHON 2026

如何将粒子特效限制在 SVG Konva.Path 的不规则路径内?

数据小陈

结论

将粒子放入带有 clipFunc 的 Konva.Group,并让该组使用与 room 相同的变换。裁剪路径和粒子位置都按组的局部坐标计算,粒子便只会显示在 SVG 路径内。

room 的缩放为 scaleX: 3.8, scaleY: 3.8,而现有的 fireGroup 没有相同的缩放。粒子又是根据 getClientRect() 返回的画布坐标生成的。裁剪路径、粒子和房间如果使用不同的坐标系,即使添加了裁剪函数,边界也可能对不齐。

修改 startFire

现代浏览器的 Path2D 可以读取 SVG path 数据。Konva 的 clipFunc 支持返回 [Path2D, clipRule],因此可以直接用原 SVG 路径裁剪组内内容:

function startFire(room) {
  if (room.fireAnimation) {
    room.fireAnimation.stop();
    room.fireGroup.destroy();
    room.fireAnimation = null;
    room.fireGroup = null;
  }

  const rect = room.getSelfRect();
  const clipPath = new Path2D(room.data());
  const layer = room.getLayer();

  const fireGroup = new Konva.Group({
    x: room.x(),
    y: room.y(),
    scaleX: room.scaleX(),
    scaleY: room.scaleY(),
    rotation: room.rotation(),
    offsetX: room.offsetX(),
    offsetY: room.offsetY(),
    clipFunc: () => [clipPath, 'nonzero'],
  });

  layer.add(fireGroup);

  const centerX = rect.x + rect.width / 2;
  const centerY = rect.y + rect.height / 2;

  const anim = new Konva.Animation(() => {
    if (Math.random() > 0.1) {
      fireGroup.add(new Konva.Circle({
        x: centerX + Math.random() * 20 - 10,
        y: centerY + Math.random() * 20 - 10,
        radius: Math.random() * 3 + 1,
        fill: Math.random() > 0.5 ? 'orange' : 'red',
        opacity: 0.8,
      }));
    }

    fireGroup.getChildren().forEach((particle) => {
      particle.y(particle.y() - 1.5);
      particle.opacity(particle.opacity() - 0.015);

      if (particle.opacity() <= 0) {
        particle.destroy();
      }
    });
  }, layer);

  anim.start();
  room.fireAnimation = anim;
  room.fireGroup = fireGroup;
}

getSelfRect() 返回路径自身的局部边界,所以中心点也使用组的局部坐标。组复制了路径的位置、缩放、旋转和偏移。如果路径还应用了倾斜等其他变换,粒子组也需要保持相同变换。

Konva 文档说明,clipFunc 可以通过返回 Path2D 和填充规则实现复杂裁剪,Konva.Path.data() 可以读取 SVG 路径数据。clipFunc 文档, Konva.Path 文档

注意事项

  • 如果缩放后的粒子显得过大或移动过快,可以调小 radius 和每帧的纵向位移。这些数值也会随 fireGroup 缩放。
  • 如果 SVG 路径包含镂空区域,请检查路径的填充规则。需要偶奇规则时,可以将 'nonzero' 改为 'evenodd'。
  • globalCompositeOperation 控制画布绘制的合成方式,效果取决于绘制顺序和画布状态。它不能按指定的几何路径裁剪内容;在这个场景中,使用组的 clipFunc 更直接。
  • Path2D 是否支持 SVG 数据取决于运行环境。目标浏览器若不支持,可以改用支持该功能的环境,或采用单独的 Canvas 遮罩方案。

备注:内容仅供参考。