Files
FiDA_Front/src/components/Canvas/DepthCanvas/manager/LayerManager.ts
2026-03-19 11:00:31 +08:00

371 lines
10 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { ref } from 'vue'
import { fabric } from 'fabric-with-all'
import { createId } from '../../tools/tools'
import { exportObjectsToImage, exportObjectToThumbnail } from '../tools/exportMethod'
import { OperationType } from '../tools/layerHelper'
import { getArrowPath, cloneObjects, getStarArr } from '../tools/canvasMethod'
export class LayerManager {
stateManager: any
canvasManager: any
layers: any
activeID: any
constructor(options) {
this.stateManager = options.stateManager;
this.canvasManager = options.canvasManager;
this.layers = ref([])
this.activeID = ref("")
}
onMounted() { }
setActiveID(id: string, isActive = true) {
this.activeID.value = id
if (isActive) {
this.canvasManager.setActiveObjectById(id)
this.stateManager.toolManager.setTool(OperationType.SELECT)
}
}
getActiveLayer() {
return this.getLayerById(this.activeID.value)
}
getLayerById(id) {
return this.layers.value.find((item: any) => item.info.id === id)
}
setLayerNameById(id, name: string) {
const layer = this.getLayerById(id)
if (layer) {
layer.info.name = name
}
const object = this.canvasManager.getObjectById(id)
if (object) {
object.info.name = name
this.canvasManager.renderAll()
}
}
/** 设置图层显示状态 */
setLayerVisibleById(id, visible: boolean) {
const layer = this.getLayerById(id)
const object = this.canvasManager.getObjectById(id)
if (!layer || !object) return
layer.visible = visible
object.set({
visible: visible
})
this.canvasManager.renderAll()
this.stateManager.recordState()
}
/** 设置图层锁定状态 */
setLayerLockById(id, lock: boolean) {
const layer = this.getLayerById(id)
const object = this.canvasManager.getObjectById(id)
if (!layer || !object) return
layer.info.lock = !!lock
layer.evented = !lock
object.info.lock = !!lock
object.set({
evented: !lock,
selectable: !lock,
})
if (lock) {
// 取消选中对象
const e = this.canvasManager.getSelectedObject()
if (e) {
const objects = [...(e._objects || [e])]
if (objects.some(v => v.info?.id === object.info.id)) {
this.canvasManager.discardActiveObject()
}
}
}
this.canvasManager.renderAll()
this.stateManager.recordState()
}
/** 删除指定图层 */
deleteLayerById(id, isActive = true) {
this.canvasManager.deleteObjectById(id)
if (id === this.activeID.value && isActive) {
this.setActiveID(this.layers.value[0]?.info?.id || "")
}
if (isActive) this.stateManager.recordState()
}
/** 复制指定图层 */
copyLayerById(id) {
const object = this.canvasManager.getObjectById(id)
if (!object) return console.warn('复制图层失败对象不存在ID:', id)
cloneObjects([object]).then(objects => {
const newObject = objects[0]
const info = JSON.parse(JSON.stringify(newObject.info))
info.id = createId("image")
// info.name = info.name
newObject.set({
top: newObject.top + 15,
left: newObject.left + 15,
info: info,
})
this.canvasManager.add(newObject)
this.setActiveID(newObject.info.id)
})
}
// 拖拽排序
dragSort(id, newIndex) {
const index = Math.abs(this.layers.value.length - newIndex - 1)
this.canvasManager.dragSort(id, index)
}
// 更新图层列表
updateLayers() {
this.layers.value = this.canvasManager.getObjects()
.filter((v: any) => !!v?.info?.id)
.reverse()
.map(v => v.toObject())
}
/** 设置图层位置-不设置默认居中 */
setLayerPosition(object, options?: any) {
const width = this.canvasManager.canvas.clipPath.width
const height = this.canvasManager.canvas.clipPath.height
if (options && options.top !== undefined) {
object.set({ top: options.top })
} else {
object.set({ top: height / 2 - object.height * object.scaleY / 2 })
}
if (options && options.left !== undefined) {
object.set({ left: options.left })
} else {
object.set({ left: width / 2 - object.width * object.scaleX / 2 })
}
}
/** 创建空图层 */
createEmptyLayer(isRecord = true, isActive = false) {
const emptyObject = new fabric.Rect({
width: 0,
height: 0,
fill: 'transparent',
info: {
id: createId("image"),
name: '空图层',
}
})
this.setLayerPosition(emptyObject)
this.canvasManager.add(emptyObject, isRecord)
if (isActive) this.setActiveID(emptyObject.info.id, false)
return emptyObject
}
/** 创建文本图层 */
async createTextLayer(text: string, options?: any) {
const textObject = new fabric.IText(text, {
fontSize: 24,
fill: '#000',
...(options || {}),
info: {
id: createId("text"),
name: '文本图层',
...(options?.info || {}),
}
})
this.setLayerPosition(textObject, options)
await this.canvasManager.add(textObject)
return textObject
}
/** 创建矩形图层 */
async createRectLayer(options?: any, isActive = false) {
const rectObject = new fabric.Rect({
width: 100,
height: 100,
fill: '#000',
strokeWidth: 0,
...(options || {}),
info: {
id: createId("rect"),
name: '矩形图层',
...(options?.info || {}),
}
})
this.setLayerPosition(rectObject, options)
await this.canvasManager.add(rectObject)
if (isActive) this.setActiveID(rectObject.info.id)
return rectObject
}
/** 创建直线图层 */
async createLineLayer(options?: any, isActive = false) {
const line = [options?.x1 || 0, options?.y1 || 0, options?.x2 || 100, options?.y2 || 0]
delete options.x1
delete options.y1
delete options.x2
delete options.y2
const lineObject = new fabric.Line(line, {
stroke: 'black', // 线条颜色
strokeWidth: 2, // 线条粗细
...(options || {}),
info: {
id: createId("line"),
name: '直线图层',
...(options?.info || {}),
}
})
this.setLayerPosition(lineObject, options)
await this.canvasManager.add(lineObject)
if (isActive) this.setActiveID(lineObject.info.id)
return lineObject
}
/** 创建椭圆图层 */
async createEllipseLayer(options?: any, isActive = false) {
const ellipseObject = new fabric.Ellipse({
radius: 50,
fill: '#000',
strokeWidth: 0,
...(options || {}),
info: {
id: createId("ellipse"),
name: '椭圆图层',
...(options?.info || {}),
}
})
this.setLayerPosition(ellipseObject, options)
await this.canvasManager.add(ellipseObject)
if (isActive) this.setActiveID(ellipseObject.info.id)
return ellipseObject
}
/** 创建三角形图层 */
async createTriangleLayer(options?: any, isActive = false) {
const triangleObject = new fabric.Triangle({
width: 100,
height: 100,
fill: '#000',
strokeWidth: 0,
...(options || {}),
info: {
id: createId("triangle"),
name: '三角形图层',
...(options?.info || {}),
}
})
this.setLayerPosition(triangleObject, options)
await this.canvasManager.add(triangleObject)
if (isActive) this.setActiveID(triangleObject.info.id)
return triangleObject
}
/** 创建五角星图层 */
async createStarLayer(options?: any, isActive = false) {
const width = options?.width || 100
const height = options?.height || 100
delete options.points
const starObject = new fabric.Polygon(getStarArr(width, height), {
fill: '#000',
strokeWidth: 0,
...(options || {}),
info: {
id: createId("star"),
name: '五角星图层',
...(options?.info || {}),
}
})
this.setLayerPosition(starObject, options)
await this.canvasManager.add(starObject)
if (isActive) this.setActiveID(starObject.info.id)
return starObject
}
/** 创建箭头图层 */
async createArrowLayer(options?: any, isActive = false) {
const width = options?.width || 100
const height = options?.height || 10
delete options.width
delete options.height
const arrowObject = new fabric.Path(getArrowPath(width, height), {
stroke: '#000', // 只设置边框颜色
strokeWidth: 3, // 边框宽度
fill: 'transparent', // 不填充
strokeLineCap: 'round',
strokeLineJoin: 'round',
...(options || {}),
info: {
id: createId("star"),
name: '箭头图层',
...(options?.info || {}),
}
});
this.setLayerPosition(arrowObject, options)
this.canvasManager.add(arrowObject)
if (isActive) this.setActiveID(arrowObject.info.id)
return arrowObject
}
/** 创建图片图层 */
async createImageLayer(imgOrUrl: string | HTMLImageElement, options?: any, isRecord = true) {
const { canvasWidth, canvasHeight } = this.canvasManager.getCanvasSize();
const imageObject = await new Promise((resolve) => {
const url = typeof imgOrUrl === 'string' ? imgOrUrl : imgOrUrl.src
fabric.Image.fromURL(url, (img) => {
const width = img.width
const height = img.height
const scaleX = width > canvasWidth ? canvasWidth * 0.8 / width : 1
const scaleY = height > canvasHeight ? canvasHeight * 0.8 / height : 1
const scale = Math.min(scaleX, scaleY)
img.set({
scaleX: scale,
scaleY: scale,
...(options || {}),
info: {
id: createId("image"),
name: "图片图层",
...(options?.info || {}),
}
})
resolve(img)
})
}) as fabric.Object
this.setLayerPosition(imageObject, options)
await this.canvasManager.add(imageObject, isRecord)
this.setActiveID(imageObject.info.id)
return imageObject
}
/** 合并图层 */
async imageMergeToLayer(targetLayer: fabric.Object, fabricImage: fabric.Object) {
const info = await exportObjectsToImage([targetLayer, fabricImage], true)
const mergedImage = await new Promise((resolve) => {
fabric.Image.fromURL(info.url, (img) => {
img.set({
left: info.left,
top: info.top,
info: {
id: createId("image"),
name: targetLayer?.info?.name || "合并图层",
}
})
resolve(img)
})
})
// console.log(mergedImage)
const index = this.canvasManager.getObjects().indexOf(targetLayer);
this.deleteLayerById(targetLayer.info.id, false)
this.setActiveID(mergedImage.info.id, false)
await this.canvasManager.add(mergedImage);
this.canvasManager.canvas.moveTo(mergedImage, index);
this.canvasManager.renderAll()
this.updateLayers()
return true;
}
/** 设置激活对象可擦除 */
setActiveObjectErasable() {
const objects = this.canvasManager.getObjects()
objects.forEach((item: any) => {
item.set({
erasable: item.info.id === this.activeID.value
})
})
}
/** 更新图层缩略图 */
async updateLayerThumbnailsById(id: string) {
const object = this.canvasManager.getObjectById(id);
if (!object) return;
const url = await exportObjectToThumbnail(object);
object.thumbnail = url
this.updateLayers()
}
dispose() { }
}