This commit is contained in:
lzp
2026-03-13 11:18:36 +08:00
parent c2e26f0328
commit 3b320d0867
12 changed files with 180 additions and 50 deletions

View File

@@ -0,0 +1,65 @@
import { fabric } from 'fabric-with-all'
/** 智能框选工具管理器 */
export class AISelectboxToolManager {
// 管理器
canvasManager: any
stateManager: any
layerManager: any
isDragging: boolean = false
startX: number = 0
startY: number = 0
demoObject: any
constructor(options) {
this.canvasManager = options.canvasManager
this.stateManager = options.stateManager
this.layerManager = options.layerManager
}
mouseDownEvent(e) {
this.isDragging = true
this.startX = e.absolutePointer.x
this.startY = e.absolutePointer.y
const rect = new fabric.Rect({
left: this.startX,
top: this.startY,
width: 0,
height: 0,
fill: 'transparent',
stroke: '#000',
strokeWidth: 1,
evented: false,
})
this.demoObject = rect
this.canvasManager.canvas.add(rect)
this.canvasManager.canvas.renderAll()
}
mouseMoveEvent(e) {
if (!this.isDragging) return;
var width = e.absolutePointer.x - this.startX
var height = e.absolutePointer.y - this.startY
var left = this.startX
var top = this.startY
if(width < 0) {
left += width
width = -width
}
if(height < 0) {
top += height
height = -height
}
this.demoObject.set({ width, height, left, top })
this.canvasManager.canvas.renderAll()
}
mouseUpEvent(e) {
if (!this.isDragging) return;
this.isDragging = false;
const object = this.demoObject.toJSON("evented")
if (object.width === 0) object.width = 100
if (object.height === 0) object.height = 100
// console.log(object)
this.canvasManager.canvas.remove(this.demoObject)
this.canvasManager.canvas.renderAll()
}
dispose() { }
}

View File

@@ -6,6 +6,22 @@ import { detectDeviceType } from '../tools/index'
import { CanvasEventManager } from "./events/CanvasEventManager";
import { OperationType } from '../tools/layerHelper'
// 自定义画布转对象属性
fabric.Object.prototype.customProperties = ["top", "left", "width", "height", "scaleX", "scaleY", "info", "thumbnail"];
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)
}
})
return this.toObject_(arr)
}
interface CanvasInitOptions {
canvasRef: any
canvasViewWidth?: number
@@ -43,8 +59,6 @@ export class CanvasManager {
this.canvas = createCanvas(options.canvasRef.value, {
preserveObjectStacking: true,
enableRetinaScaling: true,
stopContextMenu: true,
fireRightClick: true,
backgroundColor: '#fff',
})
this.setCanvasViewSize(options)
@@ -118,7 +132,7 @@ export class CanvasManager {
this.animationManager.setupInteractionAnimations();
}
/** 设置激活对象 */
setActiveObjectByID(id: string) {
setActiveObjectById(id: string) {
const obj = this.getObjectById(id)
console.log(obj)
if (obj) this.canvas.setActiveObject(obj)
@@ -184,8 +198,7 @@ export class CanvasManager {
/** 导出画布为JSON */
getCanvasJSON() {
const keys = ["top", "left", "width", "height", "scaleX", "scaleY", "info",]
const json = this.canvas.toJSON(keys)
const json = this.canvas.toJSON()
return JSON.stringify(json)
}
/** 加载画布JSON */

View File

@@ -1,7 +1,7 @@
import { ref } from 'vue'
import { fabric } from 'fabric-with-all'
import { createId } from '../../tools/tools'
import { exportObjectsToImage } from '../tools/exportMethod'
import { exportObjectsToImage, exportObjectToThumbnail } from '../tools/exportMethod'
import { OperationType } from '../tools/layerHelper'
export class LayerManager {
@@ -18,22 +18,22 @@ export class LayerManager {
setActiveID(id: string, isActive = true) {
this.activeID.value = id
if (isActive) {
this.canvasManager.setActiveObjectByID(id)
this.canvasManager.setActiveObjectById(id)
this.stateManager.toolManager.setTool(OperationType.SELECT)
}
}
getLayerByID(id) {
getLayerById(id) {
return this.layers.value.find((item: any) => item.info.id === id)
}
setLayerNameByID(id, name: string) {
const layer = this.getLayerByID(id)
setLayerNameById(id, name: string) {
const layer = this.getLayerById(id)
if (layer) {
layer.info.name = name
this.canvasManager.renderAll()
}
}
setLayerVisibleByID(id, visible: boolean) {
const layer = this.getLayerByID(id)
setLayerVisibleById(id, visible: boolean) {
const layer = this.getLayerById(id)
if (layer) {
layer.set({
visible: visible
@@ -41,7 +41,7 @@ export class LayerManager {
this.canvasManager.renderAll()
}
}
deleteLayerByID(id, isActive = true) {
deleteLayerById(id, isActive = true) {
this.canvasManager.deleteObjectById(id)
if (id === this.activeID.value && isActive) {
this.setActiveID(this.layers.value[0]?.info?.id || "")
@@ -54,18 +54,10 @@ export class LayerManager {
}
// 更新图层列表
updateLayers() {
this.layers.value = this.canvasManager.getObjects().filter((v: any) => !!v?.info?.id).reverse()
window["layers"] = this.layers
}
// 更新图层参数
updateLayerParams(layer, keys = []) {
this.layers.value.forEach((item: any) => {
if (item.info.id === layer.info.id) {
keys.forEach((key: string) => {
item.set(key, layer[key])
})
}
})
this.layers.value = this.canvasManager.getObjects()
.filter((v: any) => !!v?.info?.id)
.reverse()
.map(v => v.toObject())
}
/** 设置图层位置-不设置默认居中 */
@@ -202,7 +194,7 @@ export class LayerManager {
})
// console.log(mergedImage)
const index = this.canvasManager.getObjects().indexOf(targetLayer);
this.deleteLayerByID(targetLayer.info.id, false)
this.deleteLayerById(targetLayer.info.id, false)
this.setActiveID(mergedImage.info.id, false)
this.canvasManager.add(mergedImage, false);
this.canvasManager.canvas.moveTo(mergedImage, index);
@@ -219,5 +211,19 @@ export class LayerManager {
})
})
}
/** 更新图层缩略图 */
async updateLayerThumbnailsById(id: string) {
const object = this.canvasManager.getObjectById(id);
if (!object) return;
const url = await exportObjectToThumbnail(object);
object.set({
thumbnail: url
})
// object.thumbnail = url
this.updateLayers()
// this.canvasManager.renderAll()
}
dispose() { }
}

View File

@@ -33,28 +33,27 @@ export class RectToolManager {
}
mouseMoveEvent(e) {
if (!this.isDragging) return;
const width = e.absolutePointer.x - this.startX
const height = e.absolutePointer.y - this.startY
this.demoObject.set({ width, height })
var width = e.absolutePointer.x - this.startX
var height = e.absolutePointer.y - this.startY
var left = this.startX
var top = this.startY
if (width < 0) {
left += width
width = -width
}
if (height < 0) {
top += height
height = -height
}
this.demoObject.set({ width, height, left, top })
this.canvasManager.canvas.renderAll()
}
mouseUpEvent(e) {
if (!this.isDragging) return;
this.isDragging = false;
var width = e.absolutePointer.x - this.startX
var height = e.absolutePointer.y - this.startY
if(width === 0) width = 50
if(height === 0) height = 50
this.demoObject.set({ width, height })
const object = this.demoObject.toJSON("evented")
if(object.width < 0) {
object.left += object.width
object.width = -object.width
}
if(object.height < 0) {
object.top += object.height
object.height = -object.height
}
if (object.width === 0) object.width = 100
if (object.height === 0) object.height = 100
this.layerManager.createRectLayer(object, true)
this.canvasManager.canvas.remove(this.demoObject)
this.canvasManager.canvas.renderAll()

View File

@@ -1,6 +1,8 @@
import { isBoolean } from "lodash-es";
import { OperationType, OperationTypes } from "../../tools/layerHelper";
import { RectToolManager } from "../RectToolManager"
import { AISelectboxToolManager } from "../AISelectboxToolManager"
export class CanvasEventManager {
constructor(canvas, options = {}) {
@@ -31,6 +33,7 @@ export class CanvasEventManager {
layerManager: this.layerManager,
}
this.rectToolManager = new RectToolManager(managers)
this.aiSelectboxToolManager = new AISelectboxToolManager(managers)
// 初始化所有事件
this.initEvents();
@@ -205,6 +208,7 @@ export class CanvasEventManager {
// 橡皮擦模式
} else if (currentTool === OperationType.SELECTBOX) {
// 选择框模式
this.aiSelectboxToolManager.mouseDownEvent(opt);
} else if (currentTool === OperationType.RECTANGLE) {
// 矩形模式
this.rectToolManager.mouseDownEvent(opt);
@@ -232,6 +236,7 @@ export class CanvasEventManager {
// 橡皮擦模式
} else if (currentTool === OperationType.SELECTBOX) {
// 选择框模式
this.aiSelectboxToolManager.mouseMoveEvent(opt);
} else if (currentTool === OperationType.RECTANGLE) {
// 矩形模式
this.rectToolManager.mouseMoveEvent(opt);
@@ -315,6 +320,7 @@ export class CanvasEventManager {
// 橡皮擦模式
} else if (currentTool === OperationType.SELECTBOX) {
// 选择框模式
this.aiSelectboxToolManager.mouseUpEvent(opt);
} else if (currentTool === OperationType.RECTANGLE) {
// 矩形模式
this.rectToolManager.mouseDownEvent(opt);
@@ -379,6 +385,7 @@ export class CanvasEventManager {
// 橡皮擦模式
} else if (currentTool === OperationType.SELECTBOX) {
// 选择框模式
this.aiSelectboxToolManager.mouseMoveEvent(opt);
} else if (currentTool === OperationType.RECTANGLE) {
// 矩形模式
this.rectToolManager.mouseMoveEvent(opt);
@@ -488,6 +495,7 @@ export class CanvasEventManager {
// 橡皮擦模式
} else if (currentTool === OperationType.SELECTBOX) {
// 选择框模式
this.aiSelectboxToolManager.mouseUpEvent(opt);
} else if (currentTool === OperationType.RECTANGLE) {
// 矩形模式
this.rectToolManager.mouseUpEvent(opt);
@@ -659,6 +667,7 @@ export class CanvasEventManager {
// 橡皮擦模式
} else if (currentTool === OperationType.SELECTBOX) {
// 选择框模式
this.aiSelectboxToolManager.mouseUpEvent(opt);
} else if (currentTool === OperationType.RECTANGLE) {
// 矩形模式
this.rectToolManager.mouseUpEvent(opt);
@@ -722,6 +731,7 @@ export class CanvasEventManager {
this.canvas.on("object:modified", (e) => {
updateLayers(e);
this.stateManager.recordState();
this.layerManager.updateLayerThumbnailsById(e.target.info.id);
});
this.canvas.on("object:removed", (e) => {
updateLayers(e);
@@ -1065,6 +1075,7 @@ export class CanvasEventManager {
dispose() {
this.rectToolManager?.dispose()
this.aiSelectboxToolManager?.dispose()
// 移除所有事件监听
this.canvas.off();