chore: 本轮改造的共享契约与占位——ToolId/ShapeKind/LayerKind 扩充、Dropdown 壳、形状表、三个菜单与两个 composable 的桩
This commit is contained in:
+63
-7
@@ -1,6 +1,17 @@
|
||||
import { Canvas, FabricImage, FabricObject, Rect, Textbox, type TPointerEvent } from "fabric";
|
||||
import {
|
||||
ActiveSelection,
|
||||
Canvas,
|
||||
Ellipse,
|
||||
FabricImage,
|
||||
FabricObject,
|
||||
Line,
|
||||
Polygon,
|
||||
Rect,
|
||||
Textbox,
|
||||
type TPointerEvent,
|
||||
} from "fabric";
|
||||
import { ref, shallowRef } from "vue";
|
||||
import type { CanvasSize, LayerKind, SelectionKind, ToolId } from "@/types";
|
||||
import type { CanvasSize, LayerKind, SelectionKind, ShapeKind, ToolId } from "@/types";
|
||||
import { DEFAULT_SIZE } from "./canvasSize";
|
||||
|
||||
/**
|
||||
@@ -13,7 +24,7 @@ import { DEFAULT_SIZE } from "./canvasSize";
|
||||
*/
|
||||
|
||||
/** 自定义属性要登记在这里,`canvas.toJSON()` 才会带上(fabric v7 的 customProperties 机制)。 */
|
||||
FabricObject.customProperties = ["layerId", "layerName", "layerLocked"];
|
||||
FabricObject.customProperties = ["layerId", "layerName", "layerLocked", "isBackground", "shapeKind"];
|
||||
|
||||
declare module "fabric" {
|
||||
interface FabricObject {
|
||||
@@ -23,6 +34,17 @@ declare module "fabric" {
|
||||
layerName?: string;
|
||||
/** 锁定:不可选中、不可移动。和 fabric 自带的 selectable/evented 联动。 */
|
||||
layerLocked?: boolean;
|
||||
/**
|
||||
* 这是**那一个**专用背景图层。全画布最多一个,永远在最底层,
|
||||
* 换背景是替换它而不是再加一层。归 `useBackground` 管,别处不要自己置位。
|
||||
*/
|
||||
isBackground?: boolean;
|
||||
/**
|
||||
* 形状工具画出来的形状是哪一种。fabric 的类推不出「箭头」——它就是一个
|
||||
* Polygon/Path,和多边形同类;这个字段把用户的意图记下来,图层图标和
|
||||
* 右栏属性都读它。
|
||||
*/
|
||||
shapeKind?: ShapeKind;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +54,8 @@ const selectionKind = ref<SelectionKind>("none");
|
||||
const size = ref<CanvasSize>({ width: DEFAULT_SIZE.width, height: DEFAULT_SIZE.height });
|
||||
/** 当前工具。`select` 是默认态。 */
|
||||
const tool = ref<ToolId>("select");
|
||||
/** 形状工具当前要画哪一种形状。工具栏形状按钮右边的下拉改它。 */
|
||||
const shapeKind = ref<ShapeKind>("rect");
|
||||
/**
|
||||
* 画布内容的版本号。对象增删改、层序变化都会 +1。
|
||||
* 图层面板、属性检查器 watch 它来重新推导自己的视图——
|
||||
@@ -49,23 +73,54 @@ export function ensureLayerId(obj: FabricObject): string {
|
||||
|
||||
export function layerKindOf(obj: FabricObject | null | undefined): LayerKind {
|
||||
if (!obj) return "other";
|
||||
// 背景要先判:它本身是一张 FabricImage 或一个 Rect,光看类型分不出来。
|
||||
if (obj.isBackground) return "background";
|
||||
if (obj.shapeKind) return obj.shapeKind;
|
||||
if (obj instanceof FabricImage) return "image";
|
||||
if (obj instanceof Textbox) return "text";
|
||||
if (obj instanceof Ellipse) return "ellipse";
|
||||
if (obj instanceof Line) return "line";
|
||||
if (obj instanceof Polygon) return "polygon";
|
||||
if (obj instanceof Rect) return "rect";
|
||||
return "other";
|
||||
}
|
||||
|
||||
/** 这个种类算不算「形状」——形状共用同一组外观属性(填充、描边、圆角)。 */
|
||||
export function isShapeKind(kind: LayerKind): boolean {
|
||||
return kind === "rect" || kind === "line" || kind === "arrow" || kind === "ellipse" || kind === "polygon";
|
||||
}
|
||||
|
||||
const KIND_NAME: Record<LayerKind, string> = {
|
||||
background: "背景",
|
||||
image: "图片",
|
||||
text: "文字",
|
||||
rect: "矩形",
|
||||
line: "直线",
|
||||
arrow: "箭头",
|
||||
ellipse: "圆形",
|
||||
polygon: "多边形",
|
||||
other: "对象",
|
||||
};
|
||||
|
||||
export function defaultLayerName(obj: FabricObject): string {
|
||||
const kind = layerKindOf(obj);
|
||||
if (kind === "text") return (obj as Textbox).text?.slice(0, 20) || "文字";
|
||||
if (kind === "image") return "图片";
|
||||
if (kind === "rect") return "矩形";
|
||||
return "对象";
|
||||
return KIND_NAME[kind];
|
||||
}
|
||||
|
||||
/**
|
||||
* 选中态:右栏据此决定显示哪几组属性。
|
||||
*
|
||||
* 五种形状塌成一个 `shape`——它们共用同一组外观属性,右栏没必要为每种各写一份。
|
||||
* 背景图层归到 `image`:它就是一张图,滤镜之类的都适用。
|
||||
*/
|
||||
function kindOf(obj: FabricObject | null | undefined): SelectionKind {
|
||||
if (!obj) return "none";
|
||||
if (obj instanceof ActiveSelection) return "multi";
|
||||
const k = layerKindOf(obj);
|
||||
return k === "other" ? (obj ? "other" : "none") : k === "rect" ? "other" : k;
|
||||
if (k === "background" || k === "image") return "image";
|
||||
if (k === "text") return "text";
|
||||
return isShapeKind(k) ? "shape" : "other";
|
||||
}
|
||||
|
||||
function syncSelection() {
|
||||
@@ -285,6 +340,7 @@ export function useEditor() {
|
||||
selectionKind,
|
||||
size,
|
||||
tool,
|
||||
shapeKind,
|
||||
revision,
|
||||
mount,
|
||||
unmount,
|
||||
|
||||
Reference in New Issue
Block a user