2026-03-09 13:44:32 +08:00
|
|
|
|
import { fabric } from 'fabric-with-all'
|
2026-03-19 11:00:31 +08:00
|
|
|
|
import { ref, computed } from 'vue'
|
2026-03-09 13:44:32 +08:00
|
|
|
|
import { createCanvas } from '../tools/canvasFactory'
|
2026-03-09 14:20:44 +08:00
|
|
|
|
import { AnimationManager } from './AnimationManager'
|
2026-03-09 13:44:32 +08:00
|
|
|
|
import { detectDeviceType } from '../tools/index'
|
|
|
|
|
|
import { CanvasEventManager } from "./events/CanvasEventManager";
|
|
|
|
|
|
import { OperationType } from '../tools/layerHelper'
|
2026-03-23 16:43:08 +08:00
|
|
|
|
import { cloneObjects } from '../tools/canvasMethod'
|
2026-03-17 10:53:43 +08:00
|
|
|
|
import { createId } from '../../tools/tools'
|
2026-03-20 14:35:46 +08:00
|
|
|
|
import md5 from 'md5'
|
2026-03-09 13:44:32 +08:00
|
|
|
|
|
2026-03-13 11:18:36 +08:00
|
|
|
|
// 自定义画布转对象属性
|
2026-03-23 16:43:08 +08:00
|
|
|
|
fabric.Object.prototype.customProperties = ["top", "left", "width", "height", "scaleX", "scaleY", "info", "thumbnail", "absolutePositioned"];
|
2026-03-13 11:18:36 +08:00
|
|
|
|
fabric.Object.prototype.toObject_ = fabric.Object.prototype.toObject
|
|
|
|
|
|
fabric.Object.prototype.toObject = function () {
|
|
|
|
|
|
const args = [...arguments]
|
|
|
|
|
|
const arr = [...fabric.Object.prototype.customProperties]
|
|
|
|
|
|
args.forEach(v => {
|
|
|
|
|
|
if (typeof v === 'string') {
|
|
|
|
|
|
arr.push(v)
|
|
|
|
|
|
} else if (Array.isArray(v)) {
|
|
|
|
|
|
arr.push(...v)
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
2026-03-18 13:56:27 +08:00
|
|
|
|
const object = this.toObject_(arr)
|
|
|
|
|
|
if (object.info) {
|
|
|
|
|
|
let lock = !!object.info.lock
|
2026-03-25 11:27:24 +08:00
|
|
|
|
if (object.type === "group") lock = true
|
2026-03-18 13:56:27 +08:00
|
|
|
|
object.evented = !lock
|
|
|
|
|
|
object.selectable = !lock
|
|
|
|
|
|
}
|
|
|
|
|
|
return object
|
2026-03-13 11:18:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-09 13:44:32 +08:00
|
|
|
|
interface CanvasInitOptions {
|
|
|
|
|
|
canvasRef: any
|
|
|
|
|
|
canvasViewWidth?: number
|
|
|
|
|
|
canvasViewHeight?: number
|
|
|
|
|
|
canvasWidth?: number
|
|
|
|
|
|
canvasHeight?: number
|
2026-03-17 10:53:43 +08:00
|
|
|
|
url?: string
|
2026-03-09 13:44:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
export class CanvasManager {
|
|
|
|
|
|
stateManager: any
|
2026-03-09 16:45:30 +08:00
|
|
|
|
layerManager: any
|
|
|
|
|
|
animationManager: any
|
|
|
|
|
|
eventManager: any
|
2026-03-09 13:44:32 +08:00
|
|
|
|
deviceInfo: any
|
|
|
|
|
|
canvas: any
|
|
|
|
|
|
currentZoom: any
|
2026-03-20 14:35:46 +08:00
|
|
|
|
uniqueId: string
|
2026-03-09 13:44:32 +08:00
|
|
|
|
constructor(options) {
|
|
|
|
|
|
this.stateManager = options.stateManager;
|
2026-03-20 14:35:46 +08:00
|
|
|
|
this.uniqueId = md5(options.props.config.url || Date.now());
|
2026-03-09 13:44:32 +08:00
|
|
|
|
this.deviceInfo = detectDeviceType();
|
|
|
|
|
|
this.currentZoom = ref(100)
|
|
|
|
|
|
}
|
2026-03-16 16:51:12 +08:00
|
|
|
|
onMounted() { }
|
2026-03-19 11:00:31 +08:00
|
|
|
|
getCanvasSize() {
|
|
|
|
|
|
return {
|
|
|
|
|
|
canvasViewWidth: this.canvas.width,
|
|
|
|
|
|
canvasViewHeight: this.canvas.height,
|
|
|
|
|
|
canvasWidth: this.canvas.clipPath.width,
|
|
|
|
|
|
canvasHeight: this.canvas.clipPath.height,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-03-09 13:44:32 +08:00
|
|
|
|
setCanvasViewSize(options) {
|
2026-03-19 11:00:31 +08:00
|
|
|
|
var canvasViewWidth = options.canvasViewWidth || 1920
|
|
|
|
|
|
var canvasViewHeight = options.canvasViewHeight || 1080
|
|
|
|
|
|
this.canvas.setWidth(canvasViewWidth)
|
|
|
|
|
|
this.canvas.setHeight(canvasViewHeight)
|
2026-03-09 13:44:32 +08:00
|
|
|
|
}
|
2026-03-13 14:08:40 +08:00
|
|
|
|
/** 初始化画布 */
|
|
|
|
|
|
async initCanvas(options: CanvasInitOptions) {
|
2026-03-09 16:45:30 +08:00
|
|
|
|
this.layerManager = this.stateManager.layerManager
|
2026-03-19 11:00:31 +08:00
|
|
|
|
var canvasWidth = options.canvasWidth || 750
|
|
|
|
|
|
var canvasHeight = options.canvasHeight || 600
|
2026-03-17 11:35:04 +08:00
|
|
|
|
var image = null;
|
2026-03-17 10:53:43 +08:00
|
|
|
|
if (options.url) {
|
2026-03-17 11:35:04 +08:00
|
|
|
|
await new Promise((resolve) => {
|
2026-03-17 10:53:43 +08:00
|
|
|
|
fabric.Image.fromURL(options.url, async (img) => {
|
2026-03-19 11:00:31 +08:00
|
|
|
|
canvasWidth = img.width
|
|
|
|
|
|
canvasHeight = img.height
|
2026-03-17 10:53:43 +08:00
|
|
|
|
img.set({
|
|
|
|
|
|
left: 0,
|
|
|
|
|
|
top: 0,
|
|
|
|
|
|
scaleX: 1,
|
|
|
|
|
|
scaleY: 1,
|
2026-03-18 13:56:27 +08:00
|
|
|
|
evented: false,
|
|
|
|
|
|
selectable: false,
|
2026-03-17 10:53:43 +08:00
|
|
|
|
info: {
|
|
|
|
|
|
id: createId("image"),
|
|
|
|
|
|
name: "图片图层",
|
2026-03-18 13:56:27 +08:00
|
|
|
|
lock: true,
|
2026-03-27 16:07:13 +08:00
|
|
|
|
isOriginal: true,
|
2026-03-17 10:53:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
})
|
2026-03-17 11:35:04 +08:00
|
|
|
|
image = img
|
2026-03-17 10:53:43 +08:00
|
|
|
|
resolve(img)
|
|
|
|
|
|
}, { crossOrigin: 'anonymous' })
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
2026-03-17 11:35:04 +08:00
|
|
|
|
this.canvas = createCanvas(options.canvasRef.value, {
|
|
|
|
|
|
preserveObjectStacking: true,
|
|
|
|
|
|
enableRetinaScaling: true,
|
|
|
|
|
|
backgroundColor: '#fff',
|
|
|
|
|
|
})
|
|
|
|
|
|
if (image) {
|
|
|
|
|
|
this.canvas.add(image)
|
|
|
|
|
|
await this.layerManager.updateLayerThumbnailsById(image.info.id)
|
|
|
|
|
|
}
|
|
|
|
|
|
this.setCanvasViewSize(options)
|
|
|
|
|
|
|
2026-03-09 13:44:32 +08:00
|
|
|
|
this.canvas.clipPath = new fabric.Rect({
|
|
|
|
|
|
left: 0,
|
|
|
|
|
|
top: 0,
|
2026-03-19 11:00:31 +08:00
|
|
|
|
width: canvasWidth,
|
|
|
|
|
|
height: canvasHeight
|
2026-03-09 13:44:32 +08:00
|
|
|
|
})
|
2026-03-11 15:34:56 +08:00
|
|
|
|
|
2026-03-12 11:40:48 +08:00
|
|
|
|
// 动画管理器
|
|
|
|
|
|
this.animationManager = new AnimationManager(this.canvas, {
|
|
|
|
|
|
currentZoom: this.currentZoom,
|
|
|
|
|
|
canvasManager: this,
|
|
|
|
|
|
wheelThrottleTime: 15, // 降低滚轮事件节流时间,提高响应性
|
|
|
|
|
|
defaultEase: "power2.lin",
|
|
|
|
|
|
defaultDuration: 0.3, // 缩短默认动画时间
|
|
|
|
|
|
});
|
2026-03-17 10:53:43 +08:00
|
|
|
|
|
2026-03-12 11:40:48 +08:00
|
|
|
|
this.setupCanvasEvents()
|
|
|
|
|
|
this.setupBrushEvents()
|
|
|
|
|
|
|
2026-03-17 09:36:59 +08:00
|
|
|
|
/** 测试-开始 */
|
2026-03-19 11:00:31 +08:00
|
|
|
|
// this.stateManager.setIsRecord(false)
|
2026-03-23 16:43:08 +08:00
|
|
|
|
// const groupObject = await this.layerManager.createGroupLayer()
|
|
|
|
|
|
// const parentId = groupObject.info.id
|
|
|
|
|
|
// const rect = await this.layerManager.createRectLayer({ left: 200, info: { parentId } })
|
|
|
|
|
|
// const star = await this.layerManager.createStarLayer({ left: 400, info: { parentId } })
|
|
|
|
|
|
// const arrow = await this.layerManager.createArrowLayer({ left: 600, info: { parentId } })
|
|
|
|
|
|
// await this.layerManager.createGroupLayer()
|
2026-03-19 11:00:31 +08:00
|
|
|
|
// this.layerManager.setActiveID(rect.info.id)
|
|
|
|
|
|
// this.stateManager.setIsRecord(true)
|
2026-03-17 09:36:59 +08:00
|
|
|
|
/** 测试-结束 */
|
2026-03-17 10:53:43 +08:00
|
|
|
|
|
|
|
|
|
|
this.resetZoom(false, true)// 画布居中
|
|
|
|
|
|
|
2026-03-24 11:49:53 +08:00
|
|
|
|
this.stateManager.toolManager.setTool(OperationType.SELECT)
|
2026-03-17 10:53:43 +08:00
|
|
|
|
this.layerManager.updateLayers()
|
2026-03-12 11:40:48 +08:00
|
|
|
|
this.stateManager.recordState()
|
2026-03-11 15:34:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
/** 画布添加对象 */
|
2026-03-16 16:51:12 +08:00
|
|
|
|
async add(obj: any, isRecord = true) {
|
2026-03-11 15:34:56 +08:00
|
|
|
|
this.canvas.add(obj)
|
2026-03-13 14:08:40 +08:00
|
|
|
|
const id = obj?.info?.id || ""
|
2026-03-16 16:51:12 +08:00
|
|
|
|
if (id) {
|
2026-03-23 16:43:08 +08:00
|
|
|
|
await this.layerManager.updateLayers(!!obj.info.parentId)
|
2026-03-11 15:34:56 +08:00
|
|
|
|
this.renderAll()
|
2026-03-16 16:51:12 +08:00
|
|
|
|
await this.layerManager.updateLayerThumbnailsById(id)
|
2026-03-11 15:34:56 +08:00
|
|
|
|
}
|
2026-03-16 16:51:12 +08:00
|
|
|
|
if (isRecord) this.stateManager.recordState()
|
2026-03-09 13:44:32 +08:00
|
|
|
|
}
|
2026-03-12 15:51:18 +08:00
|
|
|
|
/** 画布移除对象 */
|
|
|
|
|
|
remove(obj: any, isUpdate = true) {
|
|
|
|
|
|
this.canvas.remove(obj)
|
|
|
|
|
|
if (isUpdate) {
|
|
|
|
|
|
this.layerManager.updateLayers()
|
|
|
|
|
|
this.renderAll()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-03-11 15:34:56 +08:00
|
|
|
|
|
2026-03-23 16:43:08 +08:00
|
|
|
|
/** 更新子图层裁剪区域 */
|
|
|
|
|
|
async updateSubLayerClipPath() {
|
|
|
|
|
|
const objects = this.getObjects().filter((v: any) => v.type !== "group" && !!v.info?.id);
|
|
|
|
|
|
for (let i = 0; i < objects.length; i++) {
|
|
|
|
|
|
let object = objects[i]
|
|
|
|
|
|
if (object.clipPath) object.set({ clipPath: null })
|
|
|
|
|
|
let group = this.getObjectById(object.info.parentId)
|
|
|
|
|
|
if (!group) continue
|
|
|
|
|
|
let path = group.clipPath
|
|
|
|
|
|
if (!path) continue
|
|
|
|
|
|
let clipPath = await cloneObjects([path]).then((v) => v[0])
|
|
|
|
|
|
clipPath.set({
|
|
|
|
|
|
absolutePositioned: true,
|
|
|
|
|
|
})
|
|
|
|
|
|
object.set({ clipPath })
|
|
|
|
|
|
}
|
|
|
|
|
|
this.renderAll()
|
|
|
|
|
|
}
|
|
|
|
|
|
/** 排序画布对象 */
|
|
|
|
|
|
async sortObjectByIds(ids: string[], isRecord?: boolean) {
|
|
|
|
|
|
ids.forEach((id, index) => {
|
|
|
|
|
|
this.canvas.moveTo(this.getObjectById(id), index)
|
|
|
|
|
|
})
|
|
|
|
|
|
await this.updateSubLayerClipPath()
|
|
|
|
|
|
this.renderAll()
|
|
|
|
|
|
if (isRecord) this.stateManager.recordState()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-03-11 15:34:56 +08:00
|
|
|
|
/** 设置画布事件 */
|
2026-03-09 13:44:32 +08:00
|
|
|
|
setupCanvasEvents() {
|
|
|
|
|
|
// 创建画布事件管理器
|
|
|
|
|
|
this.eventManager = new CanvasEventManager(this.canvas, {
|
|
|
|
|
|
canvasManager: this,
|
|
|
|
|
|
animationManager: this.animationManager,
|
|
|
|
|
|
toolManager: this.stateManager.toolManager,
|
2026-03-11 15:34:56 +08:00
|
|
|
|
layerManager: this.stateManager.layerManager,
|
2026-03-12 11:40:48 +08:00
|
|
|
|
stateManager: this.stateManager,
|
2026-03-09 13:44:32 +08:00
|
|
|
|
});
|
|
|
|
|
|
// 设置动画交互效果
|
|
|
|
|
|
this.animationManager.setupInteractionAnimations();
|
|
|
|
|
|
}
|
2026-03-11 15:34:56 +08:00
|
|
|
|
/** 设置激活对象 */
|
2026-03-13 11:18:36 +08:00
|
|
|
|
setActiveObjectById(id: string) {
|
2026-03-23 16:43:08 +08:00
|
|
|
|
this.discardActiveObject()
|
2026-03-11 15:34:56 +08:00
|
|
|
|
const obj = this.getObjectById(id)
|
2026-03-23 16:43:08 +08:00
|
|
|
|
if (!obj) return
|
|
|
|
|
|
if (obj.type === "group") {
|
2026-03-25 11:27:24 +08:00
|
|
|
|
obj._originPosition = {
|
|
|
|
|
|
top: obj.top,
|
|
|
|
|
|
left: obj.left,
|
|
|
|
|
|
cpTop: obj.clipPath.top,
|
|
|
|
|
|
cpLeft: obj.clipPath.left,
|
|
|
|
|
|
}
|
|
|
|
|
|
const objects = [obj];
|
2026-03-23 16:43:08 +08:00
|
|
|
|
this.getObjects().forEach((item: any) => {
|
|
|
|
|
|
if (item?.info?.parentId === id) objects.push(item)
|
|
|
|
|
|
})
|
|
|
|
|
|
if (objects.length > 0) this.canvas.setActiveObject(new fabric.ActiveSelection(objects, { canvas: this.canvas }));
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if (obj.evented) this.canvas.setActiveObject(obj)
|
|
|
|
|
|
}
|
2026-03-11 15:34:56 +08:00
|
|
|
|
this.renderAll()
|
|
|
|
|
|
}
|
2026-03-17 10:53:43 +08:00
|
|
|
|
resetZoom(animated = true, adaptive = true) {
|
|
|
|
|
|
this.animationManager.resetZoom(animated, adaptive)
|
2026-03-09 13:44:32 +08:00
|
|
|
|
}
|
2026-03-16 11:38:58 +08:00
|
|
|
|
// 使用动画管理器的缩放方法
|
|
|
|
|
|
animateZoom(point, targetZoom, options = {}) {
|
|
|
|
|
|
this.animationManager.animateZoom(point, targetZoom, options);
|
|
|
|
|
|
}
|
|
|
|
|
|
zoomIn() {
|
|
|
|
|
|
const currentZoom = this.canvas.getZoom()
|
|
|
|
|
|
const newZoom = Math.min(currentZoom + 0.1, 20) // 增加20%,最大20倍
|
|
|
|
|
|
// 使用画布中心作为缩放点
|
|
|
|
|
|
const centerPoint = {
|
|
|
|
|
|
x: this.canvas.width / 2,
|
|
|
|
|
|
y: this.canvas.height / 2
|
|
|
|
|
|
}
|
|
|
|
|
|
this.animateZoom(centerPoint, newZoom)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
zoomOut() {
|
|
|
|
|
|
const currentZoom = this.canvas.getZoom()
|
|
|
|
|
|
const newZoom = Math.max(currentZoom - 0.1, 0.1) // 减少20%,最小0.1倍
|
|
|
|
|
|
// 使用画布中心作为缩放点
|
|
|
|
|
|
const centerPoint = {
|
|
|
|
|
|
x: this.canvas.width / 2,
|
|
|
|
|
|
y: this.canvas.height / 2
|
|
|
|
|
|
}
|
|
|
|
|
|
this.animateZoom(centerPoint, newZoom)
|
|
|
|
|
|
}
|
2026-03-09 16:45:30 +08:00
|
|
|
|
getObjects() {
|
|
|
|
|
|
return this.canvas.getObjects() || []
|
|
|
|
|
|
}
|
|
|
|
|
|
getObjectById(id: string) {
|
2026-03-12 15:51:18 +08:00
|
|
|
|
return this.getObjects().find((item: any) => item?.info?.id === id)
|
2026-03-09 16:45:30 +08:00
|
|
|
|
}
|
2026-03-25 11:27:24 +08:00
|
|
|
|
getChildObjectsById(id: string) {
|
|
|
|
|
|
return this.getObjects().filter((item: any) => item?.info?.parentId === id)
|
|
|
|
|
|
}
|
2026-03-18 13:56:27 +08:00
|
|
|
|
/** 获取选中对象 */
|
|
|
|
|
|
getSelectedObject() {
|
|
|
|
|
|
return this.canvas.getActiveObject()
|
2026-03-13 17:31:47 +08:00
|
|
|
|
}
|
2026-03-09 16:45:30 +08:00
|
|
|
|
renderAll() {
|
|
|
|
|
|
this.canvas.renderAll()
|
|
|
|
|
|
}
|
2026-03-23 16:43:08 +08:00
|
|
|
|
deleteObjectById(id: string, isUpdate = true) {
|
2026-03-09 16:45:30 +08:00
|
|
|
|
const object = this.getObjectById(id)
|
|
|
|
|
|
if (object) {
|
|
|
|
|
|
this.canvas.remove(object)
|
2026-03-23 16:43:08 +08:00
|
|
|
|
if (isUpdate) this.layerManager.updateLayers()
|
2026-03-09 16:45:30 +08:00
|
|
|
|
this.renderAll()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-03-18 13:56:27 +08:00
|
|
|
|
/** 取消选中对象 */
|
|
|
|
|
|
discardActiveObject() {
|
|
|
|
|
|
this.canvas.discardActiveObject()
|
|
|
|
|
|
this.renderAll()
|
|
|
|
|
|
}
|
2026-03-09 13:44:32 +08:00
|
|
|
|
|
2026-03-11 15:34:56 +08:00
|
|
|
|
/** 画笔事件 */
|
|
|
|
|
|
setupBrushEvents() {
|
|
|
|
|
|
this.canvas.onBrushImageConverted = async (fabricImage) => {
|
|
|
|
|
|
const currentTool = this.stateManager.toolManager.currentTool.value;
|
|
|
|
|
|
if (currentTool === OperationType.DRAW) {
|
|
|
|
|
|
this.handleDrawImage(fabricImage)
|
2026-03-25 11:27:24 +08:00
|
|
|
|
} else if (currentTool === OperationType.AISELECT_DRAW) {
|
2026-03-25 14:37:20 +08:00
|
|
|
|
this.stateManager.aiSelectboxToolManager.handleDrawImage(fabricImage)
|
2026-03-11 15:34:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
return true
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
/** 处理绘制图像 */
|
2026-03-23 16:43:08 +08:00
|
|
|
|
async handleDrawImage(fabricImage: fabric.Object) {
|
2026-03-11 15:34:56 +08:00
|
|
|
|
const activeID = this.stateManager.layerManager.activeID.value
|
|
|
|
|
|
const activeLayer = this.getObjectById(activeID)
|
2026-03-25 11:27:24 +08:00
|
|
|
|
if (activeLayer && activeLayer.fill?.repeat !== "repeat" && activeLayer.type !== "group") {
|
2026-03-11 15:34:56 +08:00
|
|
|
|
this.layerManager.imageMergeToLayer(activeLayer, fabricImage)
|
|
|
|
|
|
} else {
|
2026-03-23 16:43:08 +08:00
|
|
|
|
const emptyLayer = await this.layerManager.createEmptyLayer(false);
|
2026-03-11 15:34:56 +08:00
|
|
|
|
this.layerManager.setActiveID(emptyLayer.info.id, false)
|
|
|
|
|
|
this.layerManager.imageMergeToLayer(emptyLayer, fabricImage)
|
|
|
|
|
|
}
|
|
|
|
|
|
return true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 导出画布为JSON */
|
|
|
|
|
|
getCanvasJSON() {
|
2026-03-13 11:18:36 +08:00
|
|
|
|
const json = this.canvas.toJSON()
|
2026-03-11 15:34:56 +08:00
|
|
|
|
return JSON.stringify(json)
|
|
|
|
|
|
}
|
|
|
|
|
|
/** 加载画布JSON */
|
2026-03-19 13:17:03 +08:00
|
|
|
|
loadJSON(json: string, rerecord = true) {
|
2026-03-19 11:00:31 +08:00
|
|
|
|
return new Promise((resolve) => {
|
|
|
|
|
|
let jsonObj = null;
|
|
|
|
|
|
try {
|
|
|
|
|
|
jsonObj = JSON.parse(json)
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('JSON解析错误:', error)
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!jsonObj) return resolve(false)
|
2026-03-20 14:35:46 +08:00
|
|
|
|
if (jsonObj.uniqueId) this.uniqueId = jsonObj.uniqueId
|
2026-03-19 11:00:31 +08:00
|
|
|
|
this.canvas.loadFromJSON(jsonObj, () => {
|
2026-03-19 13:17:03 +08:00
|
|
|
|
if (rerecord) this.stateManager.clearState()
|
2026-03-19 11:00:31 +08:00
|
|
|
|
this.resetZoom(false)
|
|
|
|
|
|
this.layerManager.updateLayers()
|
|
|
|
|
|
this.renderAll()
|
2026-03-19 13:17:03 +08:00
|
|
|
|
if (rerecord) this.stateManager.recordState()
|
2026-03-19 11:00:31 +08:00
|
|
|
|
|
|
|
|
|
|
resolve(true)
|
|
|
|
|
|
})
|
2026-03-11 15:34:56 +08:00
|
|
|
|
})
|
|
|
|
|
|
}
|
2026-03-18 17:25:19 +08:00
|
|
|
|
/** 导出画布为处理图片的JSON */
|
|
|
|
|
|
getCanvasDisUrlJSON() {
|
|
|
|
|
|
const canvas = this.canvas.toJSON()
|
2026-03-20 09:46:15 +08:00
|
|
|
|
const images = {};
|
2026-03-20 14:35:46 +08:00
|
|
|
|
const create = (url, key) => {
|
|
|
|
|
|
const key_ = `xxxxx_${this.uniqueId}_${md5(key)}_xxxxx`;
|
|
|
|
|
|
images[key_] = url
|
|
|
|
|
|
return key_
|
2026-03-18 17:25:19 +08:00
|
|
|
|
}
|
2026-03-20 14:35:46 +08:00
|
|
|
|
canvas.uniqueId = this.uniqueId
|
2026-03-18 17:25:19 +08:00
|
|
|
|
canvas.objects.forEach((object: any) => {
|
2026-03-20 14:35:46 +08:00
|
|
|
|
const id = object.info?.id
|
2026-03-18 17:25:19 +08:00
|
|
|
|
if (object.thumbnail) {
|
2026-03-20 14:35:46 +08:00
|
|
|
|
object.thumbnail = create(object.thumbnail, `thumbnail_${id}`)
|
2026-03-18 17:25:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
if (object.src) {
|
2026-03-20 14:35:46 +08:00
|
|
|
|
object.src = create(object.src, `src_${id}`)
|
2026-03-20 13:59:53 +08:00
|
|
|
|
object.crossOrigin = 'anonymous'
|
2026-03-18 17:25:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
if (object.fill?.source) {
|
2026-03-20 14:35:46 +08:00
|
|
|
|
object.fill.source = create(object.fill.source, `fillsource_${id}`)
|
2026-03-18 17:25:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
if (object.info?.fill?.source) {
|
2026-03-20 14:35:46 +08:00
|
|
|
|
object.info.fill.source = create(object.info.fill.source, `infofillsource_${id}`)
|
2026-03-18 17:25:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
return { canvas: JSON.stringify(canvas), images }
|
|
|
|
|
|
}
|
|
|
|
|
|
/** 处理JSON为正常画布 */
|
2026-03-20 09:46:15 +08:00
|
|
|
|
processCanvasDisUrlJSON(obj: { canvas: string, images: Object }) {
|
2026-03-18 17:25:19 +08:00
|
|
|
|
var json = obj.canvas;
|
2026-03-20 09:46:15 +08:00
|
|
|
|
const images = obj.images || {}
|
|
|
|
|
|
for (const key in images) {
|
|
|
|
|
|
json = json.replace(new RegExp(key), images[key])
|
|
|
|
|
|
}
|
|
|
|
|
|
return json
|
2026-03-18 17:25:19 +08:00
|
|
|
|
}
|
2026-03-12 11:40:48 +08:00
|
|
|
|
dispose() {
|
|
|
|
|
|
this.animationManager?.dispose()
|
|
|
|
|
|
this.eventManager?.dispose()
|
|
|
|
|
|
}
|
2026-03-09 13:44:32 +08:00
|
|
|
|
}
|