Fabric.js 图层的顺序管理:置顶、置底、上移、下移

2026-09-24 92 浏览 0 评论 0 字

图层面板是画布编辑器的标配。别小看 图层排序 这几个按钮,一旦背景层、工作区层参与其中,顺序管理就很容易出乱子。这篇文章把置顶/置底/上移/下移的正确写法,以及 保护关键层 的坑写清楚。

一、Fabric 的排序 API

Fabric 对象有 4 个经典层级方法,本质是调整对象在 canvas.getObjects() 数组里的下标:

  • bringToFront() :到最顶层
  • sendToBack() :到最底层
  • bringForward() :上移一层
  • sendBackwards() :下移一层

用法极其简单:

selected.sendToBack();   // 点"置底"
selected.bringToFront(); // 点"置顶"
selected.bringForward(); // 点"上一层"
selected.sendBackwards();// 点"下一层"

二、问题:背景层和工作区层会被误动

如果你的画布里有"工作区(workspace)"和"背景(background)"这种特殊层,它们往往必须固定在最底层或锁定各自顺序。如果用户把某张图"置到底",它可能把 workspace 和 background 又抬到上面来了——因为 sendToBack 会把它放到数组最前面。

解决思路是在每次排序后,把关键层"强制回位":

function sortKeyLayers() {
  if (background) background.sendToBack();   // 背景永远在最底
  if (workspace) workspace.sendToBack();     // 工作区次底
  if (background) background.bringForward(); // 让背景介于 workspace 之下/之上按需
}

规约很清晰: 背景永远垫底,工作区在其上,业务图层在工作区之上 。每次排序后调用一次即可保证关键层不被顶乱。

二、命令的上移/下移要循环处理

bringForward / sendBackwards 一次只移一层。如果要支持"自定义拖拽排序"的效果(把层 A 拖到层 B 的位置),就得计算位移次数并循环:

// 假设把 newIndex 位置的层进行排序
function moveBySteps(layer, step) {
  const step = Math.abs(step);
  for (let i = 0; i < step; i++) {
    if (step > 0) layer.bringForward(); else layer.sendBackwards();
  }
  canvas.requestRenderAll();
}

四、用数组索引直接排序更高效

靠 bringForward 一次次挪,层多时性能堪忧。更稳妥的方法是基于 canvas.getObjects() 数组本身重组,再 canvas._objects.sort (Fabric 内部有私有数组但推荐用公开方式)。若只是几个按钮,循环 acceptable;若是拖拽排序,建议走数组重组。

五、与历史记录联动

排序是可撤销的。所以在完成一次排序后,建议推入历史快照,让"撤销/重做"也能恢复层级:

function pushHistory() {
  if (!here.changeHistory) return;
  const d = canvas.toDatalessJSON();
  // 和时间戳做去重,若与上一笔一致则不重复记录
  const hash = md5(JSON.stringify(d));
  if (history.length && hash === history[history.length - 1].hash) return;
  history.push({ hash, data: d });
  historyIndex = history.length - 1;
}

六、避坑

  1. 置底被关键层"反弹" :见第二节,排序后强制回位关键层。
  2. 多层置顶/置底语义 :多个层同时置底,它们在底层内的相对顺序可能被打乱。处理多选时先按数组下标排序,保证底端相对顺序稳定。
  3. sendBackwards 与梯形关系 :如果有多个互相遮挡的图形,"置底但只压低一层"的诡异表现,多半是 moveTo(index) 用错。 moveTo 目标 index 要基于 getObjects() 下标精确算。
  4. sortable 插件联动 :用 Drag 对层列表改动时,要把 UI 的 newIndex 换算成 moveTo 的下标,否则 UI 顺序和 canvas 顺序不一致。

七、验证

构造 workspace + background + 三个矩形。分别点击"置顶/置底/上移/下移",验证:关键层始终在最底且相对顺序不变、业务层按预期改变遮挡关系、撤销能恢复原顺序。这套逻辑理解透了,层级面板就稳了。

FAQ 常见问题

  • 问:用 sendToBack 置底,background 也跟着跑了怎么办?
    Fabric 的 sendToBack / sendBackwards 会把对象压到最底,可能挤到背景/工作区之下。排序后要再把关键层强制 bringToFront / 重新 moveTo 到它们应有的位置,保证背景不被误伤。

  • 问:多个图层同时"置底",它们底下的相对顺序乱怎么办?
    多选置底前先按 getObjects() 的下标排好序,再逐个下移,这样它们在底层内能保持相对稳定,不会出现 A/B 顺序被翻转。

  • 问:为什么"只置下一层"换个图形就多了好几层?
    各位多半是 moveTo 的目标 index 算错了。 moveTo(index) 的下标要基于 getObjects() 的实时下标精确计算,而不是凭直觉数;否则遮挡关系会错位。


发布评论

发布评论前请先 登录。
0 评论
点赞
收藏

评论列表 0

暂无评论