customforge 0.1.0-alpha.2 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +85 -56
- package/LICENSES/NunitoSans-OFL.txt +93 -93
- package/LICENSES/plain-mug-CC-BY-4.0.txt +11 -0
- package/README.md +592 -461
- package/README.zh-CN.md +587 -459
- package/dist/{ProductCustomizer-rMRyWe7L.js → ProductCustomizer-4ytA68eg.js} +869 -143
- package/dist/ProductCustomizer-4ytA68eg.js.map +1 -0
- package/dist/bridge/TextureBridge.d.ts +1 -1
- package/dist/core/api.d.ts +94 -0
- package/dist/core/api.d.ts.map +1 -0
- package/dist/core/config.d.ts +3 -12
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/design.d.ts.map +1 -1
- package/dist/core/types.d.ts +183 -5
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/uv.d.ts +8 -0
- package/dist/core/uv.d.ts.map +1 -0
- package/dist/cup_decal_small_margins.glb +0 -0
- package/dist/customizer/ProductCustomizer.d.ts +98 -9
- package/dist/customizer/ProductCustomizer.d.ts.map +1 -1
- package/dist/editor/DesignEditor.d.ts +118 -10
- package/dist/editor/DesignEditor.d.ts.map +1 -1
- package/dist/editor/uvBounds.d.ts +15 -0
- package/dist/editor/uvBounds.d.ts.map +1 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/style.css +2120 -1327
- package/dist/viewer/ProductViewer.d.ts +37 -6
- package/dist/viewer/ProductViewer.d.ts.map +1 -1
- package/dist/viewer/uvLayout.d.ts +11 -0
- package/dist/viewer/uvLayout.d.ts.map +1 -0
- package/dist/workbench/CustomForgeWorkbench.d.ts +85 -8
- package/dist/workbench/CustomForgeWorkbench.d.ts.map +1 -1
- package/dist/workbench/config.d.ts +42 -1
- package/dist/workbench/config.d.ts.map +1 -1
- package/dist/workbench/icons.d.ts.map +1 -1
- package/dist/workbench/index.d.ts +3 -3
- package/dist/workbench/index.d.ts.map +1 -1
- package/dist/workbench/template.d.ts.map +1 -1
- package/dist/workbench/types.d.ts +261 -13
- package/dist/workbench/types.d.ts.map +1 -1
- package/dist/workbench.js +1248 -116
- package/dist/workbench.js.map +1 -1
- package/package.json +27 -20
- package/dist/ProductCustomizer-rMRyWe7L.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProductCustomizer-4ytA68eg.js","names":[],"sources":["../src/core/config.ts","../src/core/dom.ts","../src/core/design.ts","../src/editor/DesignHistory.ts","../src/editor/objectBounds.ts","../src/editor/uvBounds.ts","../src/editor/imageSource.ts","../src/editor/DesignEditor.ts","../src/bridge/TextureBridge.ts","../src/viewer/assets/cup_decal_small_margins.glb?url&no-inline","../src/viewer/uvLayout.ts","../src/viewer/ProductViewer.ts","../src/customizer/ProductCustomizer.ts"],"sourcesContent":["import type {\n ProductConfiguration,\n ResolvedProductConfiguration,\n} from './types'\n\n/** 默认接收实时纹理的 Mesh 名称 */\nexport const DEFAULT_SURFACE_MESH = 'PrintArea'\n\n/**\n * 完成默认值补全后的内部产品配置\n */\nexport type NormalizedProductConfiguration = ResolvedProductConfiguration\n\n/**\n * 清理产品配置并补全运行所需的默认值\n *\n * 远程模型和随包提供的默认 GLB 模型均默认不翻转纹理\n *\n * @param product 外部传入的产品配置\n * @returns 可以直接交给编辑器和查看器使用的完整配置\n */\nexport function normalizeProductConfiguration(\n product: ProductConfiguration = {},\n): NormalizedProductConfiguration {\n const modelUrl = product.modelUrl?.trim() || undefined\n const textureUrl = product.textureUrl?.trim() || undefined\n const surfaceMesh = product.surfaceMesh?.trim() || DEFAULT_SURFACE_MESH\n\n return {\n modelUrl,\n textureUrl,\n surfaceMesh,\n textureFlipY: product.textureFlipY ?? false,\n }\n}\n","import type { ElementTarget } from './types'\n\n/**\n * 将 HTMLElement 或 CSS 选择器解析为挂载容器\n *\n * @param target DOM 元素或 CSS 选择器\n * @param label 错误消息中使用的容器名称\n * @returns 解析得到的 DOM 元素\n * @throws CSS 选择器无法找到对应元素时抛出错误\n */\nexport function resolveElement(target: ElementTarget, label: string): HTMLElement {\n if (target instanceof HTMLElement) {\n return target\n }\n\n const element = document.querySelector<HTMLElement>(target)\n if (!element) {\n throw new Error(`${label} element was not found: ${target}`)\n }\n\n return element\n}\n","import type {\n DesignImageRole,\n DesignDocument,\n DesignObject,\n DesignObjectState,\n DesignObjectTransform,\n TextAlignment,\n TextFontStyle,\n TextFontWeight,\n} from './types'\n\n/** 当前支持的 Design JSON Schema 版本 */\nexport const DESIGN_SCHEMA_VERSION = 1\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction readRecord(value: unknown, path: string): Record<string, unknown> {\n if (!isRecord(value)) {\n throw new TypeError(`${path} must be an object`)\n }\n return value\n}\n\nfunction readString(\n value: unknown,\n path: string,\n allowEmpty = false,\n): string {\n if (typeof value !== 'string' || (!allowEmpty && value.trim().length === 0)) {\n throw new TypeError(`${path} must be ${allowEmpty ? 'a string' : 'a non-empty string'}`)\n }\n return value\n}\n\nfunction readFiniteNumber(value: unknown, path: string): number {\n if (typeof value !== 'number' || !Number.isFinite(value)) {\n throw new TypeError(`${path} must be a finite number`)\n }\n return value\n}\n\nfunction readPositiveNumber(value: unknown, path: string): number {\n const number = readFiniteNumber(value, path)\n if (number <= 0) {\n throw new TypeError(`${path} must be greater than zero`)\n }\n return number\n}\n\nfunction readBoolean(value: unknown, path: string): boolean {\n if (typeof value !== 'boolean') {\n throw new TypeError(`${path} must be a boolean`)\n }\n return value\n}\n\nfunction readImageRole(value: unknown, path: string): DesignImageRole {\n if (value !== 'element' && value !== 'background') {\n throw new TypeError(`${path} must be element or background`)\n }\n return value\n}\n\nfunction readTextAlignment(value: unknown, path: string): TextAlignment {\n if (value !== 'left' && value !== 'center' && value !== 'right') {\n throw new TypeError(`${path} must be left, center, or right`)\n }\n return value\n}\n\nfunction readTextFontStyle(value: unknown, path: string): TextFontStyle {\n if (value !== 'normal' && value !== 'italic') {\n throw new TypeError(`${path} must be normal or italic`)\n }\n return value\n}\n\nfunction readTextFontWeight(value: unknown, path: string): TextFontWeight {\n if (value === 'normal' || value === 'bold') {\n return value\n }\n const weight = readFiniteNumber(value, path)\n if (weight <= 0 || weight > 1000) {\n throw new TypeError(`${path} must be between 1 and 1000`)\n }\n return weight\n}\n\nfunction parseObjectState(\n object: Record<string, unknown>,\n path: string,\n): DesignObjectState {\n return {\n ...(object.name === undefined\n ? {}\n : { name: readString(object.name, `${path}.name`).trim() }),\n visible:\n object.visible === undefined\n ? true\n : readBoolean(object.visible, `${path}.visible`),\n locked:\n object.locked === undefined\n ? false\n : readBoolean(object.locked, `${path}.locked`),\n }\n}\n\nfunction parseTransform(value: unknown, path: string): DesignObjectTransform {\n const transform = readRecord(value, path)\n return {\n x: readFiniteNumber(transform.x, `${path}.x`),\n y: readFiniteNumber(transform.y, `${path}.y`),\n scaleX: readPositiveNumber(transform.scaleX, `${path}.scaleX`),\n scaleY: readPositiveNumber(transform.scaleY, `${path}.scaleY`),\n rotation: readFiniteNumber(transform.rotation, `${path}.rotation`),\n flipX: readBoolean(transform.flipX, `${path}.flipX`),\n flipY: readBoolean(transform.flipY, `${path}.flipY`),\n }\n}\n\nfunction parseObject(value: unknown, index: number): DesignObject {\n const path = `design.objects[${index}]`\n const object = readRecord(value, path)\n const id = readString(object.id, `${path}.id`)\n const transform = parseTransform(object.transform, `${path}.transform`)\n const state = parseObjectState(object, path)\n\n if (object.type === 'text') {\n return {\n id,\n type: 'text',\n ...state,\n transform,\n text: readString(object.text, `${path}.text`, true),\n width: readPositiveNumber(object.width, `${path}.width`),\n fontFamily: readString(object.fontFamily, `${path}.fontFamily`),\n fontSize: readPositiveNumber(object.fontSize, `${path}.fontSize`),\n color: readString(object.color, `${path}.color`),\n ...(object.fontWeight === undefined\n ? {}\n : {\n fontWeight: readTextFontWeight(\n object.fontWeight,\n `${path}.fontWeight`,\n ),\n }),\n ...(object.fontStyle === undefined\n ? {}\n : {\n fontStyle: readTextFontStyle(\n object.fontStyle,\n `${path}.fontStyle`,\n ),\n }),\n ...(object.underline === undefined\n ? {}\n : { underline: readBoolean(object.underline, `${path}.underline`) }),\n ...(object.textAlign === undefined\n ? {}\n : {\n textAlign: readTextAlignment(\n object.textAlign,\n `${path}.textAlign`,\n ),\n }),\n ...(object.lineHeight === undefined\n ? {}\n : {\n lineHeight: readPositiveNumber(\n object.lineHeight,\n `${path}.lineHeight`,\n ),\n }),\n ...(object.charSpacing === undefined\n ? {}\n : {\n charSpacing: readFiniteNumber(\n object.charSpacing,\n `${path}.charSpacing`,\n ),\n }),\n ...(object.backgroundColor === undefined\n ? {}\n : {\n backgroundColor: readString(\n object.backgroundColor,\n `${path}.backgroundColor`,\n ),\n }),\n }\n }\n\n if (object.type === 'image') {\n const src = readString(object.src, `${path}.src`)\n if (src.startsWith('blob:')) {\n throw new TypeError(`${path}.src must not use a Blob URL`)\n }\n return {\n id,\n type: 'image',\n ...state,\n transform,\n src,\n ...(object.role === undefined\n ? {}\n : { role: readImageRole(object.role, `${path}.role`) }),\n }\n }\n\n throw new TypeError(`${path}.type is not supported`)\n}\n\n/**\n * 校验并净化外部 Design JSON\n *\n * @param value JSON.parse 结果或其他未知输入\n * @returns 只包含当前 Schema 字段的新设计文档\n * @throws 文档版本、字段类型、对象 ID 或图片来源不符合契约时抛出 TypeError\n */\nexport function parseDesignDocument(value: unknown): DesignDocument {\n const design = readRecord(value, 'design')\n if (design.version !== DESIGN_SCHEMA_VERSION) {\n throw new TypeError(`design.version must be ${DESIGN_SCHEMA_VERSION}`)\n }\n\n const canvas = readRecord(design.canvas, 'design.canvas')\n if (!Array.isArray(design.objects)) {\n throw new TypeError('design.objects must be an array')\n }\n\n const objects = design.objects.map(parseObject)\n const ids = new Set<string>()\n let backgroundCount = 0\n for (const object of objects) {\n if (ids.has(object.id)) {\n throw new TypeError(`design object id is duplicated: ${object.id}`)\n }\n ids.add(object.id)\n if (object.type === 'image' && object.role === 'background') {\n backgroundCount += 1\n if (backgroundCount > 1) {\n throw new TypeError('design must not contain more than one background')\n }\n if (objects.indexOf(object) !== 0) {\n throw new TypeError('design background must be the first object')\n }\n }\n }\n\n return {\n version: DESIGN_SCHEMA_VERSION,\n canvas: {\n width: readPositiveNumber(canvas.width, 'design.canvas.width'),\n height: readPositiveNumber(canvas.height, 'design.canvas.height'),\n },\n objects,\n }\n}\n","import type { HistoryState } from '../core/types'\n\n/** 固定容量的设计快照历史 */\nexport class DesignHistory<T> {\n private entries: T[]\n private index = 0\n\n /**\n * @param initial 初始设计快照\n * @param limit 最多保留的撤销步骤数量\n */\n constructor(initial: T, private readonly limit: number) {\n if (!Number.isInteger(limit) || limit < 1) {\n throw new RangeError('History limit must be a positive integer')\n }\n this.entries = [initial]\n }\n\n /** 当前撤销与重做可用状态 */\n get state(): HistoryState {\n return {\n canUndo: this.index > 0,\n canRedo: this.index < this.entries.length - 1,\n }\n }\n\n /** 当前步骤之前的快照 */\n peekUndo(): T | undefined {\n return this.entries[this.index - 1]\n }\n\n /** 当前步骤之后的快照 */\n peekRedo(): T | undefined {\n return this.entries[this.index + 1]\n }\n\n /** 添加新快照并丢弃当前步骤之后的重做分支 */\n push(snapshot: T): void {\n this.entries.splice(this.index + 1)\n this.entries.push(snapshot)\n if (this.entries.length > this.limit + 1) {\n this.entries.splice(0, this.entries.length - this.limit - 1)\n }\n this.index = this.entries.length - 1\n }\n\n /** 在目标快照成功恢复后确认一次撤销 */\n confirmUndo(): void {\n if (this.index > 0) {\n this.index -= 1\n }\n }\n\n /** 在目标快照成功恢复后确认一次重做 */\n confirmRedo(): void {\n if (this.index < this.entries.length - 1) {\n this.index += 1\n }\n }\n\n /** 以当前快照重新开始历史记录 */\n reset(snapshot: T): void {\n this.entries = [snapshot]\n this.index = 0\n }\n}\n","/** 参与画布边界计算的轴对齐包围盒 */\nexport interface ObjectBounds {\n /** 包围盒左边缘的画布横坐标 */\n left: number\n\n /** 包围盒上边缘的画布纵坐标 */\n top: number\n\n /** 包围盒宽度 */\n width: number\n\n /** 包围盒高度 */\n height: number\n}\n\n/** 将对象包围盒移回画布所需的偏移量 */\nexport interface ContainmentOffset {\n /** 应加到对象横坐标的偏移量 */\n x: number\n\n /** 应加到对象纵坐标的偏移量 */\n y: number\n}\n\n/**\n * 计算对象完整进入画布所需的最大等比缩放系数\n *\n * @param bounds 对象当前的轴对齐包围盒\n * @param canvasWidth 画布逻辑宽度\n * @param canvasHeight 画布逻辑高度\n * @returns 不大于 1 的缩放系数,对象已经可容纳时返回 1\n */\nexport function calculateContainmentScale(\n bounds: ObjectBounds,\n canvasWidth: number,\n canvasHeight: number,\n): number {\n const widthScale = bounds.width > canvasWidth ? canvasWidth / bounds.width : 1\n const heightScale = bounds.height > canvasHeight ? canvasHeight / bounds.height : 1\n return Math.min(widthScale, heightScale, 1)\n}\n\n/**\n * 计算可容纳对象移回画布所需的平移距离\n *\n * 调用前应先确保包围盒不大于画布,否则无法同时满足两侧边界\n *\n * @param bounds 对象当前的轴对齐包围盒\n * @param canvasWidth 画布逻辑宽度\n * @param canvasHeight 画布逻辑高度\n * @returns 应叠加到对象位置的画布坐标偏移量\n */\nexport function calculateContainmentOffset(\n bounds: ObjectBounds,\n canvasWidth: number,\n canvasHeight: number,\n): ContainmentOffset {\n const x =\n bounds.left < 0\n ? -bounds.left\n : Math.min(0, canvasWidth - bounds.left - bounds.width)\n const y =\n bounds.top < 0\n ? -bounds.top\n : Math.min(0, canvasHeight - bounds.top - bounds.height)\n\n return { x, y }\n}\n","import type { UvLayout } from '../core/uv'\nimport type { ObjectBounds } from './objectBounds'\n\nfunction fullCanvasBounds(width: number, height: number): ObjectBounds {\n return { left: 0, top: 0, width, height }\n}\n\n/**\n * 将 UV 三角形的坐标范围转换为逻辑画布包围框\n *\n * UV 坐标会限制在 0 到 1 之间;布局为空、无效或退化时回退到整张画布\n *\n * @param layout 当前可打印 Mesh 的 UV 布局\n * @param flipY 是否按照垂直翻转后的纹理方向计算\n * @param canvasWidth 逻辑画布宽度\n * @param canvasHeight 逻辑画布高度\n * @returns 当前 UV 可打印区域在逻辑画布中的轴对齐包围框\n */\nexport function calculateUvCanvasBounds(\n layout: UvLayout,\n flipY: boolean,\n canvasWidth: number,\n canvasHeight: number,\n): ObjectBounds {\n const coordinates = layout.triangleCoordinates\n let minimumX = Number.POSITIVE_INFINITY\n let minimumY = Number.POSITIVE_INFINITY\n let maximumX = Number.NEGATIVE_INFINITY\n let maximumY = Number.NEGATIVE_INFINITY\n\n for (let index = 0; index + 1 < coordinates.length; index += 2) {\n const u = coordinates[index]\n const v = coordinates[index + 1]\n if (!Number.isFinite(u) || !Number.isFinite(v)) {\n continue\n }\n\n const x = Math.min(Math.max(u, 0), 1) * canvasWidth\n const normalizedY = flipY ? 1 - v : v\n const y = Math.min(Math.max(normalizedY, 0), 1) * canvasHeight\n minimumX = Math.min(minimumX, x)\n minimumY = Math.min(minimumY, y)\n maximumX = Math.max(maximumX, x)\n maximumY = Math.max(maximumY, y)\n }\n\n const width = maximumX - minimumX\n const height = maximumY - minimumY\n if (\n !Number.isFinite(width) ||\n !Number.isFinite(height) ||\n width <= 0 ||\n height <= 0\n ) {\n return fullCanvasBounds(canvasWidth, canvasHeight)\n }\n\n return { left: minimumX, top: minimumY, width, height }\n}\n","function readBlobAsDataUrl(blob: Blob): Promise<string> {\n return new Promise((resolve, reject) => {\n const reader = new FileReader()\n reader.addEventListener('load', () => {\n if (typeof reader.result === 'string') {\n resolve(reader.result)\n } else {\n reject(new Error('Blob image could not be converted to a Data URL'))\n }\n })\n reader.addEventListener('error', () => {\n reject(reader.error ?? new Error('Blob image could not be read'))\n })\n reader.readAsDataURL(blob)\n })\n}\n\n/**\n * 将短生命周期 Blob URL 转换为可写入 Design JSON 的 Data URL\n *\n * 远程 URL 和已有 Data URL 会保持不变\n *\n * @param src 图片来源\n * @returns 可跨页面会话重新加载的图片来源\n * @throws Blob URL 已失效或浏览器无法读取对应 Blob 时抛出错误\n */\nexport async function resolvePersistentImageSource(src: string): Promise<string> {\n if (!src.startsWith('blob:')) {\n return src\n }\n\n const response = await fetch(src)\n if (!response.ok) {\n throw new Error(`Blob image could not be loaded: ${response.status}`)\n }\n return readBlobAsDataUrl(await response.blob())\n}\n","import {\n ActiveSelection,\n Canvas,\n FabricImage,\n Point,\n Textbox,\n type FabricObject,\n} from 'fabric'\nimport { parseDesignDocument } from '../core/design'\nimport type {\n AddImageOptions,\n AddTextOptions,\n CanvasBounds,\n DesignCanvas as DesignCanvasSize,\n DesignImageRole,\n DesignDocument,\n DesignObject,\n DesignObjectTransform,\n EditorAppearance,\n HistoryState,\n ImageDesignObject,\n TextAlignment,\n TextDesignObject,\n TextFontStyle,\n TextFontWeight,\n UpdateObjectTransformOptions,\n UpdateTextOptions,\n} from '../core/types'\nimport type { UvLayout } from '../core/uv'\nimport { DesignHistory } from './DesignHistory'\nimport {\n calculateContainmentOffset,\n calculateContainmentScale,\n} from './objectBounds'\nimport { calculateUvCanvasBounds } from './uvBounds'\nimport { resolvePersistentImageSource } from './imageSource'\n\nconst EDITOR_DISPLAY_GUTTER = 56\n\ninterface NormalizedEditorAppearance {\n selectionFill: string\n selectionBorder: string\n controlFill: string\n controlBorder: string\n objectBorder: string\n controlSize: number\n uvFill?: string\n uvBoundary: string\n}\n\nfunction normalizeEditorAppearance(\n appearance: EditorAppearance = {},\n): NormalizedEditorAppearance {\n const controlSize = appearance.controlSize ?? 8\n if (!Number.isFinite(controlSize) || controlSize <= 0) {\n throw new RangeError('appearance.editor.controlSize must be greater than zero')\n }\n\n return {\n selectionFill: appearance.selectionFill?.trim() || 'rgba(19, 113, 125, 0.12)',\n selectionBorder: appearance.selectionBorder?.trim() || '#13717d',\n controlFill: appearance.controlFill?.trim() || '#ffffff',\n controlBorder: appearance.controlBorder?.trim() || '#13717d',\n objectBorder: appearance.objectBorder?.trim() || '#13717d',\n controlSize,\n uvFill: appearance.uvFill?.trim() || undefined,\n uvBoundary: appearance.uvBoundary?.trim() || '#dc2626',\n }\n}\n\n/** 二维编辑器内部初始化配置 */\ninterface DesignEditorOptions {\n /** 画布逻辑宽度,单位为像素 */\n width: number\n\n /** 画布逻辑高度,单位为像素 */\n height: number\n\n /** 最多保留的撤销步骤数量 */\n historyLimit: number\n\n /** 编辑画布的无障碍名称 */\n ariaLabel?: string\n\n /** 未提供内容时添加到画布的默认文字 */\n defaultText?: string\n\n /** 空文字对象的默认名称 */\n textObjectName?: string\n\n /** 普通图片对象的默认名称 */\n imageObjectName?: string\n\n /** 背景图片对象的默认名称 */\n backgroundObjectName?: string\n\n /** 选择框、对象控制点和 UV 辅助层外观 */\n appearance?: EditorAppearance\n}\n\ntype RenderListener = () => void\ntype SelectionListener = (objectIds: string[]) => void\ntype HistoryListener = (state: HistoryState) => void\n\n/** 将 Fabric 主画布重绘为不含选择框的实时纹理 */\nclass DesignCanvas extends Canvas {\n /** 将纯设计内容绘制到稳定的纹理 Canvas */\n renderTexture(context: CanvasRenderingContext2D): void {\n const previousSkipControlsDrawing = this.skipControlsDrawing\n this.skipControlsDrawing = true\n\n try {\n this.renderCanvas(context, this.getObjects())\n } finally {\n this.skipControlsDrawing = previousSkipControlsDrawing\n }\n }\n}\n\n/**\n * 基于 Fabric.js 的二维纹理编辑器\n *\n * 负责基础纹理、UV 区域辅助层、文字、图片、对象选择和 PNG 导出\n * 显示尺寸可以响应容器变化,内部逻辑尺寸保持不变\n */\nexport class DesignEditor {\n /** 当前实例使用的 Fabric Canvas */\n readonly canvas: Canvas\n\n private readonly host: HTMLElement\n private readonly width: number\n private readonly height: number\n private readonly defaultText: string\n private readonly textObjectName: string\n private readonly imageObjectName: string\n private readonly backgroundObjectName: string\n private readonly appearance: NormalizedEditorAppearance\n private editableBounds: CanvasBounds\n private readonly renderListeners = new Set<RenderListener>()\n private readonly selectionListeners = new Set<SelectionListener>()\n private readonly historyListeners = new Set<HistoryListener>()\n private readonly objectIds = new WeakMap<FabricObject, string>()\n private readonly objectsById = new Map<string, FabricObject>()\n private readonly objectNames = new WeakMap<FabricObject, string>()\n private readonly objectLocks = new WeakMap<FabricObject, boolean>()\n private readonly usedObjectIds = new Set<string>()\n private readonly imageSources = new WeakMap<FabricImage, string>()\n private readonly imageRoles = new WeakMap<FabricImage, DesignImageRole>()\n private readonly resizeObserver: ResizeObserver\n private readonly history: DesignHistory<DesignDocument>\n private readonly uvOverlay: HTMLCanvasElement\n private readonly textureRenderer: DesignCanvas\n private readonly textureCanvasElement: HTMLCanvasElement\n private readonly textureContext: CanvasRenderingContext2D\n private historySignature: string\n private historyTimer?: ReturnType<typeof setTimeout>\n private historyBusy = false\n private renderingTexture = false\n private objectIdSequence = 0\n\n /**\n * @param host 二维编辑器挂载容器\n * @param options 画布逻辑尺寸和历史容量\n */\n constructor(host: HTMLElement, options: DesignEditorOptions) {\n this.host = host\n this.width = options.width\n this.height = options.height\n this.defaultText = options.defaultText ?? 'Edit this text'\n this.textObjectName = options.textObjectName ?? 'Text'\n this.imageObjectName = options.imageObjectName ?? 'Image'\n this.backgroundObjectName = options.backgroundObjectName ?? 'Background'\n this.appearance = normalizeEditorAppearance(options.appearance)\n this.editableBounds = {\n left: 0,\n top: 0,\n width: this.width,\n height: this.height,\n }\n\n const element = document.createElement('canvas')\n element.setAttribute('aria-label', options.ariaLabel ?? 'UV texture editor')\n host.replaceChildren(element)\n\n const canvas = new DesignCanvas(element, {\n width: this.width,\n height: this.height,\n preserveObjectStacking: true,\n selectionColor: this.appearance.selectionFill,\n selectionBorderColor: this.appearance.selectionBorder,\n })\n this.canvas = canvas\n this.textureRenderer = canvas\n this.textureCanvasElement = document.createElement('canvas')\n this.textureCanvasElement.width = this.width\n this.textureCanvasElement.height = this.height\n const textureContext = this.textureCanvasElement.getContext('2d')\n if (!textureContext) {\n canvas.dispose()\n throw new Error('Unable to create the live texture canvas')\n }\n this.textureContext = textureContext\n const initialDesign = this.saveDesign()\n this.history = new DesignHistory(initialDesign, options.historyLimit)\n this.historySignature = JSON.stringify(initialDesign)\n\n this.canvas.wrapperEl.classList.add('customforge-design-canvas')\n this.uvOverlay = document.createElement('canvas')\n this.uvOverlay.width = this.width\n this.uvOverlay.height = this.height\n this.uvOverlay.className = 'customforge-uv-layout'\n this.uvOverlay.setAttribute('aria-hidden', 'true')\n this.uvOverlay.hidden = true\n this.canvas.wrapperEl.insertBefore(\n this.uvOverlay,\n this.canvas.upperCanvasEl,\n )\n this.host.dataset.renderState = 'pending'\n this.canvas.on('after:render', () => {\n if (this.renderingTexture) {\n return\n }\n\n this.renderTextureCanvas()\n this.markRenderState()\n this.renderListeners.forEach((listener) => listener())\n })\n\n const notifySelection = () => this.notifySelectionChange()\n\n this.canvas.on('selection:created', notifySelection)\n this.canvas.on('selection:updated', notifySelection)\n this.canvas.on('selection:cleared', notifySelection)\n\n const constrainTarget = ({ target }: { target: FabricObject }) => {\n this.constrainObjectToCanvas(target)\n }\n this.canvas.on('object:moving', constrainTarget)\n this.canvas.on('object:scaling', constrainTarget)\n this.canvas.on('object:rotating', constrainTarget)\n this.canvas.on('object:skewing', constrainTarget)\n this.canvas.on('object:resizing', constrainTarget)\n this.canvas.on('object:modified', ({ target }) => {\n this.constrainObjectToCanvas(target)\n this.commitHistory()\n })\n this.canvas.on('text:changed', ({ target }) => {\n this.constrainObjectToCanvas(target)\n this.scheduleHistoryCommit()\n })\n\n this.resizeObserver = new ResizeObserver(() => this.resizeDisplay())\n this.resizeObserver.observe(host)\n this.resizeDisplay()\n }\n\n /** 供 Three.js 创建 CanvasTexture 的纯设计画布,不包含选择框和变换控件 */\n get textureCanvas(): HTMLCanvasElement {\n return this.textureCanvasElement\n }\n\n /**\n * 在交互画布上显示目标 Mesh 的 UV 可打印区域,但不写入实时纹理或导出图片\n *\n * @param layout 从当前目标 Mesh 提取的 UV 布局\n * @param flipY 是否按照垂直翻转后的纹理方向显示\n */\n setUvLayout(layout: UvLayout, flipY: boolean): void {\n this.editableBounds = calculateUvCanvasBounds(\n layout,\n flipY,\n this.width,\n this.height,\n )\n const context = this.uvOverlay.getContext('2d')\n if (!context) {\n return\n }\n\n context.clearRect(0, 0, this.width, this.height)\n const { triangleCoordinates, boundaryCoordinates } = layout\n if (triangleCoordinates.length === 0) {\n this.uvOverlay.hidden = true\n return\n }\n\n this.uvOverlay.hidden = false\n const accent = this.appearance.uvFill ??\n (getComputedStyle(this.canvas.wrapperEl)\n .getPropertyValue('--cfw-accent')\n .trim() || '#268a4b')\n const canvasX = (u: number) =>\n Math.min(Math.max(u * this.width, 1), this.width - 1)\n const canvasY = (v: number) =>\n Math.min(\n Math.max((flipY ? 1 - v : v) * this.height, 1),\n this.height - 1,\n )\n\n context.save()\n context.beginPath()\n for (let index = 0; index < triangleCoordinates.length; index += 6) {\n context.moveTo(\n canvasX(triangleCoordinates[index]),\n canvasY(triangleCoordinates[index + 1]),\n )\n context.lineTo(\n canvasX(triangleCoordinates[index + 2]),\n canvasY(triangleCoordinates[index + 3]),\n )\n context.lineTo(\n canvasX(triangleCoordinates[index + 4]),\n canvasY(triangleCoordinates[index + 5]),\n )\n context.closePath()\n }\n context.fillStyle = accent\n context.globalAlpha = 0.012\n context.fill()\n\n context.beginPath()\n for (let index = 0; index < boundaryCoordinates.length; index += 4) {\n context.moveTo(\n canvasX(boundaryCoordinates[index]),\n canvasY(boundaryCoordinates[index + 1]),\n )\n context.lineTo(\n canvasX(boundaryCoordinates[index + 2]),\n canvasY(boundaryCoordinates[index + 3]),\n )\n }\n context.globalAlpha = 0.28\n context.strokeStyle = this.appearance.uvBoundary\n context.lineWidth = 1\n context.lineCap = 'round'\n context.lineJoin = 'round'\n context.setLineDash([6, 6])\n context.stroke()\n context.restore()\n }\n\n /** 当前画布中设计对象的数量,不包含产品基础纹理 */\n get objectCount(): number {\n return this.canvas.getObjects().length\n }\n\n /**\n * 订阅 Fabric Canvas 完成渲染事件\n *\n * @param listener 每次画布完成渲染后调用的函数\n * @returns 用于取消本次订阅的函数\n */\n onRender(listener: RenderListener): () => void {\n this.renderListeners.add(listener)\n return () => this.renderListeners.delete(listener)\n }\n\n /**\n * 订阅画布选中状态变化\n *\n * @param listener 接收当前选中对象 ID 的函数\n * @returns 用于取消本次订阅的函数\n */\n onSelectionChange(listener: SelectionListener): () => void {\n this.selectionListeners.add(listener)\n return () => this.selectionListeners.delete(listener)\n }\n\n /**\n * 订阅撤销与重做可用状态变化\n *\n * @param listener 接收最新历史状态的函数\n * @returns 用于取消本次订阅的函数\n */\n onHistoryChange(listener: HistoryListener): () => void {\n this.historyListeners.add(listener)\n return () => this.historyListeners.delete(listener)\n }\n\n /** 当前是否存在可以撤销的设计快照 */\n get canUndo(): boolean {\n return this.history.state.canUndo\n }\n\n /** 当前是否存在可以重做的设计快照 */\n get canRedo(): boolean {\n return this.history.state.canRedo\n }\n\n /**\n * 返回当前对象的独立 Design JSON 快照\n *\n * @returns 按画布层级从后到前排列的对象数组\n */\n getObjects(): DesignObject[] {\n return this.canvas.getObjects().map((object) => this.serializeObject(object))\n }\n\n /** 返回当前逻辑画布尺寸 */\n getCanvasSize(): DesignCanvasSize {\n return { width: this.width, height: this.height }\n }\n\n /** 返回当前 UV 可打印区域的独立包围框 */\n getPrintableBounds(): CanvasBounds {\n return { ...this.editableBounds }\n }\n\n /** 当前选中对象的 ID,按画布层级从后到前排列 */\n getSelectedObjectIds(): string[] {\n return this.canvas\n .getActiveObjects()\n .map((object) => this.objectIds.get(object))\n .filter((id): id is string => Boolean(id))\n }\n\n /**\n * 按稳定 ID 选中一个可见对象\n *\n * @param id Design JSON 中的对象 ID\n * @returns 是否找到并选中了对象\n */\n selectObject(id: string): boolean {\n return this.selectObjects([id])\n }\n\n /**\n * 按稳定 ID 同时选中多个可见对象\n *\n * 所有 ID 都必须有效且对象可见,否则保持原选区不变\n *\n * @param ids 对象 ID,空数组表示清除选区\n * @returns 是否应用了请求的选区\n */\n selectObjects(ids: readonly string[]): boolean {\n const uniqueIds = [...new Set(ids)]\n if (uniqueIds.length === 0) {\n return this.clearSelection()\n }\n\n const objects = uniqueIds.map((id) => this.objectsById.get(id))\n if (objects.some((object) => !object || !object.visible)) {\n return false\n }\n\n this.applySelection(objects as FabricObject[])\n this.canvas.requestRenderAll()\n this.notifySelectionChange()\n return true\n }\n\n /**\n * 清除当前画布选区,不修改设计内容或历史记录\n *\n * @returns 清除前是否存在选中对象\n */\n clearSelection(): boolean {\n if (!this.canvas.getActiveObject()) {\n return false\n }\n\n this.canvas.discardActiveObject()\n this.canvas.requestRenderAll()\n this.notifySelectionChange()\n return true\n }\n\n /**\n * 按稳定 ID 删除一个对象\n *\n * @param id Design JSON 中的对象 ID\n * @returns 是否找到并删除了对象\n */\n removeObject(id: string): boolean {\n this.flushHistoryCommit()\n const object = this.objectsById.get(id)\n if (!object) {\n return false\n }\n\n if (this.canvas.getActiveObjects().includes(object)) {\n this.canvas.discardActiveObject()\n }\n this.canvas.remove(object)\n this.unregisterObject(object)\n object.dispose()\n this.canvas.requestRenderAll()\n this.notifySelectionChange()\n this.commitHistory()\n return true\n }\n\n /**\n * 将对象移动到指定图层索引\n *\n * @param id Design JSON 中的对象 ID\n * @param index 从 0 开始的索引,0 表示最底层\n * @returns 对象层级是否发生变化\n */\n moveObject(id: string, index: number): boolean {\n this.flushHistoryCommit()\n const object = this.objectsById.get(id)\n const objects = this.canvas.getObjects()\n if (!object || !Number.isInteger(index) || objects.length === 0) {\n return false\n }\n\n const currentIndex = objects.indexOf(object)\n const role = object instanceof FabricImage\n ? this.imageRoles.get(object) ?? 'element'\n : 'element'\n const backgroundCount = objects.filter(\n (entry) => entry instanceof FabricImage && this.imageRoles.get(entry) === 'background',\n ).length\n const minimumIndex = role === 'background' ? 0 : backgroundCount\n const maximumIndex = role === 'background' ? 0 : objects.length - 1\n const nextIndex = Math.min(Math.max(index, minimumIndex), maximumIndex)\n if (currentIndex === nextIndex) {\n return false\n }\n\n this.canvas.moveObjectTo(object, nextIndex)\n this.canvas.requestRenderAll()\n this.commitHistory()\n return true\n }\n\n /**\n * 将现有图片转换为铺满当前 UV 可打印区域的设计背景\n *\n * 转换会替换已有设计背景、重置旋转、锁定对象并移动到最底层\n *\n * @param id Design JSON 中的图片对象 ID\n * @returns 是否找到普通图片并完成转换\n */\n setImageAsBackground(id: string): boolean {\n this.flushHistoryCommit()\n const object = this.objectsById.get(id)\n if (\n !(object instanceof FabricImage) ||\n this.imageRoles.get(object) === 'background'\n ) {\n return false\n }\n\n this.removeDesignBackgrounds()\n this.imageRoles.set(object, 'background')\n this.fitImageToEditableBounds(object)\n this.applyObjectLock(object, true)\n this.canvas.moveObjectTo(object, 0)\n this.canvas.setActiveObject(object)\n this.canvas.requestRenderAll()\n this.notifySelectionChange()\n this.commitHistory()\n return true\n }\n\n /**\n * 修改对象在图层面板中的名称\n *\n * @param id Design JSON 中的对象 ID\n * @param name 非空图层名称\n * @returns 是否找到并更新了对象\n */\n renameObject(id: string, name: string): boolean {\n this.flushHistoryCommit()\n const object = this.objectsById.get(id)\n const normalizedName = name.trim()\n if (!object || !normalizedName) {\n return false\n }\n\n this.objectNames.set(object, normalizedName)\n this.canvas.requestRenderAll()\n this.commitHistory()\n return true\n }\n\n /**\n * 修改对象是否参与渲染\n *\n * @param id Design JSON 中的对象 ID\n * @param visible 是否参与二维画布、三维纹理和 PNG 渲染\n * @returns 是否找到并更新了对象\n */\n setObjectVisibility(id: string, visible: boolean): boolean {\n this.flushHistoryCommit()\n const object = this.objectsById.get(id)\n if (!object || object.visible === visible) {\n return Boolean(object)\n }\n\n if (!visible && this.canvas.getActiveObjects().includes(object)) {\n this.canvas.discardActiveObject()\n }\n object.set({ visible })\n this.canvas.requestRenderAll()\n this.notifySelectionChange()\n this.commitHistory()\n return true\n }\n\n /**\n * 修改对象是否允许通过画布控件变换\n *\n * @param id Design JSON 中的对象 ID\n * @param locked 是否锁定移动、缩放、旋转、倾斜和文字编辑\n * @returns 是否找到并更新了对象\n */\n setObjectLocked(id: string, locked: boolean): boolean {\n this.flushHistoryCommit()\n const object = this.objectsById.get(id)\n if (!object) {\n return false\n }\n\n this.applyObjectLock(object, locked)\n this.canvas.requestRenderAll()\n this.commitHistory()\n return true\n }\n\n /**\n * 更新对象中心位置、缩放、旋转和翻转\n *\n * 对象最终仍会约束在逻辑画布内,连续调用会合并为一个历史步骤\n *\n * @param id Design JSON 中的对象 ID\n * @param options 要更新的变换字段\n * @returns 约束后的对象快照,找不到对象时返回 undefined\n * @throws 数值无效或缩放倍数不大于零时抛出错误\n */\n updateObjectTransform(\n id: string,\n options: UpdateObjectTransformOptions,\n ): DesignObject | undefined {\n const object = this.objectsById.get(id)\n if (!object) {\n return undefined\n }\n this.validateTransformOptions(options)\n\n const selectedIds = this.getSelectedObjectIds()\n if (object.group instanceof ActiveSelection) {\n this.canvas.discardActiveObject()\n }\n\n const current = this.serializeTransform(object)\n const x = options.x ?? current.x\n const y = options.y ?? current.y\n object.set({\n scaleX: options.scaleX ?? current.scaleX,\n scaleY: options.scaleY ?? current.scaleY,\n angle: options.rotation ?? current.rotation,\n flipX: options.flipX ?? current.flipX,\n flipY: options.flipY ?? current.flipY,\n })\n object.setPositionByOrigin(new Point(x, y), 'center', 'center')\n this.constrainObjectToCanvas(object)\n\n const selectedObjects = selectedIds\n .map((selectedId) => this.objectsById.get(selectedId))\n .filter((selected): selected is FabricObject => Boolean(selected?.visible))\n if (selectedObjects.length > 0) {\n this.applySelection(selectedObjects)\n }\n this.canvas.requestRenderAll()\n this.notifySelectionChange()\n this.scheduleHistoryCommit()\n return this.serializeObject(object)\n }\n\n /**\n * 修改已有文字对象的内容和排版样式\n *\n * 连续调用会在短暂空闲后合并为一个历史步骤\n * 字体必须由消费页面提前加载,否则浏览器会使用回退字体\n *\n * @param id Design JSON 中的对象 ID\n * @param options 要修改的文字属性,未传字段保持不变\n * @returns 是否找到并更新了文字对象\n * @throws 字号、行高、字距、字重或 CSS 颜色不符合约束时抛出错误\n */\n updateText(id: string, options: UpdateTextOptions): boolean {\n const object = this.objectsById.get(id)\n if (!(object instanceof Textbox)) {\n return false\n }\n\n if (options.text !== undefined) {\n object.set({ text: options.text })\n }\n if (options.fontFamily !== undefined) {\n const fontFamily = options.fontFamily.trim()\n if (!fontFamily) {\n throw new TypeError('fontFamily must be a non-empty string')\n }\n object.set({ fontFamily })\n }\n if (options.fontSize !== undefined) {\n if (!Number.isFinite(options.fontSize) || options.fontSize <= 0) {\n throw new RangeError('fontSize must be greater than zero')\n }\n object.set({ fontSize: options.fontSize })\n }\n if (options.color !== undefined) {\n if (!options.color.trim()) {\n throw new TypeError('color must be a non-empty CSS color')\n }\n object.set({ fill: options.color })\n }\n if (options.fontWeight !== undefined) {\n const validWeight =\n options.fontWeight === 'normal' ||\n options.fontWeight === 'bold' ||\n (typeof options.fontWeight === 'number' &&\n Number.isFinite(options.fontWeight) &&\n options.fontWeight > 0 &&\n options.fontWeight <= 1000)\n if (!validWeight) {\n throw new RangeError(\n 'fontWeight must be normal, bold, or between 1 and 1000',\n )\n }\n object.set({ fontWeight: options.fontWeight })\n }\n if (options.fontStyle !== undefined) {\n if (options.fontStyle !== 'normal' && options.fontStyle !== 'italic') {\n throw new TypeError('fontStyle must be normal or italic')\n }\n object.set({ fontStyle: options.fontStyle })\n }\n if (options.underline !== undefined) {\n object.set({ underline: options.underline })\n }\n if (options.textAlign !== undefined) {\n if (!['left', 'center', 'right'].includes(options.textAlign)) {\n throw new TypeError('textAlign must be left, center, or right')\n }\n object.set({ textAlign: options.textAlign })\n }\n if (options.lineHeight !== undefined) {\n if (!Number.isFinite(options.lineHeight) || options.lineHeight <= 0) {\n throw new RangeError('lineHeight must be greater than zero')\n }\n object.set({ lineHeight: options.lineHeight })\n }\n if (options.charSpacing !== undefined) {\n if (!Number.isFinite(options.charSpacing)) {\n throw new TypeError('charSpacing must be a finite number')\n }\n object.set({ charSpacing: options.charSpacing })\n }\n if (options.backgroundColor !== undefined) {\n if (options.backgroundColor !== null && !options.backgroundColor.trim()) {\n throw new TypeError(\n 'backgroundColor must be a non-empty CSS color or null',\n )\n }\n object.set({ backgroundColor: options.backgroundColor ?? '' })\n }\n\n object.initDimensions()\n if (object.group instanceof ActiveSelection) {\n object.group.triggerLayout()\n this.constrainObjectToCanvas(object.group)\n } else {\n this.constrainObjectToCanvas(object)\n }\n this.canvas.requestRenderAll()\n this.scheduleHistoryCommit()\n return true\n }\n\n /**\n * 让一个未锁定文字对象进入画布内联编辑状态\n *\n * @param id Design JSON 中的对象 ID\n * @returns 是否找到文字对象并进入编辑状态\n */\n editText(id: string): boolean {\n this.flushHistoryCommit()\n const object = this.objectsById.get(id)\n if (\n !(object instanceof Textbox) ||\n !object.visible ||\n (this.objectLocks.get(object) ?? false)\n ) {\n return false\n }\n\n this.canvas.setActiveObject(object)\n object.enterEditing()\n object.hiddenTextarea?.focus()\n this.canvas.requestRenderAll()\n this.notifySelectionChange()\n return true\n }\n\n /**\n * 设置铺满画布的基础纹理,不传地址时恢复透明背景\n *\n * 背景纹理不参与对象选择,但会包含在实时纹理和 PNG 导出中\n *\n * @param url 基础纹理地址\n * @throws 图片加载失败或被 CORS 策略阻止时抛出错误\n */\n async setBackgroundTexture(url?: string): Promise<void> {\n if (!url) {\n this.canvas.backgroundImage = undefined\n this.canvas.backgroundColor = ''\n this.canvas.requestRenderAll()\n return\n }\n\n const image = await FabricImage.fromURL(url, { crossOrigin: 'anonymous' })\n const scaleX = this.width / Math.max(image.width, 1)\n const scaleY = this.height / Math.max(image.height, 1)\n\n image.set({\n left: 0,\n top: 0,\n originX: 'left',\n originY: 'top',\n scaleX,\n scaleY,\n selectable: false,\n evented: false,\n })\n\n this.canvas.backgroundImage = image\n this.canvas.requestRenderAll()\n }\n\n /**\n * 添加并选中一个可编辑文字对象\n *\n * 文字位置使用画布像素坐标,原点位于对象左上角\n * 对象过大时会等比缩小,越界时会自动移回画布\n *\n * @param options 文字内容、位置和样式\n * @returns 创建后的可持久化文字对象快照\n */\n addText(options: AddTextOptions = {}): TextDesignObject {\n this.flushHistoryCommit()\n const text = new Textbox(options.text ?? this.defaultText, {\n left: options.x ?? this.width * 0.18,\n top: options.y ?? this.height * 0.36,\n width: options.width ?? this.width * 0.42,\n fontFamily: options.fontFamily ?? 'Arial',\n fontSize: options.fontSize ?? 22,\n fontWeight: options.fontWeight ?? 700,\n fontStyle: options.fontStyle ?? 'normal',\n underline: options.underline ?? false,\n fill: options.color ?? '#172126',\n backgroundColor: options.backgroundColor ?? '',\n originX: 'left',\n originY: 'top',\n textAlign: options.textAlign ?? 'center',\n lineHeight: options.lineHeight ?? 1.16,\n charSpacing: options.charSpacing ?? 0,\n editable: true,\n ...this.objectControlAppearance(),\n })\n\n this.addAndSelect(text, options.name)\n return this.serializeObject(text) as TextDesignObject\n }\n\n /**\n * 加载、添加并选中一个图片对象\n *\n * role 为 background 时替换已有设计背景、铺满当前 UV 可打印区域并默认锁定在最底层\n * 图片位置使用画布像素坐标,原点位于图片中心\n * 对象过大时会等比缩小,越界时会自动移回画布\n * 远程图片必须提供正确的 CORS 响应头才能安全导出 PNG\n *\n * @param options 图片地址、中心位置和显示宽度\n * @returns 创建后的可持久化图片对象快照\n * @throws 图片加载失败或被 CORS 策略阻止时抛出错误\n */\n async addImage(options: AddImageOptions): Promise<ImageDesignObject> {\n this.flushHistoryCommit()\n const source = await resolvePersistentImageSource(options.src)\n const image = await this.loadImage(source)\n const role = options.role ?? 'element'\n const targetWidth = options.width ?? this.width * 0.22\n const scale = targetWidth / Math.max(image.width, 1)\n\n image.set({\n left: options.x ?? this.width * 0.62,\n top: options.y ?? this.height * 0.29,\n originX: 'center',\n originY: 'center',\n scaleX: scale,\n scaleY: scale,\n ...this.objectControlAppearance(),\n })\n\n if (role === 'background') {\n this.removeDesignBackgrounds()\n this.fitImageToEditableBounds(image)\n }\n this.imageSources.set(image, source)\n this.addAndSelect(image, options.name, role === 'background', role)\n return this.serializeObject(image) as ImageDesignObject\n }\n\n /**\n * 返回与 Fabric.js 无关的当前设计快照\n *\n * 文档包含设计背景等设计对象和逻辑画布尺寸,不包含产品基础纹理或模型配置\n *\n * @returns 可以安全传给 JSON.stringify 的 Design JSON 文档\n * @throws 画布包含不支持的对象或非字符串文字填充时抛出错误\n */\n saveDesign(): DesignDocument {\n return {\n version: 1,\n canvas: { width: this.width, height: this.height },\n objects: this.getObjects(),\n }\n }\n\n /**\n * 校验并恢复 Design JSON,图片全部加载成功后才替换当前对象\n *\n * 背景纹理和当前产品保持不变,恢复后不选中任何对象\n *\n * @param value JSON.parse 结果或符合 DesignDocument 的对象\n * @throws Schema 无效、画布尺寸不匹配或图片无法加载时抛出错误\n */\n async loadDesign(value: unknown): Promise<void> {\n this.flushHistoryCommit()\n await this.replaceDesign(value)\n this.commitHistory()\n }\n\n private async replaceDesign(value: unknown): Promise<void> {\n const design = parseDesignDocument(value)\n if (design.canvas.width !== this.width || design.canvas.height !== this.height) {\n throw new RangeError(\n `Design canvas ${design.canvas.width}x${design.canvas.height} does not match editor canvas ${this.width}x${this.height}`,\n )\n }\n\n const entries: Array<{\n id: string\n object: FabricObject\n source?: string\n name?: string\n locked: boolean\n role: DesignImageRole\n }> = []\n\n try {\n for (const object of design.objects) {\n entries.push({\n id: object.id,\n object: await this.createObjectFromDesign(object),\n source: object.type === 'image' ? object.src : undefined,\n name: object.name,\n locked: object.locked ?? false,\n role: object.type === 'image' ? object.role ?? 'element' : 'element',\n })\n }\n } catch (error) {\n entries.forEach(({ object }) => object.dispose())\n throw error\n }\n\n const previousObjects = this.canvas.getObjects()\n this.canvas.discardActiveObject()\n this.canvas.remove(...previousObjects)\n previousObjects.forEach((object) => object.dispose())\n this.usedObjectIds.clear()\n this.objectsById.clear()\n\n for (const entry of entries) {\n this.registerObject(\n entry.object,\n entry.id,\n entry.name,\n entry.locked,\n entry.role,\n )\n if (entry.object instanceof FabricImage && entry.source) {\n this.imageSources.set(entry.object, entry.source)\n }\n this.canvas.add(entry.object)\n this.constrainObjectToCanvas(entry.object)\n }\n\n this.canvas.requestRenderAll()\n this.notifySelectionChange()\n }\n\n /**\n * 恢复上一个设计快照\n *\n * @returns 是否成功恢复了一个历史步骤\n */\n async undo(): Promise<boolean> {\n this.flushHistoryCommit()\n const target = this.history.peekUndo()\n if (!target || this.historyBusy) {\n return false\n }\n\n this.historyBusy = true\n try {\n await this.replaceDesign(target)\n this.history.confirmUndo()\n this.historySignature = JSON.stringify(this.saveDesign())\n this.notifyHistoryChange()\n return true\n } finally {\n this.historyBusy = false\n }\n }\n\n /**\n * 恢复下一个设计快照\n *\n * @returns 是否成功恢复了一个历史步骤\n */\n async redo(): Promise<boolean> {\n this.flushHistoryCommit()\n const target = this.history.peekRedo()\n if (!target || this.historyBusy) {\n return false\n }\n\n this.historyBusy = true\n try {\n await this.replaceDesign(target)\n this.history.confirmRedo()\n this.historySignature = JSON.stringify(this.saveDesign())\n this.notifyHistoryChange()\n return true\n } finally {\n this.historyBusy = false\n }\n }\n\n /** 以当前设计为起点清空撤销与重做历史 */\n clearHistory(): void {\n this.flushHistoryCommit()\n const current = this.saveDesign()\n this.history.reset(current)\n this.historySignature = JSON.stringify(current)\n this.notifyHistoryChange()\n }\n\n /**\n * 删除当前对象或多选选区\n *\n * @returns 是否删除了至少一个对象\n */\n deleteSelected(): boolean {\n this.flushHistoryCommit()\n const selection = this.canvas.getActiveObjects()\n if (selection.length === 0) {\n return false\n }\n\n this.canvas.remove(...selection)\n selection.forEach((object) => {\n this.unregisterObject(object)\n object.dispose()\n })\n this.canvas.discardActiveObject()\n this.canvas.requestRenderAll()\n this.notifySelectionChange()\n this.commitHistory()\n return true\n }\n\n /**\n * 将当前画布内容导出为 PNG 并触发浏览器下载\n *\n * @param filename 下载文件名\n * @throws 画布被无 CORS 授权的远程图片污染时抛出安全错误\n */\n exportTexture(filename = 'custom-texture.png'): void {\n const anchor = document.createElement('a')\n anchor.download = filename\n anchor.href = this.getTextureDataUrl()\n anchor.click()\n }\n\n /**\n * 返回不含选择框和 UV 辅助层的 PNG Data URL\n *\n * @returns 与逻辑画布同尺寸的 PNG Data URL\n * @throws 远程图片污染 Canvas 时抛出安全错误\n */\n getTextureDataUrl(): string {\n this.renderTextureCanvas()\n return this.textureCanvasElement.toDataURL('image/png')\n }\n\n /**\n * 异步返回不含选择框和 UV 辅助层的 PNG Blob\n *\n * @returns 与逻辑画布同尺寸的 PNG Blob\n * @throws 远程图片污染 Canvas 或浏览器无法编码时抛出错误\n */\n getTextureBlob(): Promise<Blob> {\n this.renderTextureCanvas()\n return new Promise((resolve, reject) => {\n this.textureCanvasElement.toBlob((blob) => {\n if (blob) {\n resolve(blob)\n } else {\n reject(new Error('The texture canvas could not be encoded as PNG'))\n }\n }, 'image/png')\n })\n }\n\n /** 释放 ResizeObserver、事件监听和 Fabric Canvas */\n destroy(): void {\n if (this.historyTimer !== undefined) {\n clearTimeout(this.historyTimer)\n this.historyTimer = undefined\n }\n this.resizeObserver.disconnect()\n this.renderListeners.clear()\n this.selectionListeners.clear()\n this.historyListeners.clear()\n this.objectsById.clear()\n this.usedObjectIds.clear()\n this.canvas.dispose()\n this.host.replaceChildren()\n }\n\n private addAndSelect(\n object: FabricObject,\n name?: string,\n locked = false,\n role: DesignImageRole = 'element',\n ): void {\n this.registerObject(object, undefined, name, locked, role)\n this.canvas.add(object)\n if (object instanceof FabricImage && role === 'background') {\n this.canvas.moveObjectTo(object, 0)\n }\n this.constrainObjectToCanvas(object)\n this.canvas.setActiveObject(object)\n this.canvas.requestRenderAll()\n this.notifySelectionChange()\n this.commitHistory()\n }\n\n private registerObject(\n object: FabricObject,\n id = this.createObjectId(),\n name?: string,\n locked = false,\n role: DesignImageRole = 'element',\n ): void {\n if (this.usedObjectIds.has(id)) {\n throw new Error(`Design object id is already in use: ${id}`)\n }\n this.usedObjectIds.add(id)\n this.objectIds.set(object, id)\n this.objectsById.set(id, object)\n if (name) {\n this.objectNames.set(object, name)\n }\n if (object instanceof FabricImage) {\n this.imageRoles.set(object, role)\n }\n this.applyObjectLock(object, locked)\n }\n\n private unregisterObject(object: FabricObject): void {\n const id = this.objectIds.get(object)\n if (id) {\n this.objectsById.delete(id)\n }\n }\n\n private createObjectId(): string {\n let id: string\n do {\n this.objectIdSequence += 1\n id = `object-${this.objectIdSequence}`\n } while (this.usedObjectIds.has(id))\n return id\n }\n\n private serializeObject(object: FabricObject): DesignObject {\n const id = this.objectIds.get(object)\n if (!id) {\n throw new Error('Design object is missing its stable id')\n }\n\n const transform = this.serializeTransform(object)\n const state = {\n name: this.objectNames.get(object) ?? this.createObjectName(object),\n visible: object.visible,\n locked: this.objectLocks.get(object) ?? false,\n }\n if (object instanceof Textbox) {\n if (typeof object.fill !== 'string') {\n throw new Error(`Text object ${id} uses an unsupported non-string fill`)\n }\n return {\n id,\n type: 'text',\n ...state,\n transform,\n text: object.text,\n width: object.width,\n fontFamily: object.fontFamily,\n fontSize: object.fontSize,\n color: object.fill,\n fontWeight: object.fontWeight as TextFontWeight,\n fontStyle: object.fontStyle as TextFontStyle,\n underline: object.underline,\n textAlign: object.textAlign as TextAlignment,\n lineHeight: object.lineHeight,\n charSpacing: object.charSpacing,\n ...(object.backgroundColor\n ? { backgroundColor: object.backgroundColor }\n : {}),\n }\n }\n\n if (object instanceof FabricImage) {\n const src = this.imageSources.get(object) ?? object.getSrc()\n if (!src || src.startsWith('blob:')) {\n throw new Error(`Image object ${id} does not have a persistent source`)\n }\n return {\n id,\n type: 'image',\n ...state,\n transform,\n src,\n role: this.imageRoles.get(object) ?? 'element',\n }\n }\n\n throw new Error(`Design object ${id} has an unsupported type`)\n }\n\n private serializeTransform(object: FabricObject): DesignObjectTransform {\n const center = object.getCenterPoint()\n return {\n x: center.x,\n y: center.y,\n scaleX: Math.abs(object.scaleX),\n scaleY: Math.abs(object.scaleY),\n rotation: object.angle,\n flipX: object.scaleX < 0 ? !object.flipX : object.flipX,\n flipY: object.scaleY < 0 ? !object.flipY : object.flipY,\n }\n }\n\n private async createObjectFromDesign(design: DesignObject): Promise<FabricObject> {\n const common = {\n left: design.transform.x,\n top: design.transform.y,\n originX: 'center' as const,\n originY: 'center' as const,\n scaleX: design.transform.scaleX,\n scaleY: design.transform.scaleY,\n angle: design.transform.rotation,\n flipX: design.transform.flipX,\n flipY: design.transform.flipY,\n visible: design.visible ?? true,\n ...this.objectControlAppearance(),\n }\n\n if (design.type === 'text') {\n return new Textbox(design.text, {\n ...common,\n width: design.width,\n fontFamily: design.fontFamily,\n fontSize: design.fontSize,\n fontWeight: design.fontWeight ?? 700,\n fontStyle: design.fontStyle ?? 'normal',\n underline: design.underline ?? false,\n fill: design.color,\n backgroundColor: design.backgroundColor ?? '',\n textAlign: design.textAlign ?? 'center',\n lineHeight: design.lineHeight ?? 1.16,\n charSpacing: design.charSpacing ?? 0,\n editable: true,\n })\n }\n\n const image = await this.loadImage(design.src)\n image.set(common)\n return image\n }\n\n private loadImage(source: string): Promise<FabricImage> {\n return FabricImage.fromURL(\n source,\n source.startsWith('data:') ? undefined : { crossOrigin: 'anonymous' },\n )\n }\n\n private createObjectName(object: FabricObject): string {\n if (object instanceof Textbox) {\n return object.text.trim().slice(0, 48) || this.textObjectName\n }\n if (object instanceof FabricImage) {\n return this.imageRoles.get(object) === 'background'\n ? this.backgroundObjectName\n : this.imageObjectName\n }\n return 'Object'\n }\n\n private applyObjectLock(object: FabricObject, locked: boolean): void {\n this.objectLocks.set(object, locked)\n object.set({\n hasBorders: !locked,\n hasControls: !locked,\n lockMovementX: locked,\n lockMovementY: locked,\n lockRotation: locked,\n lockScalingX: locked,\n lockScalingY: locked,\n lockSkewingX: locked,\n lockSkewingY: locked,\n })\n if (object instanceof Textbox) {\n object.set({ editable: !locked })\n }\n }\n\n private notifySelectionChange(): void {\n const objectIds = this.getSelectedObjectIds()\n this.selectionListeners.forEach((listener) => listener(objectIds))\n }\n\n private removeDesignBackgrounds(): void {\n const backgrounds = this.canvas.getObjects().filter(\n (object): object is FabricImage =>\n object instanceof FabricImage && this.imageRoles.get(object) === 'background',\n )\n if (backgrounds.length === 0) {\n return\n }\n\n if (backgrounds.some((object) => this.canvas.getActiveObjects().includes(object))) {\n this.canvas.discardActiveObject()\n }\n this.canvas.remove(...backgrounds)\n backgrounds.forEach((object) => {\n this.unregisterObject(object)\n object.dispose()\n })\n }\n\n private scheduleHistoryCommit(): void {\n if (this.historyBusy) {\n return\n }\n if (this.historyTimer !== undefined) {\n clearTimeout(this.historyTimer)\n }\n this.historyTimer = setTimeout(() => {\n this.historyTimer = undefined\n this.commitHistory()\n }, 320)\n }\n\n private flushHistoryCommit(): void {\n if (this.historyTimer === undefined) {\n return\n }\n clearTimeout(this.historyTimer)\n this.historyTimer = undefined\n this.commitHistory()\n }\n\n private commitHistory(): void {\n if (this.historyBusy) {\n return\n }\n if (this.historyTimer !== undefined) {\n clearTimeout(this.historyTimer)\n this.historyTimer = undefined\n }\n\n const snapshot = this.saveDesign()\n const signature = JSON.stringify(snapshot)\n if (signature === this.historySignature) {\n return\n }\n this.history.push(snapshot)\n this.historySignature = signature\n this.notifyHistoryChange()\n }\n\n private notifyHistoryChange(): void {\n const state = this.history.state\n this.historyListeners.forEach((listener) => listener(state))\n }\n\n private constrainObjectToCanvas(object: FabricObject): void {\n object.setCoords()\n let bounds = object.getBoundingRect()\n const scale = calculateContainmentScale(bounds, this.width, this.height)\n\n if (scale < 1) {\n object.set({\n scaleX: object.scaleX * scale,\n scaleY: object.scaleY * scale,\n })\n object.setCoords()\n bounds = object.getBoundingRect()\n }\n\n const offset = calculateContainmentOffset(bounds, this.width, this.height)\n if (offset.x !== 0 || offset.y !== 0) {\n object.set({\n left: object.left + offset.x,\n top: object.top + offset.y,\n })\n object.setCoords()\n }\n }\n\n private resizeDisplay(): void {\n const availableWidth = Math.max(this.host.clientWidth - EDITOR_DISPLAY_GUTTER, 1)\n const availableHeight = Math.max(this.host.clientHeight - EDITOR_DISPLAY_GUTTER, 1)\n const scale = Math.min(\n availableWidth / this.width,\n availableHeight / this.height,\n 1,\n )\n\n this.canvas.setDimensions(\n {\n width: `${Math.floor(this.width * scale)}px`,\n height: `${Math.floor(this.height * scale)}px`,\n },\n { cssOnly: true },\n )\n }\n\n private applySelection(objects: FabricObject[]): void {\n this.canvas.discardActiveObject()\n this.canvas.setActiveObject(\n objects.length === 1\n ? objects[0]\n : new ActiveSelection(objects, { canvas: this.canvas }),\n )\n }\n\n private objectControlAppearance(): Record<string, string | number | boolean> {\n return {\n transparentCorners: false,\n cornerColor: this.appearance.controlFill,\n cornerStrokeColor: this.appearance.controlBorder,\n borderColor: this.appearance.objectBorder,\n cornerSize: this.appearance.controlSize,\n }\n }\n\n private validateTransformOptions(options: UpdateObjectTransformOptions): void {\n for (const [name, value] of [\n ['x', options.x],\n ['y', options.y],\n ['rotation', options.rotation],\n ] as const) {\n if (value !== undefined && !Number.isFinite(value)) {\n throw new TypeError(`${name} must be a finite number`)\n }\n }\n for (const [name, value] of [\n ['scaleX', options.scaleX],\n ['scaleY', options.scaleY],\n ] as const) {\n if (value !== undefined && (!Number.isFinite(value) || value <= 0)) {\n throw new RangeError(`${name} must be greater than zero`)\n }\n }\n for (const [name, value] of [\n ['flipX', options.flipX],\n ['flipY', options.flipY],\n ] as const) {\n if (value !== undefined && typeof value !== 'boolean') {\n throw new TypeError(`${name} must be a boolean`)\n }\n }\n }\n\n private fitImageToEditableBounds(image: FabricImage): void {\n const bounds = this.editableBounds\n image.set({\n left: bounds.left + bounds.width / 2,\n top: bounds.top + bounds.height / 2,\n angle: 0,\n scaleX: bounds.width / Math.max(image.width, 1),\n scaleY: bounds.height / Math.max(image.height, 1),\n })\n image.setCoords()\n }\n\n private renderTextureCanvas(): void {\n this.renderingTexture = true\n try {\n this.textureRenderer.renderTexture(this.textureContext)\n } finally {\n this.renderingTexture = false\n }\n }\n\n private markRenderState(): void {\n if (this.host.dataset.renderState === 'nonblank') {\n return\n }\n\n const context = this.canvas.getContext()\n let minimum = 255\n let maximum = 0\n let opaqueSamples = 0\n\n for (let row = 1; row < 8; row += 1) {\n for (let column = 1; column < 16; column += 1) {\n const x = Math.floor((column / 16) * this.width)\n const y = Math.floor((row / 8) * this.height)\n const pixel = context.getImageData(x, y, 1, 1).data\n const luminance = (pixel[0] + pixel[1] + pixel[2]) / 3\n minimum = Math.min(minimum, luminance)\n maximum = Math.max(maximum, luminance)\n if (pixel[3] > 0) {\n opaqueSamples += 1\n }\n }\n }\n\n if (opaqueSamples > 0 && maximum - minimum > 8) {\n this.host.dataset.renderState = 'nonblank'\n }\n }\n}\n","import { CanvasTexture, SRGBColorSpace } from 'three'\nimport type { DesignEditor } from '../editor/DesignEditor'\nimport type { ProductViewer } from '../viewer/ProductViewer'\n\n/**\n * 将二维编辑画布转换为 Three.js 实时纹理\n *\n * 多次画布渲染会合并到下一个动画帧,避免重复标记纹理更新\n */\nexport class TextureBridge {\n /** 绑定到三维产品材质的实时 CanvasTexture */\n readonly texture: CanvasTexture\n\n private readonly stopListening: () => void\n private updateFrame = 0\n\n /**\n * @param editor 提供不含交互控件的纹理 Canvas 和渲染事件的二维编辑器\n * @param viewer 接收实时纹理的三维查看器\n * @param flipY 是否垂直翻转纹理\n */\n constructor(editor: DesignEditor, viewer: ProductViewer, flipY: boolean) {\n this.texture = new CanvasTexture(editor.textureCanvas)\n this.texture.colorSpace = SRGBColorSpace\n this.texture.flipY = flipY\n this.texture.anisotropy = 4\n viewer.setTexture(this.texture)\n\n this.stopListening = editor.onRender(() => this.scheduleUpdate())\n this.scheduleUpdate()\n }\n\n /**\n * 更新纹理垂直翻转状态并立即标记刷新\n *\n * @param flipY 是否垂直翻转纹理\n */\n setFlipY(flipY: boolean): void {\n this.texture.flipY = flipY\n this.texture.needsUpdate = true\n }\n\n /** 取消编辑器订阅、动画帧并释放 CanvasTexture */\n destroy(): void {\n this.stopListening()\n cancelAnimationFrame(this.updateFrame)\n this.texture.dispose()\n }\n\n /** 将同一帧内的多次画布渲染合并为一次纹理更新 */\n private scheduleUpdate(): void {\n if (this.updateFrame) {\n return\n }\n\n this.updateFrame = requestAnimationFrame(() => {\n this.updateFrame = 0\n this.texture.needsUpdate = true\n })\n }\n}\n","export default \"__VITE_ASSET__VRAku6fjghkApIISiBWPzg__\"","import { Mesh } from 'three'\nimport type { UvLayout } from '../core/uv'\n\ninterface GeometryRange {\n start: number\n count: number\n}\n\ninterface UvEdge {\n coordinates: [number, number, number, number]\n occurrences: number\n}\n\nconst UV_EDGE_PRECISION = 1_000_000\n\nfunction createPointKey(u: number, v: number): string {\n return `${Math.round(u * UV_EDGE_PRECISION)},${Math.round(v * UV_EDGE_PRECISION)}`\n}\n\nfunction extractBoundaryCoordinates(\n triangleCoordinates: Float32Array,\n): Float32Array {\n const edges = new Map<string, UvEdge>()\n\n for (let index = 0; index < triangleCoordinates.length; index += 6) {\n const points = [\n [triangleCoordinates[index], triangleCoordinates[index + 1]],\n [triangleCoordinates[index + 2], triangleCoordinates[index + 3]],\n [triangleCoordinates[index + 4], triangleCoordinates[index + 5]],\n ] as const\n\n for (const [startIndex, endIndex] of [[0, 1], [1, 2], [2, 0]] as const) {\n const start = points[startIndex]\n const end = points[endIndex]\n const startKey = createPointKey(start[0], start[1])\n const endKey = createPointKey(end[0], end[1])\n const edgeKey = startKey < endKey\n ? `${startKey}|${endKey}`\n : `${endKey}|${startKey}`\n const edge = edges.get(edgeKey)\n\n if (edge) {\n edge.occurrences += 1\n } else {\n edges.set(edgeKey, {\n coordinates: [start[0], start[1], end[0], end[1]],\n occurrences: 1,\n })\n }\n }\n }\n\n return new Float32Array(\n [...edges.values()]\n .filter((edge) => edge.occurrences === 1)\n .flatMap((edge) => edge.coordinates),\n )\n}\n\n/**\n * 提取目标 Mesh 第一个材质槽使用的 UV 三角形\n *\n * @param mesh 接收实时纹理的目标 Mesh\n * @returns 保持模型原始 V 方向的 UV 三角形和外边界布局\n * @throws Mesh 缺少 UV、面数据无效或第一个材质槽没有三角形时抛出错误\n */\nexport function extractUvLayout(mesh: Mesh): UvLayout {\n const geometry = mesh.geometry\n const uv = geometry.getAttribute('uv')\n if (!uv) {\n throw new Error(`Customizable mesh does not contain UV coordinates: ${mesh.name}`)\n }\n\n const index = geometry.getIndex()\n const elementCount = index?.count ?? uv.count\n const ranges: GeometryRange[] =\n Array.isArray(mesh.material) && geometry.groups.length > 0\n ? geometry.groups\n .filter((group) => (group.materialIndex ?? 0) === 0)\n .map(({ start, count }) => ({ start, count }))\n : [{ start: 0, count: elementCount }]\n const coordinateCount = ranges.reduce((total, range) => {\n if (\n !Number.isInteger(range.start) ||\n !Number.isInteger(range.count) ||\n range.start < 0 ||\n range.count < 0 ||\n range.start + range.count > elementCount ||\n range.count % 3 !== 0\n ) {\n throw new Error(`Customizable mesh has invalid triangle groups: ${mesh.name}`)\n }\n return total + range.count * 2\n }, 0)\n\n if (coordinateCount === 0) {\n throw new Error(`Customizable mesh has no triangles in its first material slot: ${mesh.name}`)\n }\n\n const triangleCoordinates = new Float32Array(coordinateCount)\n let targetIndex = 0\n for (const range of ranges) {\n for (let offset = range.start; offset < range.start + range.count; offset += 1) {\n const vertexIndex = index ? index.getX(offset) : offset\n const u = uv.getX(vertexIndex)\n const v = uv.getY(vertexIndex)\n if (!Number.isFinite(u) || !Number.isFinite(v)) {\n throw new Error(`Customizable mesh has invalid UV coordinates: ${mesh.name}`)\n }\n triangleCoordinates[targetIndex] = u\n triangleCoordinates[targetIndex + 1] = v\n targetIndex += 2\n }\n }\n\n return {\n triangleCoordinates,\n boundaryCoordinates: extractBoundaryCoordinates(triangleCoordinates),\n }\n}\n","import {\n ACESFilmicToneMapping,\n AmbientLight,\n Box3,\n DirectionalLight,\n MathUtils,\n Mesh,\n MeshStandardMaterial,\n Object3D,\n PerspectiveCamera,\n Scene,\n SRGBColorSpace,\n Vector3,\n WebGLRenderer,\n type CanvasTexture,\n type Material,\n} from 'three'\nimport { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'\nimport { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'\nimport type { NormalizedProductConfiguration } from '../core/config'\nimport type { ProductViewState, ViewerAppearance } from '../core/types'\nimport type { UvLayout } from '../core/uv'\nimport defaultProductModelUrl from './assets/cup_decal_small_margins.glb?url&no-inline'\nimport { extractUvLayout } from './uvLayout'\n\nconst DEFAULT_PRODUCT_HEIGHT = 2.35\nconst DEFAULT_PRODUCT_BASE_Y = -1.17\nconst DEFAULT_PRODUCT_ROTATION = MathUtils.degToRad(-72)\nconst DEFAULT_CAMERA_AZIMUTH = MathUtils.degToRad(18)\nconst DEFAULT_CAMERA_ELEVATION = MathUtils.degToRad(10)\nconst CAMERA_VIEWPORT_FILL = 0.7\n\ntype ViewChangeListener = (state: ProductViewState) => void\n\n/**\n * 基于 Three.js 的三维产品查看器\n *\n * 负责模型加载、目标 Mesh 查找、实时纹理绑定、相机控制和资源释放\n * 当前只将纹理应用到目标 Mesh 的第一个材质槽\n */\nexport class ProductViewer {\n private readonly host: HTMLElement\n private readonly scene = new Scene()\n private readonly camera = new PerspectiveCamera(35, 1, 0.05, 100)\n private readonly renderer: WebGLRenderer\n private readonly controls: OrbitControls\n private readonly loader = new GLTFLoader()\n private readonly resizeObserver: ResizeObserver\n private productRoot?: Object3D\n private surface?: Mesh\n private texture?: CanvasTexture\n private animationFrame = 0\n private readonly viewChangeListeners = new Set<ViewChangeListener>()\n private readonly handleControlsChange = (): void => {\n const state = this.getViewState()\n this.viewChangeListeners.forEach((listener) => listener(state))\n }\n\n /**\n * @param host 三维查看器挂载容器\n * @param ariaLabel 三维产品画布的无障碍名称\n * @param appearance WebGL 画布清屏颜色等外观配置\n */\n constructor(\n host: HTMLElement,\n ariaLabel = 'Interactive 3D product preview',\n appearance: ViewerAppearance = {},\n ) {\n this.host = host\n host.replaceChildren()\n\n this.renderer = new WebGLRenderer({ antialias: true, alpha: true })\n this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))\n this.renderer.outputColorSpace = SRGBColorSpace\n this.renderer.toneMapping = ACESFilmicToneMapping\n this.renderer.toneMappingExposure = 1.05\n if (appearance.backgroundColor?.trim()) {\n this.renderer.setClearColor(appearance.backgroundColor.trim(), 1)\n }\n this.renderer.domElement.setAttribute('aria-label', ariaLabel)\n this.renderer.domElement.classList.add('customforge-viewer-canvas')\n host.append(this.renderer.domElement)\n host.dataset.renderState = 'pending'\n\n this.camera.position.set(4.6, 2.8, 5.8)\n\n this.controls = new OrbitControls(this.camera, this.renderer.domElement)\n this.controls.enableDamping = true\n this.controls.minDistance = 3.2\n this.controls.maxDistance = 11\n this.controls.target.set(0, 0, 0)\n this.controls.addEventListener('change', this.handleControlsChange)\n\n this.addStudioLighting()\n this.resizeObserver = new ResizeObserver(() => this.resize())\n this.resizeObserver.observe(host)\n this.resize()\n this.render()\n }\n\n /**\n * 加载远程模型或随包提供的默认 GLB 模型\n *\n * @param product 已完成默认值补全的产品配置\n * @returns 目标 Mesh 第一个材质槽使用的 UV 三角形布局\n * @throws 模型加载失败、找不到目标 Mesh 或目标 Mesh 缺少有效 UV 时抛出错误\n */\n async loadProduct(product: NormalizedProductConfiguration): Promise<UvLayout> {\n this.removeProduct()\n\n if (product.modelUrl) {\n const gltf = await this.loader.loadAsync(product.modelUrl)\n this.productRoot = gltf.scene\n this.surface = this.findSurface(gltf.scene, product.surfaceMesh)\n } else {\n const gltf = await this.loader.loadAsync(defaultProductModelUrl)\n this.productRoot = gltf.scene\n this.surface = this.findSurface(gltf.scene, product.surfaceMesh)\n this.prepareBundledProduct(gltf.scene, this.surface)\n }\n\n const surface = this.surface\n if (!surface) {\n throw new Error('Customizable mesh was not initialized')\n }\n const uvLayout = extractUvLayout(surface)\n\n this.scene.add(this.productRoot)\n if (this.texture) {\n this.attachTexture(this.texture)\n }\n this.fitCamera(this.productRoot)\n return uvLayout\n }\n\n /**\n * 保存并绑定二维编辑器生成的实时纹理\n *\n * 如果模型尚未加载,纹理会在模型加载完成后自动绑定\n *\n * @param texture 由二维画布创建的 CanvasTexture\n */\n setTexture(texture: CanvasTexture): void {\n this.texture = texture\n if (this.surface) {\n this.attachTexture(texture)\n }\n }\n\n /** 根据当前模型包围盒恢复默认相机位置 */\n resetView(): void {\n if (this.productRoot) {\n this.fitCamera(this.productRoot)\n }\n }\n\n /** 返回当前相机位置和轨道控制目标点的独立快照 */\n getViewState(): ProductViewState {\n return {\n position: this.vectorToValue(this.camera.position),\n target: this.vectorToValue(this.controls.target),\n }\n }\n\n /**\n * 恢复相机位置和轨道控制目标点\n *\n * @param state 要恢复的三维观察视角\n * @returns 应用后的独立视角快照\n * @throws 坐标不是有限数值或相机与目标点重合时抛出错误\n */\n setViewState(state: ProductViewState): ProductViewState {\n this.validateViewState(state)\n this.camera.position.set(\n state.position.x,\n state.position.y,\n state.position.z,\n )\n this.controls.target.set(state.target.x, state.target.y, state.target.z)\n this.camera.updateProjectionMatrix()\n this.controls.update()\n return this.getViewState()\n }\n\n /**\n * 订阅三维视角变化\n *\n * 用户旋转、缩放、平移以及 API 恢复视角时都会触发\n *\n * @param listener 接收独立视角快照的监听函数\n * @returns 用于取消本次订阅的函数\n */\n onViewChange(listener: ViewChangeListener): () => void {\n this.viewChangeListeners.add(listener)\n return () => this.viewChangeListeners.delete(listener)\n }\n\n /** 释放动画帧、相机控制、模型材质和 WebGLRenderer */\n destroy(): void {\n cancelAnimationFrame(this.animationFrame)\n this.resizeObserver.disconnect()\n this.controls.removeEventListener('change', this.handleControlsChange)\n this.controls.dispose()\n this.viewChangeListeners.clear()\n this.removeProduct()\n this.renderer.dispose()\n this.renderer.domElement.remove()\n }\n\n private addStudioLighting(): void {\n this.scene.add(new AmbientLight('#ffffff', 0.9))\n\n const key = new DirectionalLight('#ffffff', 2.1)\n key.position.set(4, 5, 6)\n this.scene.add(key)\n\n const fill = new DirectionalLight('#e8f0f2', 1.15)\n fill.position.set(-5, 2, 4)\n this.scene.add(fill)\n\n const rim = new DirectionalLight('#ffffff', 0.8)\n rim.position.set(1, 4, -5)\n this.scene.add(rim)\n }\n\n private prepareBundledProduct(root: Object3D, surface: Mesh): void {\n surface.renderOrder = 1\n const surfaceMaterials = Array.isArray(surface.material)\n ? surface.material\n : [surface.material]\n surfaceMaterials.forEach((material) => {\n material.depthWrite = false\n material.polygonOffset = true\n material.polygonOffsetFactor = -1\n material.polygonOffsetUnits = -1\n })\n\n const initialBounds = new Box3().setFromObject(root)\n const initialHeight = initialBounds.getSize(new Vector3()).y\n if (!Number.isFinite(initialHeight) || initialHeight <= 0) {\n throw new Error('The bundled product has invalid bounds')\n }\n\n root.scale.multiplyScalar(DEFAULT_PRODUCT_HEIGHT / initialHeight)\n root.rotation.y = DEFAULT_PRODUCT_ROTATION\n root.updateMatrixWorld(true)\n\n const bounds = new Box3().setFromObject(root)\n const center = bounds.getCenter(new Vector3())\n root.position.x -= center.x\n root.position.y += DEFAULT_PRODUCT_BASE_Y - bounds.min.y\n root.position.z -= center.z\n root.updateMatrixWorld(true)\n }\n\n /**\n * 按名称查找接收实时纹理的 Mesh\n *\n * @throws 找不到对象或同名对象不是 Mesh 时抛出错误\n */\n private findSurface(root: Object3D, meshName: string): Mesh {\n const object = root.getObjectByName(meshName)\n if (!(object instanceof Mesh)) {\n throw new Error(`Customizable mesh was not found: ${meshName}`)\n }\n return object\n }\n\n /**\n * 将纹理应用到目标 Mesh 的第一个材质槽\n *\n * 非 MeshStandardMaterial 会被替换为基础标准材质\n */\n private attachTexture(texture: CanvasTexture): void {\n if (!this.surface) {\n return\n }\n\n const materials = Array.isArray(this.surface.material)\n ? this.surface.material\n : [this.surface.material]\n const material = materials[0]\n\n if (!(material instanceof MeshStandardMaterial)) {\n const replacement = new MeshStandardMaterial({\n color: '#ffffff',\n roughness: 0.45,\n map: texture,\n })\n this.surface.material = replacement\n return\n }\n\n material.map = texture\n material.color.set('#ffffff')\n material.needsUpdate = true\n }\n\n private fitCamera(root: Object3D, direction?: Vector3): void {\n const bounds = new Box3().setFromObject(root)\n const size = bounds.getSize(new Vector3())\n const center = bounds.getCenter(new Vector3())\n const diagonal = size.length()\n if (!Number.isFinite(diagonal) || diagonal <= 0) {\n throw new Error('The product has invalid bounds')\n }\n\n const viewDirection = direction?.clone() ?? this.defaultCameraDirection()\n if (viewDirection.lengthSq() === 0) {\n viewDirection.copy(this.defaultCameraDirection())\n }\n viewDirection.normalize()\n const right = new Vector3().crossVectors(this.camera.up, viewDirection)\n if (right.lengthSq() < 1e-8) {\n right.set(1, 0, 0)\n } else {\n right.normalize()\n }\n const up = new Vector3().crossVectors(viewDirection, right).normalize()\n const verticalSlope =\n Math.tan(MathUtils.degToRad(this.camera.fov / 2)) * CAMERA_VIEWPORT_FILL\n const horizontalSlope = verticalSlope * Math.max(this.camera.aspect, 0.1)\n let distance = 0\n\n for (const x of [bounds.min.x, bounds.max.x]) {\n for (const y of [bounds.min.y, bounds.max.y]) {\n for (const z of [bounds.min.z, bounds.max.z]) {\n const offset = new Vector3(x, y, z).sub(center)\n const depth = offset.dot(viewDirection)\n distance = Math.max(\n distance,\n depth + Math.abs(offset.dot(right)) / horizontalSlope,\n depth + Math.abs(offset.dot(up)) / verticalSlope,\n )\n }\n }\n }\n distance = Math.max(distance, diagonal * 0.55, 0.1)\n\n this.controls.target.copy(center)\n this.camera.position.copy(center).addScaledVector(viewDirection, distance)\n this.camera.near = Math.max(Math.min(distance * 0.02, diagonal * 0.01), 0.01)\n this.camera.far = Math.max(distance + diagonal * 2, 10)\n this.camera.updateProjectionMatrix()\n this.controls.minDistance = Math.max(diagonal * 0.45, distance * 0.3, 0.05)\n this.controls.maxDistance = Math.max(distance * 4, diagonal * 4)\n this.controls.update()\n }\n\n private defaultCameraDirection(): Vector3 {\n const horizontal = Math.cos(DEFAULT_CAMERA_ELEVATION)\n return new Vector3(\n horizontal * Math.sin(DEFAULT_CAMERA_AZIMUTH),\n Math.sin(DEFAULT_CAMERA_ELEVATION),\n horizontal * Math.cos(DEFAULT_CAMERA_AZIMUTH),\n )\n }\n\n private vectorToValue(vector: Vector3): ProductViewState['position'] {\n return { x: vector.x, y: vector.y, z: vector.z }\n }\n\n private validateViewState(state: ProductViewState): void {\n const values = [\n state.position.x,\n state.position.y,\n state.position.z,\n state.target.x,\n state.target.y,\n state.target.z,\n ]\n if (!values.every(Number.isFinite)) {\n throw new TypeError('View position and target must contain finite numbers')\n }\n\n const distanceSquared =\n (state.position.x - state.target.x) ** 2 +\n (state.position.y - state.target.y) ** 2 +\n (state.position.z - state.target.z) ** 2\n if (distanceSquared <= Number.EPSILON) {\n throw new RangeError('View position and target must not be identical')\n }\n }\n\n private removeProduct(): void {\n if (!this.productRoot) {\n return\n }\n\n this.scene.remove(this.productRoot)\n this.productRoot.traverse((object) => {\n if (!(object instanceof Mesh)) {\n return\n }\n object.geometry.dispose()\n const materials: Material[] = Array.isArray(object.material)\n ? object.material\n : [object.material]\n materials.forEach((material) => material.dispose())\n })\n this.productRoot = undefined\n this.surface = undefined\n }\n\n private resize(): void {\n const width = Math.max(this.host.clientWidth, 1)\n const height = Math.max(this.host.clientHeight, 1)\n const direction = this.camera.position.clone().sub(this.controls.target)\n this.renderer.setSize(width, height, false)\n this.camera.aspect = width / height\n if (this.productRoot && direction.lengthSq() > 0) {\n this.fitCamera(this.productRoot, direction)\n } else {\n this.camera.updateProjectionMatrix()\n }\n }\n\n private render = (): void => {\n this.animationFrame = requestAnimationFrame(this.render)\n this.controls.update()\n this.renderer.render(this.scene, this.camera)\n this.markRenderState()\n }\n\n private markRenderState(): void {\n if (this.host.dataset.renderState === 'nonblank') {\n return\n }\n\n const context = this.renderer.getContext()\n const width = context.drawingBufferWidth\n const height = context.drawingBufferHeight\n if (width < 2 || height < 2) {\n return\n }\n\n const pixel = new Uint8Array(4)\n let minimum = 255\n let maximum = 0\n\n for (const xRatio of [0.25, 0.5, 0.75]) {\n for (const yRatio of [0.25, 0.5, 0.75]) {\n context.readPixels(\n Math.floor(width * xRatio),\n Math.floor(height * yRatio),\n 1,\n 1,\n context.RGBA,\n context.UNSIGNED_BYTE,\n pixel,\n )\n const luminance = (pixel[0] + pixel[1] + pixel[2]) / 3\n minimum = Math.min(minimum, luminance)\n maximum = Math.max(maximum, luminance)\n }\n }\n\n if (maximum - minimum > 8) {\n this.host.dataset.renderState = 'nonblank'\n }\n }\n}\n","import { normalizeProductConfiguration } from '../core/config'\nimport type {\n CustomizerEventListener,\n ProductCustomizerApi,\n} from '../core/api'\nimport { resolveElement } from '../core/dom'\nimport type {\n AddImageOptions,\n AddTextOptions,\n CanvasBounds,\n CustomizerEventMap,\n CustomizerEventName,\n CustomizerOptions,\n CustomizerState,\n DesignCanvas,\n DesignDocument,\n DesignObject,\n ImageDesignObject,\n ProductConfiguration,\n ProductViewState,\n ResolvedProductConfiguration,\n TextDesignObject,\n UpdateObjectTransformOptions,\n UpdateTextOptions,\n} from '../core/types'\nimport { DesignEditor } from '../editor/DesignEditor'\nimport { TextureBridge } from '../bridge/TextureBridge'\nimport { ProductViewer } from '../viewer/ProductViewer'\n\n/**\n * 统一管理二维编辑器、三维查看器和实时纹理同步\n *\n * 仅支持具有 DOM、Canvas、WebGL 和 ResizeObserver 的浏览器环境\n * 每个实例独立持有 DOM 事件、Fabric 状态和 WebGL 资源\n * 不再使用实例时必须调用 `destroy()`\n */\nexport class ProductCustomizer implements ProductCustomizerApi {\n private readonly events = new EventTarget()\n private readonly textureBridge: TextureBridge\n private readonly editor: DesignEditor\n private readonly viewer: ProductViewer\n private product: ReturnType<typeof normalizeProductConfiguration>\n private readonly stopSelectionListener: () => void\n private readonly stopRenderListener: () => void\n private readonly stopHistoryListener: () => void\n private readonly stopViewListener: () => void\n private destroyed = false\n\n private constructor(options: CustomizerOptions) {\n const editorHost = resolveElement(options.editor, 'Editor')\n const viewerHost = resolveElement(options.viewer, 'Viewer')\n\n if (editorHost === viewerHost) {\n throw new Error('Editor and viewer must use different elements')\n }\n\n const historyLimit = options.historyLimit ?? 50\n if (!Number.isInteger(historyLimit) || historyLimit < 1) {\n throw new RangeError('historyLimit must be a positive integer')\n }\n\n this.product = normalizeProductConfiguration(options.product)\n this.editor = new DesignEditor(editorHost, {\n width: options.editorWidth ?? 1024,\n height: options.editorHeight ?? 512,\n historyLimit,\n ariaLabel: options.editorAriaLabel,\n defaultText: options.defaultText,\n textObjectName: options.textObjectName,\n imageObjectName: options.imageObjectName,\n backgroundObjectName: options.backgroundObjectName,\n appearance: options.appearance?.editor,\n })\n this.viewer = new ProductViewer(\n viewerHost,\n options.viewerAriaLabel,\n options.appearance?.viewer,\n )\n this.textureBridge = new TextureBridge(\n this.editor,\n this.viewer,\n this.product.textureFlipY,\n )\n\n this.stopSelectionListener = this.editor.onSelectionChange((objectIds) => {\n this.emit('selectionchange', {\n hasSelection: objectIds.length > 0,\n objectIds,\n })\n })\n this.stopRenderListener = this.editor.onRender(() => {\n this.emit('change', { objectCount: this.editor.objectCount })\n })\n this.stopHistoryListener = this.editor.onHistoryChange((state) => {\n this.emit('historychange', state)\n })\n this.stopViewListener = this.viewer.onViewChange((state) => {\n this.emit('viewchange', state)\n })\n }\n\n /**\n * 创建实例并完成初始产品加载\n *\n * @param options 产品定制器初始化配置\n * @returns 初始化完成的产品定制器实例\n * @throws DOM 容器无效或初始化失败时释放已创建资源并继续抛出原始错误\n */\n static async create(options: CustomizerOptions): Promise<ProductCustomizer> {\n const customizer = new ProductCustomizer(options)\n try {\n await customizer.initialize()\n return customizer\n } catch (error) {\n customizer.destroy()\n throw error\n }\n }\n\n /**\n * 订阅产品定制器事件\n *\n * @param event 事件名称\n * @param listener 接收对应事件载荷的监听函数\n * @returns 用于取消本次订阅的函数\n */\n on<K extends CustomizerEventName>(\n event: K,\n listener: CustomizerEventListener<K>,\n ): () => void {\n const wrapped = (browserEvent: Event) => {\n listener((browserEvent as CustomEvent<CustomizerEventMap[K]>).detail)\n }\n this.events.addEventListener(event, wrapped)\n return () => this.events.removeEventListener(event, wrapped)\n }\n\n /**\n * 在二维画布中添加并选中一个文字对象\n *\n * 创建后和用户变换期间,对象会自动缩放或平移以保持完整可见\n *\n * @param options 文字内容、位置和样式配置\n * @returns 创建后的可持久化文字对象快照\n */\n addText(options?: AddTextOptions): TextDesignObject {\n return this.editor.addText(options)\n }\n\n /**\n * 加载图片并将其添加到二维画布\n *\n * role 为 background 时会替换已有设计背景、铺满当前 UV 可打印区域并默认锁定在最底层\n * 创建后和用户变换期间,对象会自动缩放或平移以保持完整可见\n *\n * @param options 图片地址、位置和显示宽度\n * @returns 创建后的可持久化图片对象快照\n * @throws 图片无法访问、加载失败或被 CORS 策略阻止时抛出错误\n */\n async addImage(options: AddImageOptions): Promise<ImageDesignObject> {\n try {\n return await this.editor.addImage(options)\n } catch (error) {\n this.reportError(error)\n throw error\n }\n }\n\n /** 返回产品、画布、对象、选区、历史和视角的独立状态快照 */\n getState(): CustomizerState {\n return {\n product: this.getProduct(),\n canvas: this.getCanvasSize(),\n printableBounds: this.getPrintableBounds(),\n objects: this.getObjects(),\n selectedObjectIds: this.getSelectedObjectIds(),\n history: {\n canUndo: this.canUndo(),\n canRedo: this.canRedo(),\n },\n view: this.getViewState(),\n }\n }\n\n /** 返回当前补全默认值后的产品配置独立快照 */\n getProduct(): ResolvedProductConfiguration {\n return { ...this.product }\n }\n\n /** 返回当前二维逻辑画布尺寸 */\n getCanvasSize(): DesignCanvas {\n return this.editor.getCanvasSize()\n }\n\n /** 返回当前产品 UV 在逻辑画布中的可打印包围框 */\n getPrintableBounds(): CanvasBounds {\n return this.editor.getPrintableBounds()\n }\n\n /**\n * 返回当前可编辑对象的独立快照\n *\n * @returns 按画布层级从后到前排列的 Design JSON 对象\n */\n getObjects(): DesignObject[] {\n return this.editor.getObjects()\n }\n\n /** 当前选中对象的 ID,按画布层级从后到前排列 */\n getSelectedObjectIds(): string[] {\n return this.editor.getSelectedObjectIds()\n }\n\n /**\n * 按稳定 ID 选中一个可见对象\n *\n * @param id Design JSON 中的对象 ID\n * @returns 是否找到并选中了对象\n */\n selectObject(id: string): boolean {\n return this.editor.selectObject(id)\n }\n\n /**\n * 按稳定 ID 同时选中多个可见对象\n *\n * 所有 ID 都必须有效且可见,否则保持原选区不变;空数组清除选区\n *\n * @param ids Design JSON 中的对象 ID\n * @returns 是否应用了请求的选区\n */\n selectObjects(ids: readonly string[]): boolean {\n return this.editor.selectObjects(ids)\n }\n\n /**\n * 清除当前画布选区,不修改设计内容或历史记录\n *\n * @returns 清除前是否存在选中对象\n */\n clearSelection(): boolean {\n return this.editor.clearSelection()\n }\n\n /**\n * 按稳定 ID 删除一个对象\n *\n * @param id Design JSON 中的对象 ID\n * @returns 是否找到并删除了对象\n */\n removeObject(id: string): boolean {\n return this.editor.removeObject(id)\n }\n\n /**\n * 将对象移动到指定图层索引\n *\n * @param id Design JSON 中的对象 ID\n * @param index 从 0 开始的索引,0 表示最底层\n * @returns 对象层级是否发生变化\n */\n moveObject(id: string, index: number): boolean {\n return this.editor.moveObject(id, index)\n }\n\n /**\n * 将现有图片转换为铺满当前 UV 可打印区域的设计背景\n *\n * 转换会替换已有设计背景、重置旋转、锁定对象并移动到最底层\n *\n * @param id Design JSON 中的图片对象 ID\n * @returns 是否找到普通图片并完成转换\n */\n setImageAsBackground(id: string): boolean {\n return this.editor.setImageAsBackground(id)\n }\n\n /**\n * 修改对象在图层面板中的名称\n *\n * @param id Design JSON 中的对象 ID\n * @param name 非空图层名称\n * @returns 是否找到并更新了对象\n */\n renameObject(id: string, name: string): boolean {\n return this.editor.renameObject(id, name)\n }\n\n /**\n * 修改对象是否参与渲染\n *\n * @param id Design JSON 中的对象 ID\n * @param visible 是否参与二维画布、三维纹理和 PNG 渲染\n * @returns 是否找到并更新了对象\n */\n setObjectVisibility(id: string, visible: boolean): boolean {\n return this.editor.setObjectVisibility(id, visible)\n }\n\n /**\n * 修改对象是否允许通过画布控件变换\n *\n * @param id Design JSON 中的对象 ID\n * @param locked 是否锁定移动、缩放、旋转、倾斜和文字编辑\n * @returns 是否找到并更新了对象\n */\n setObjectLocked(id: string, locked: boolean): boolean {\n return this.editor.setObjectLocked(id, locked)\n }\n\n /**\n * 更新对象中心位置、缩放、旋转和翻转\n *\n * @param id Design JSON 中的对象 ID\n * @param options 要更新的变换字段\n * @returns 约束后的对象快照,找不到对象时返回 undefined\n * @throws 数值无效或缩放倍数不大于零时抛出错误\n */\n updateObjectTransform(\n id: string,\n options: UpdateObjectTransformOptions,\n ): DesignObject | undefined {\n return this.editor.updateObjectTransform(id, options)\n }\n\n /**\n * 修改已有文字对象的内容和排版样式\n *\n * 连续调用会在短暂空闲后合并为一个撤销步骤\n *\n * @param id Design JSON 中的对象 ID\n * @param options 要修改的文字属性,未传字段保持不变\n * @returns 是否找到并更新了文字对象\n * @throws 字号、行高、字距、字重或 CSS 颜色不符合约束时抛出错误\n */\n updateText(id: string, options: UpdateTextOptions): boolean {\n return this.editor.updateText(id, options)\n }\n\n /**\n * 让一个未锁定文字对象进入画布内联编辑状态\n *\n * @param id Design JSON 中的对象 ID\n * @returns 是否找到文字对象并进入编辑状态\n */\n editText(id: string): boolean {\n return this.editor.editText(id)\n }\n\n /**\n * 删除二维编辑器中的当前对象或选区\n *\n * @returns 是否删除了至少一个对象\n */\n deleteSelected(): boolean {\n return this.editor.deleteSelected()\n }\n\n /**\n * 创建当前二维设计的版本化 JSON 快照\n *\n * 快照不包含产品模型、目标 Mesh 或基础纹理配置\n * Blob URL 图片会在添加时转换为 Data URL,因此返回值可以跨页面会话保存\n *\n * @returns 可以安全传给 JSON.stringify 的 DesignDocument\n * @throws 设计中存在不支持的对象或文字填充时抛出错误\n */\n saveDesign(): DesignDocument {\n return this.editor.saveDesign()\n }\n\n /**\n * 校验并恢复版本化 Design JSON\n *\n * 图片全部加载成功后才替换当前二维对象,产品和基础纹理保持不变\n * 输入画布尺寸必须与当前编辑器的逻辑尺寸完全一致\n *\n * @param value JSON.parse 结果或符合 DesignDocument 的对象\n * @throws Schema 无效、画布尺寸不匹配或图片加载失败时抛出错误\n */\n async loadDesign(value: unknown): Promise<void> {\n this.emit('status', { message: 'Loading design' })\n try {\n await this.editor.loadDesign(value)\n this.emit('status', { message: 'Design loaded' })\n } catch (error) {\n this.reportError(error)\n throw error\n }\n }\n\n /** 当前是否存在可以撤销的设计快照 */\n canUndo(): boolean {\n return this.editor.canUndo\n }\n\n /** 当前是否存在可以重做的设计快照 */\n canRedo(): boolean {\n return this.editor.canRedo\n }\n\n /**\n * 恢复上一个设计快照\n *\n * @returns 是否成功恢复了一个历史步骤\n * @throws 历史中的图片无法恢复时抛出错误\n */\n async undo(): Promise<boolean> {\n try {\n const changed = await this.editor.undo()\n if (changed) {\n this.emit('status', { message: 'Undo complete' })\n }\n return changed\n } catch (error) {\n this.reportError(error)\n throw error\n }\n }\n\n /**\n * 恢复下一个设计快照\n *\n * @returns 是否成功恢复了一个历史步骤\n * @throws 历史中的图片无法恢复时抛出错误\n */\n async redo(): Promise<boolean> {\n try {\n const changed = await this.editor.redo()\n if (changed) {\n this.emit('status', { message: 'Redo complete' })\n }\n return changed\n } catch (error) {\n this.reportError(error)\n throw error\n }\n }\n\n /** 以当前设计为起点清空撤销与重做历史 */\n clearHistory(): void {\n this.editor.clearHistory()\n }\n\n /**\n * 返回当前合成纹理的 PNG Data URL\n *\n * 结果与逻辑画布同尺寸,不包含选择框和 UV 辅助层\n *\n * @returns PNG Data URL\n * @throws 远程图片污染 Canvas 时抛出安全错误\n */\n getTextureDataUrl(): string {\n return this.editor.getTextureDataUrl()\n }\n\n /**\n * 异步返回当前合成纹理的 PNG Blob\n *\n * 结果与逻辑画布同尺寸,不包含选择框和 UV 辅助层\n *\n * @returns PNG Blob\n * @throws 远程图片污染 Canvas 或浏览器无法编码时抛出错误\n */\n getTextureBlob(): Promise<Blob> {\n return this.editor.getTextureBlob()\n }\n\n /**\n * 将当前二维设计合成为 PNG 并触发浏览器下载\n *\n * @param filename 下载文件名,默认为 `custom-texture.png`\n * @throws 远程图片污染 Canvas 时可能抛出安全错误\n */\n exportTexture(filename?: string): void {\n this.editor.exportTexture(filename)\n }\n\n /** 恢复三维产品的默认相机位置 */\n resetView(): void {\n this.viewer.resetView()\n }\n\n /** 返回当前三维相机位置和观察目标点 */\n getViewState(): ProductViewState {\n return this.viewer.getViewState()\n }\n\n /**\n * 恢复三维观察视角\n *\n * @param state 要恢复的相机位置和观察目标点\n * @returns 应用后的独立视角快照\n * @throws 坐标不是有限数值或相机与目标点重合时抛出错误\n */\n setViewState(state: ProductViewState): ProductViewState {\n return this.viewer.setViewState(state)\n }\n\n /**\n * 更换模型、基础纹理和接收纹理的目标 Mesh\n *\n * 成功后保留当前设计对象,并将当前设计设为新的历史起点\n *\n * @param product 新的产品配置\n * @throws 模型或纹理加载失败、目标 Mesh 不存在时抛出错误\n */\n async loadProduct(product: ProductConfiguration): Promise<void> {\n const nextProduct = normalizeProductConfiguration(product)\n this.emit('status', { message: 'Loading product' })\n\n try {\n const uvLayout = await this.viewer.loadProduct(nextProduct)\n await this.editor.setBackgroundTexture(nextProduct.textureUrl)\n this.textureBridge.setFlipY(nextProduct.textureFlipY)\n this.editor.setUvLayout(uvLayout, nextProduct.textureFlipY)\n this.product = nextProduct\n this.editor.clearHistory()\n this.emit('ready', { product: this.getProduct() })\n this.emit('status', { message: nextProduct.modelUrl ? 'Remote product ready' : 'Demo product ready' })\n } catch (error) {\n this.reportError(error)\n throw error\n }\n }\n\n /**\n * 释放事件监听、Fabric Canvas、纹理和 WebGL 资源\n *\n * 重复调用不会再次释放资源,首次调用后不得继续使用其他实例方法\n */\n destroy(): void {\n if (this.destroyed) {\n return\n }\n\n this.destroyed = true\n this.stopSelectionListener?.()\n this.stopRenderListener?.()\n this.stopHistoryListener?.()\n this.stopViewListener?.()\n this.textureBridge.destroy()\n this.editor.destroy()\n this.viewer.destroy()\n }\n\n private async initialize(): Promise<void> {\n const uvLayout = await this.viewer.loadProduct(this.product)\n await this.editor.setBackgroundTexture(this.product.textureUrl)\n this.textureBridge.setFlipY(this.product.textureFlipY)\n this.editor.setUvLayout(uvLayout, this.product.textureFlipY)\n this.emit('ready', { product: this.getProduct() })\n }\n\n private emit<K extends CustomizerEventName>(\n event: K,\n detail: CustomizerEventMap[K],\n ): void {\n this.events.dispatchEvent(new CustomEvent(event, { detail }))\n }\n\n private reportError(error: unknown): void {\n const normalized = error instanceof Error ? error : new Error(String(error))\n this.emit('error', { error: normalized })\n this.emit('status', { message: normalized.message })\n }\n}\n"],"mappings":"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"}
|
|
@@ -12,7 +12,7 @@ export declare class TextureBridge {
|
|
|
12
12
|
private readonly stopListening;
|
|
13
13
|
private updateFrame;
|
|
14
14
|
/**
|
|
15
|
-
* @param editor
|
|
15
|
+
* @param editor 提供不含交互控件的纹理 Canvas 和渲染事件的二维编辑器
|
|
16
16
|
* @param viewer 接收实时纹理的三维查看器
|
|
17
17
|
* @param flipY 是否垂直翻转纹理
|
|
18
18
|
*/
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { AddImageOptions, AddTextOptions, CanvasBounds, CustomizerEventMap, CustomizerEventName, CustomizerState, DesignCanvas, DesignDocument, DesignObject, ImageDesignObject, ProductConfiguration, ProductViewState, ResolvedProductConfiguration, TextDesignObject, UpdateObjectTransformOptions, UpdateTextOptions } from './types';
|
|
2
|
+
/** 产品定制器事件监听函数 */
|
|
3
|
+
export type CustomizerEventListener<K extends CustomizerEventName> = (detail: CustomizerEventMap[K]) => void;
|
|
4
|
+
/**
|
|
5
|
+
* 与具体 UI 框架无关的产品定制能力契约
|
|
6
|
+
*
|
|
7
|
+
* 消费方可以基于该接口构建自己的工具栏、图层面板、状态提示和页面样式
|
|
8
|
+
* 接口不暴露 Fabric.js 或 Three.js 实例,调用 destroy 后不得继续使用
|
|
9
|
+
*/
|
|
10
|
+
export interface ProductCustomizerApi {
|
|
11
|
+
/** 订阅核心状态事件并返回取消订阅函数 */
|
|
12
|
+
on<K extends CustomizerEventName>(event: K, listener: CustomizerEventListener<K>): () => void;
|
|
13
|
+
/** 添加并选中文字对象,返回可持久化对象快照 */
|
|
14
|
+
addText(options?: AddTextOptions): TextDesignObject;
|
|
15
|
+
/**
|
|
16
|
+
* 加载、添加并选中图片,返回可持久化对象快照
|
|
17
|
+
*
|
|
18
|
+
* 远程图片必须允许跨域访问;Blob URL 会在加入设计时转换为 Data URL
|
|
19
|
+
*/
|
|
20
|
+
addImage(options: AddImageOptions): Promise<ImageDesignObject>;
|
|
21
|
+
/** 返回产品、画布、对象、选区、历史和视角的独立状态快照 */
|
|
22
|
+
getState(): CustomizerState;
|
|
23
|
+
/** 返回当前补全默认值后的产品配置 */
|
|
24
|
+
getProduct(): ResolvedProductConfiguration;
|
|
25
|
+
/** 返回当前逻辑画布尺寸 */
|
|
26
|
+
getCanvasSize(): DesignCanvas;
|
|
27
|
+
/** 返回当前产品 UV 在逻辑画布中的可打印包围框 */
|
|
28
|
+
getPrintableBounds(): CanvasBounds;
|
|
29
|
+
/** 按从后到前顺序返回设计对象快照 */
|
|
30
|
+
getObjects(): DesignObject[];
|
|
31
|
+
/** 返回当前选中对象 ID */
|
|
32
|
+
getSelectedObjectIds(): string[];
|
|
33
|
+
/** 按稳定 ID 选中一个可见对象 */
|
|
34
|
+
selectObject(id: string): boolean;
|
|
35
|
+
/** 按稳定 ID 同时选中多个可见对象,空数组表示清除选区 */
|
|
36
|
+
selectObjects(ids: readonly string[]): boolean;
|
|
37
|
+
/** 清除当前选区且不修改设计内容 */
|
|
38
|
+
clearSelection(): boolean;
|
|
39
|
+
/** 按稳定 ID 删除一个对象 */
|
|
40
|
+
removeObject(id: string): boolean;
|
|
41
|
+
/** 将对象移动到从 0 开始的图层索引 */
|
|
42
|
+
moveObject(id: string, index: number): boolean;
|
|
43
|
+
/** 将普通图片转换成适配当前 UV 可打印区域的设计背景 */
|
|
44
|
+
setImageAsBackground(id: string): boolean;
|
|
45
|
+
/** 修改对象在图层工具中显示的名称 */
|
|
46
|
+
renameObject(id: string, name: string): boolean;
|
|
47
|
+
/** 修改对象是否参与二维、三维和导出渲染 */
|
|
48
|
+
setObjectVisibility(id: string, visible: boolean): boolean;
|
|
49
|
+
/** 修改对象是否允许通过画布控件交互变换 */
|
|
50
|
+
setObjectLocked(id: string, locked: boolean): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* 更新对象中心位置、缩放、旋转和翻转并返回实际结果
|
|
53
|
+
*
|
|
54
|
+
* 坐标使用逻辑画布像素,缩放必须大于零,结果会约束在画布内
|
|
55
|
+
*/
|
|
56
|
+
updateObjectTransform(id: string, options: UpdateObjectTransformOptions): DesignObject | undefined;
|
|
57
|
+
/** 更新文字内容和排版属性 */
|
|
58
|
+
updateText(id: string, options: UpdateTextOptions): boolean;
|
|
59
|
+
/** 让未锁定文字进入画布内编辑状态 */
|
|
60
|
+
editText(id: string): boolean;
|
|
61
|
+
/** 删除当前对象或多选对象 */
|
|
62
|
+
deleteSelected(): boolean;
|
|
63
|
+
/** 返回当前版本化 Design JSON 快照 */
|
|
64
|
+
saveDesign(): DesignDocument;
|
|
65
|
+
/** 校验并以事务方式恢复 Design JSON */
|
|
66
|
+
loadDesign(value: unknown): Promise<void>;
|
|
67
|
+
/** 查询当前是否可以撤销 */
|
|
68
|
+
canUndo(): boolean;
|
|
69
|
+
/** 查询当前是否可以重做 */
|
|
70
|
+
canRedo(): boolean;
|
|
71
|
+
/** 恢复上一个历史快照 */
|
|
72
|
+
undo(): Promise<boolean>;
|
|
73
|
+
/** 恢复下一个历史快照 */
|
|
74
|
+
redo(): Promise<boolean>;
|
|
75
|
+
/** 将当前设计设为新的历史起点 */
|
|
76
|
+
clearHistory(): void;
|
|
77
|
+
/** 返回不含选择框和 UV 辅助层的 PNG Data URL,Canvas 被跨域资源污染时会抛出安全错误 */
|
|
78
|
+
getTextureDataUrl(): string;
|
|
79
|
+
/** 异步返回不含选择框和 UV 辅助层的 PNG Blob,Canvas 被跨域资源污染时会拒绝 Promise */
|
|
80
|
+
getTextureBlob(): Promise<Blob>;
|
|
81
|
+
/** 下载当前合成纹理 PNG */
|
|
82
|
+
exportTexture(filename?: string): void;
|
|
83
|
+
/** 返回当前三维观察视角 */
|
|
84
|
+
getViewState(): ProductViewState;
|
|
85
|
+
/** 恢复三维观察视角并返回实际结果,坐标无效或相机与目标重合时抛出错误 */
|
|
86
|
+
setViewState(state: ProductViewState): ProductViewState;
|
|
87
|
+
/** 恢复适配当前模型的默认三维观察视角 */
|
|
88
|
+
resetView(): void;
|
|
89
|
+
/** 更换模型、基础纹理和目标 Mesh,远程资源必须允许跨域访问 */
|
|
90
|
+
loadProduct(product: ProductConfiguration): Promise<void>;
|
|
91
|
+
/** 释放事件、Canvas、观察器和 WebGL 资源 */
|
|
92
|
+
destroy(): void;
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/core/api.ts"],"names":[],"mappings":"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"}
|
package/dist/core/config.d.ts
CHANGED
|
@@ -1,23 +1,14 @@
|
|
|
1
|
-
import type { ProductConfiguration } from './types';
|
|
1
|
+
import type { ProductConfiguration, ResolvedProductConfiguration } from './types';
|
|
2
2
|
/** 默认接收实时纹理的 Mesh 名称 */
|
|
3
3
|
export declare const DEFAULT_SURFACE_MESH = "PrintArea";
|
|
4
4
|
/**
|
|
5
5
|
* 完成默认值补全后的内部产品配置
|
|
6
6
|
*/
|
|
7
|
-
export
|
|
8
|
-
/** 清理空白后的模型地址 */
|
|
9
|
-
modelUrl?: string;
|
|
10
|
-
/** 清理空白后的基础纹理地址 */
|
|
11
|
-
textureUrl?: string;
|
|
12
|
-
/** 接收实时纹理的 Mesh 名称 */
|
|
13
|
-
surfaceMesh: string;
|
|
14
|
-
/** 是否垂直翻转 CanvasTexture */
|
|
15
|
-
textureFlipY: boolean;
|
|
16
|
-
}
|
|
7
|
+
export type NormalizedProductConfiguration = ResolvedProductConfiguration;
|
|
17
8
|
/**
|
|
18
9
|
* 清理产品配置并补全运行所需的默认值
|
|
19
10
|
*
|
|
20
|
-
*
|
|
11
|
+
* 远程模型和随包提供的默认 GLB 模型均默认不翻转纹理
|
|
21
12
|
*
|
|
22
13
|
* @param product 外部传入的产品配置
|
|
23
14
|
* @returns 可以直接交给编辑器和查看器使用的完整配置
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,oBAAoB,EACpB,4BAA4B,EAC7B,MAAM,SAAS,CAAA;AAEhB,wBAAwB;AACxB,eAAO,MAAM,oBAAoB,cAAc,CAAA;AAE/C;;GAEG;AACH,MAAM,MAAM,8BAA8B,GAAG,4BAA4B,CAAA;AAEzE;;;;;;;GAOG;AACH,wBAAgB,6BAA6B,CAC3C,OAAO,GAAE,oBAAyB,GACjC,8BAA8B,CAWhC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"design.d.ts","sourceRoot":"","sources":["../../src/core/design.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,cAAc,
|
|
1
|
+
{"version":3,"file":"design.d.ts","sourceRoot":"","sources":["../../src/core/design.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,cAAc,EAOf,MAAM,SAAS,CAAA;AAEhB,kCAAkC;AAClC,eAAO,MAAM,qBAAqB,IAAI,CAAA;AA0MtC;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,CAsClE"}
|