Files
FiDA_Front/src/components/Canvas/DepthCanvas/manager/CanvasManager.ts

203 lines
5.5 KiB
TypeScript
Raw Normal View History

2026-03-09 13:44:32 +08:00
import { fabric } from 'fabric-with-all'
import { ref } from 'vue'
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'
interface CanvasInitOptions {
canvasRef: any
canvasViewWidth?: number
canvasViewHeight?: number
canvasWidth?: number
canvasHeight?: number
}
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
canvasViewWidth: number
canvasViewHeight: number
canvasWidth: number
canvasHeight: number
currentZoom: any
constructor(options) {
this.stateManager = options.stateManager;
this.deviceInfo = detectDeviceType();
this.currentZoom = ref(100)
}
setCanvasViewSize(options) {
this.canvasViewWidth = options.canvasViewWidth || 1920
this.canvasViewHeight = options.canvasViewHeight || 1080
}
initCanvas(options: CanvasInitOptions) {
2026-03-09 16:45:30 +08:00
this.layerManager = this.stateManager.layerManager
2026-03-09 13:44:32 +08:00
this.setCanvasViewSize(options)
this.canvasWidth = options.canvasWidth || 750
this.canvasHeight = options.canvasHeight || 600
this.canvas = createCanvas(options.canvasRef.value, {
width: this.canvasViewWidth,
height: this.canvasViewHeight,
preserveObjectStacking: true,
enableRetinaScaling: true,
stopContextMenu: true,
fireRightClick: true,
backgroundColor: '#fff',
})
this.canvas.clipPath = new fabric.Rect({
left: 0,
top: 0,
width: this.canvasWidth,
height: this.canvasHeight
})
// 画布居中
const canvasX = this.canvasViewWidth / 2 - this.canvasWidth / 2
const canvasY = this.canvasViewHeight / 2 - this.canvasHeight / 2
this.canvas.viewportTransform = [1, 0, 0, 1, canvasX, canvasY]
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, // 缩短默认动画时间
});
this.setupCanvasEvents()
this.setupBrushEvents()
this.stateManager.toolManager.setTool(OperationType.SELECT)
2026-03-09 13:44:32 +08:00
// 创建矩形
2026-03-11 15:34:56 +08:00
const rect = this.layerManager.createRectLayer({
left: 400,
top: 100,
2026-03-09 13:44:32 +08:00
})
//创建圆形
2026-03-11 15:34:56 +08:00
const circle = this.layerManager.createCircleLayer({
2026-03-09 13:44:32 +08:00
left: 200,
top: 200,
2026-03-09 16:45:30 +08:00
})
// 文字
2026-03-11 15:34:56 +08:00
const text = this.layerManager.createTextLayer('Hello World');
2026-03-09 16:45:30 +08:00
this.layerManager.updateLayers()
2026-03-11 15:34:56 +08:00
this.layerManager.setActiveID(text.info.id)
2026-03-12 11:40:48 +08:00
this.stateManager.recordState()
2026-03-11 15:34:56 +08:00
}
/** 画布添加对象 */
add(obj: any, isUpdate = true) {
this.canvas.add(obj)
if (isUpdate) {
this.layerManager.updateLayers()
this.renderAll()
}
2026-03-09 13:44:32 +08:00
}
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
/** 设置激活对象 */
setActiveObjectByID(id: string) {
const obj = this.getObjectById(id)
if (obj) this.canvas.setActiveObject(obj)
this.renderAll()
}
2026-03-09 13:44:32 +08:00
resetZoom() {
this.animationManager.resetZoom()
}
2026-03-09 16:45:30 +08:00
getObjects() {
return this.canvas.getObjects() || []
}
getObjectById(id: string) {
return this.getObjects().find((item: any) => item.info.id === id)
}
renderAll() {
this.canvas.renderAll()
}
2026-03-09 13:44:32 +08:00
2026-03-09 16:45:30 +08:00
deleteObjectById(id: string) {
const object = this.getObjectById(id)
if (object) {
this.canvas.remove(object)
this.layerManager.updateLayers()
this.renderAll()
}
}
// 拖拽排序
dragSort(id, newIndex) {
this.canvas.moveTo(this.getObjectById(id), newIndex)
this.layerManager.updateLayers()
this.renderAll()
}
getBitObjects() {
return this.getObjects().map(v => {
const object = v.toJSON("info");
return object
})
}
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)
}
return true
};
}
/** 处理绘制图像 */
handleDrawImage(fabricImage: fabric.Object) {
const activeID = this.stateManager.layerManager.activeID.value
const activeLayer = this.getObjectById(activeID)
if (activeLayer) {
this.layerManager.imageMergeToLayer(activeLayer, fabricImage)
} else {
const emptyLayer = this.layerManager.createEmptyLayer();
this.layerManager.setActiveID(emptyLayer.info.id, false)
this.layerManager.imageMergeToLayer(emptyLayer, fabricImage)
}
return true
}
/** 导出画布为JSON */
getCanvasJSON() {
const keys = ["top", "left", "width", "height", "scaleX", "scaleY", "info",]
const json = this.canvas.toJSON(keys)
return JSON.stringify(json)
}
/** 加载画布JSON */
loadJSON(json: string, callback?: (success: boolean) => void) {
let jsonObj = null;
try {
jsonObj = JSON.parse(json)
} catch (error) {
console.error('JSON解析错误:', error)
}
if (!jsonObj) return callback?.(false);
this.canvas.loadFromJSON(jsonObj, () => {
this.layerManager.updateLayers()
this.renderAll()
callback?.(true)
})
2026-03-09 13:44:32 +08:00
2026-03-11 15:34:56 +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
}