fix: 背景默认锁定;补 ShapeMenu 的 close 绑定
背景铺满整个画板,不锁的话画布上就没有「空白处」了:想框选、想点空白取消选中, 实际都会抓住背景整个拖走(实测一次误拖就把底图挪出画板)。但也不能做成永久 不可选——用户对背景的核心需求正是「调整/缩放/裁剪一下」。锁定是可见且可撤销的: 图层面板那一行的锁图标点一下就解开,解开后拖拽缩放和普通图层完全一样。 走 layerLocked 而不是直接写 selectable:false,因为锁定态由 applyLocked 统一管, 自己写 selectable 会被 object:added 触发的 registerLayer 翻回去。 TopBar 没把 Dropdown 的 close 传给 ShapeMenu,选完形状下拉不收。 顺带删掉 useEditor.addRect:形状改成拖拽创建之后没有调用方了。
This commit is contained in:
@@ -168,8 +168,8 @@ async function scrollToExport() {
|
||||
</button>
|
||||
</span>
|
||||
</template>
|
||||
<template #panel>
|
||||
<ShapeMenu />
|
||||
<template #panel="{ close }">
|
||||
<ShapeMenu :close="close" />
|
||||
</template>
|
||||
</Dropdown>
|
||||
|
||||
|
||||
@@ -15,11 +15,20 @@ import { capImageSize } from "./imageLimits";
|
||||
* 关键实现约束(都是踩出来的,别再踩):
|
||||
* - `current()` 每次现找,不要缓存引用——`loadFromJSON` 之后画布上是新对象,
|
||||
* 缓存的引用会指向一个已经不在画布上的旧对象。
|
||||
* - **背景和普通图层一样可以点选、拖动、缩放。** 一开始做成了不可点选(防止点空白
|
||||
* 处误选),但用户对背景的核心需求就是「调整 / 缩放 / 裁剪一下」,不可点选就没法
|
||||
* 在画布上取景。要取消选中点画板外面的灰底就行——画布是无限工作区,画板外一大片
|
||||
* 都是空的。(顺带省掉一个坑:`selectable:false` 会被图层面板的解锁按钮
|
||||
* `applyLocked(obj,false)` 翻回 true,得靠「最后再设」这种顺序技巧才压得住。)
|
||||
* - **背景默认锁定(`layerLocked = true`)。**
|
||||
*
|
||||
* 它铺满整个画板,不锁的话画布上就没有「空白处」了:想框选、想点空白取消选中,
|
||||
* 实际都会抓住背景把它整个拖走(2026-09-05 实测,一次误拖就把底图挪出画板)。
|
||||
*
|
||||
* 但也不能做成永久不可选——用户对背景的核心需求正是「调整 / 缩放 / 裁剪一下」。
|
||||
* 锁定是**可见且可撤销**的:图层面板那一行的锁图标点一下就能解开,解开后拖拽
|
||||
* 缩放和普通图层完全一样。
|
||||
*
|
||||
* 走 `layerLocked` 而不是直接写 `selectable: false`,是因为锁定态由
|
||||
* `applyLocked()` 统一管(selectable / evented / lockMovement… 一整套),
|
||||
* 而 `registerLayer()` 每次都会照 `layerLocked` 重设一遍。自己写 `selectable`
|
||||
* 会被 `object:added` 触发的那次 `registerLayer` 翻回去,只能靠「最后再设」
|
||||
* 这类顺序技巧压住,脆。
|
||||
* - 铺满用 `width * scaleX` 而不是 `getScaledWidth()`:后者会把默认 strokeWidth
|
||||
* 也算进去(见 common 的 Fabric 陷阱 3)。
|
||||
*/
|
||||
@@ -107,6 +116,8 @@ export function useBackground() {
|
||||
const capped = capImageSize(img.getElement() as HTMLImageElement);
|
||||
if (capped !== img.getElement()) img.setElement(capped);
|
||||
img.isBackground = true;
|
||||
// 默认锁定,理由见文件头。registerLayer 会照这个标志跑一遍 applyLocked。
|
||||
img.layerLocked = true;
|
||||
fitToCanvas(img);
|
||||
registerLayer(img);
|
||||
c.add(img);
|
||||
@@ -132,6 +143,7 @@ export function useBackground() {
|
||||
fill: color,
|
||||
});
|
||||
rect.isBackground = true;
|
||||
rect.layerLocked = true;
|
||||
registerLayer(rect);
|
||||
c.add(rect);
|
||||
c.sendObjectToBack(rect);
|
||||
|
||||
@@ -328,31 +328,6 @@ function addText(text = "标题"): Textbox {
|
||||
return box;
|
||||
}
|
||||
|
||||
/** 加一个矩形。圆角走 rx/ry,右栏的「圆角」改的就是它。 */
|
||||
function addRect(at?: { x: number; y: number }): Rect {
|
||||
const c = canvas.value;
|
||||
if (!c) throw new Error("画布还没挂载");
|
||||
const w = Math.round(size.value.width * 0.3);
|
||||
const h = Math.round(size.value.height * 0.25);
|
||||
const rect = new Rect({
|
||||
left: at?.x ?? size.value.width / 2,
|
||||
top: at?.y ?? size.value.height / 2,
|
||||
originX: "center",
|
||||
originY: "center",
|
||||
width: w,
|
||||
height: h,
|
||||
fill: "#6ea8fe",
|
||||
rx: 0,
|
||||
ry: 0,
|
||||
});
|
||||
registerLayer(rect);
|
||||
c.add(rect);
|
||||
c.setActiveObject(rect);
|
||||
c.requestRenderAll();
|
||||
bumpRevision();
|
||||
return rect;
|
||||
}
|
||||
|
||||
function getCanvas(): Canvas {
|
||||
const c = canvas.value;
|
||||
if (!c) throw new Error("画布还没挂载");
|
||||
@@ -380,7 +355,6 @@ export function useEditor() {
|
||||
setSize,
|
||||
addImage,
|
||||
addText,
|
||||
addRect,
|
||||
getCanvas,
|
||||
selectedObjects,
|
||||
bumpRevision,
|
||||
|
||||
Reference in New Issue
Block a user