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

自定义控件修改Fabric Circle位置和尺寸后为何未重渲染?

问题:Fabric.js自定义控件缩放Circle时未重渲染

我查阅了Fabric官方文档及示例,自定义了两个控件用于缩放Circle组件。发现Circle的容器尺寸和控件位置已发生变化,但Fabric并未对Circle进行重渲染。想了解该问题的原因,感谢您的帮助!

代码示例

JavaScript

var canvas = (this.__canvas = new fabric.Canvas("c"));
// create a rect object
function Add() {
  var newRect = new fabric.Rect({
    left: 100,
    top: 50,
    fill: "yellow",
    width: 200,
    height: 100,
    objectCaching: false,
    stroke: "lightgreen",
    strokeWidth: 4
  });
  canvas.add(newRect);
}

var circle = new fabric.Circle({
  radius: 100,
  fill: "#f55",
  top: 100,
  left: 100
  // visible: false
});

circle.set({
  borderColor: "red",
  cornerColor: "green",
  cornerSize: 12,
  cornerStyle: "circle",
  transparentCorners: false
});

canvas.add(circle);
canvas.setActiveObject(circle);
circle.controls = updateControls();

function updateControls() {
  return {
    mt: new fabric.Control({
      x: 0,
      y: -0.5,
      cursorStyle: "not-allowed",
      render: renderNotAllowed
    }),
    mr: new fabric.Control({
      x: 0.5,
      y: 0,
      cursorStyle: "not-allowed",
      render: renderNotAllowed
    }),
    ml: new fabric.Control({
      x: -0.5,
      y: 0,
      cursorStyle: "not-allowed",
      render: renderNotAllowed
    }),
    mb: new fabric.Control({
      x: 0,
      y: 0.5,
      cursorStyle: "not-allowed",
      render: renderNotAllowed
    }),
    br: new fabric.Control({
      x: 0.5,
      y: 0.5,
      cursorStyle: "crosshair",
      actionHandler: scaleObject
    }),
    tr: new fabric.Control({
      x: 0.5,
      y: -0.5,
      cursorStyle: "crosshair",
      actionHandler: scaleObject
    })
  };
}

function scaleObject(eventData, transform, x, y) {
  var circleObject = transform.target;
  var xOff = 0,
    yOff = 0,
    diff = 0;
  if (circleObject.__corner === "tr") {
    var trX = circleObject["left"];
    var trY = circleObject["top"] + 2 * circleObject["radius"];
    xOff = x - trX;
    yOff = trY - y;
    diff = Math.max(xOff, yOff, 0);
    circleObject["top"] = trY - diff;
    circleObject["left"] = trX;
  } else if (circleObject.__corner === "br") {
    var brX = circleObject["left"];
    var brY = circleObject["top"];
    xOff = x - brX;
    yOff = y - brY;
    diff = Math.max(xOff, yOff, 0);
  }
  var newRadius = Math.round(diff / 2);
  circleObject["radius"] = newRadius;
  circleObject["height"] = 2 * newRadius;
  circleObject["width"] = 2 * newRadius;
  circleObject.setCoords();
  canvas.requestRenderAll();
}

function renderNotAllowed(ctx, left, top, styleOverride, fabricObject) {
  ctx.beginPath();
  ctx.arc(left, top, 6, 0, 2 * Math.PI, false);
  ctx.fillStyle = "gray";
  ctx.fill();
  ctx.lineWidth = 1;
  ctx.strokeStyle = "white";
  ctx.stroke();
}

CSS

.controls {
  display: inline-block;
}

HTML

<script src="https://unpkg.com/fabric@latest/dist/fabric.js"></script>
<div class="controls">
  <p>
    <button id="add" onclick="Add()">Add a rectangle</button>
  </p>
</div>
<canvas id="c" width="500" height="500" style="border:1px solid #ccc"></canvas>

问题原因及解决方案

原因

你直接通过赋值修改了Circle对象的radius、top、left等属性,而没有使用Fabric提供的set()方法。Fabric内部依赖set()来标记对象为"已变更(dirty)",触发后续的重渲染逻辑。直接赋值不会触发这个标记,导致Canvas无法感知对象需要重新绘制。

解决方案

将属性赋值替换为set()方法修改,让Fabric正确检测到对象变更:

修改后的scaleObject函数:

function scaleObject(eventData, transform, x, y) {
  var circleObject = transform.target;
  var xOff = 0,
    yOff = 0,
    diff = 0;
  const updateProps = {};

  if (circleObject.__corner === "tr") {
    const trX = circleObject.left;
    const trY = circleObject.top + 2 * circleObject.radius;
    xOff = x - trX;
    yOff = trY - y;
    diff = Math.max(xOff, yOff, 0);
    updateProps.top = trY - diff;
    updateProps.left = trX;
  } else if (circleObject.__corner === "br") {
    const brX = circleObject.left;
    const brY = circleObject.top;
    xOff = x - brX;
    yOff = y - brY;
    diff = Math.max(xOff, yOff, 0);
  }

  const newRadius = Math.round(diff / 2);
  updateProps.radius = newRadius;
  updateProps.height = 2 * newRadius;
  updateProps.width = 2 * newRadius;

  circleObject.set(updateProps);
  circleObject.setCoords();
  canvas.requestRenderAll();
}

这样修改后,Fabric会正确识别Circle的属性变更,触发重渲染,你就能看到Circle的图形随控件操作更新了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 16:28:12