2026-03-17 17:17:48 +08:00
|
|
|
import { OperationType, OperationTypes } from "../tools/layerHelper";
|
2026-03-26 10:53:01 +08:00
|
|
|
import { getStarArr, getArrowPath, getLinePath, distance, angleBetweenPointsDegrees } from "../tools/canvasMethod";
|
2026-03-17 17:17:48 +08:00
|
|
|
import { fabric } from 'fabric-with-all'
|
|
|
|
|
/** 形状管理器 */
|
|
|
|
|
export class ShapeToolManager {
|
|
|
|
|
// 管理器
|
|
|
|
|
canvasManager: any
|
|
|
|
|
stateManager: any
|
|
|
|
|
layerManager: any
|
|
|
|
|
toolManager: any
|
|
|
|
|
|
|
|
|
|
isDragging: boolean = false
|
|
|
|
|
startX: number = 0
|
|
|
|
|
startY: number = 0
|
|
|
|
|
demoObject: any
|
|
|
|
|
tools = [
|
|
|
|
|
OperationType.RECTANGLE,
|
|
|
|
|
OperationType.LINE,
|
|
|
|
|
OperationType.ARROW,
|
|
|
|
|
OperationType.ELLIPSE,
|
|
|
|
|
OperationType.TRIANGLE,
|
|
|
|
|
OperationType.STAR,
|
|
|
|
|
]
|
|
|
|
|
constructor(options) {
|
|
|
|
|
this.canvasManager = options.canvasManager
|
|
|
|
|
this.stateManager = options.stateManager
|
|
|
|
|
this.layerManager = options.layerManager
|
|
|
|
|
this.toolManager = options.toolManager
|
|
|
|
|
}
|
|
|
|
|
mouseDownEvent(e) {
|
|
|
|
|
this.isDragging = false
|
|
|
|
|
this.demoObject = null
|
|
|
|
|
|
|
|
|
|
this.startX = e.absolutePointer.x
|
|
|
|
|
this.startY = e.absolutePointer.y
|
|
|
|
|
const currentTool = this.toolManager.currentTool.value
|
|
|
|
|
if (currentTool === OperationType.RECTANGLE) {
|
|
|
|
|
this.demoObject = this.downRectangle()
|
|
|
|
|
} else if (currentTool === OperationType.LINE) {
|
|
|
|
|
this.demoObject = this.downLine()
|
|
|
|
|
} else if (currentTool === OperationType.ELLIPSE) {
|
|
|
|
|
this.demoObject = this.downEllipse()
|
|
|
|
|
} else if (currentTool === OperationType.TRIANGLE) {
|
|
|
|
|
this.demoObject = this.downTriangle()
|
|
|
|
|
} else if (currentTool === OperationType.STAR) {
|
|
|
|
|
this.demoObject = this.downStar()
|
|
|
|
|
} else if (currentTool === OperationType.ARROW) {
|
|
|
|
|
this.demoObject = this.downArrow()
|
|
|
|
|
}
|
|
|
|
|
if (!this.demoObject) return;
|
|
|
|
|
this.demoObject.set({
|
|
|
|
|
evented: false,
|
|
|
|
|
})
|
|
|
|
|
this.demoObject.set
|
|
|
|
|
this.isDragging = true
|
|
|
|
|
this.canvasManager.canvas.add(this.demoObject)
|
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
const currentTool = this.toolManager.currentTool.value
|
|
|
|
|
if (currentTool === OperationType.RECTANGLE) {
|
|
|
|
|
this.moveRectangle({ width, height, left, top })
|
|
|
|
|
} else if (currentTool === OperationType.LINE) {
|
|
|
|
|
this.moveLine(e.absolutePointer)
|
|
|
|
|
} else if (currentTool === OperationType.ELLIPSE) {
|
|
|
|
|
this.moveEllipse({ width, height, left, top })
|
|
|
|
|
} else if (currentTool === OperationType.TRIANGLE) {
|
|
|
|
|
this.moveTriangle({ width, height, left, top })
|
|
|
|
|
} else if (currentTool === OperationType.STAR) {
|
|
|
|
|
this.moveStar({ width, height, left, top })
|
|
|
|
|
} else if (currentTool === OperationType.ARROW) {
|
|
|
|
|
this.moveArrow(e.absolutePointer)
|
|
|
|
|
}
|
|
|
|
|
this.demoObject.set({
|
|
|
|
|
evented: false,
|
|
|
|
|
})
|
|
|
|
|
this.canvasManager.canvas.renderAll()
|
|
|
|
|
}
|
|
|
|
|
mouseUpEvent(e) {
|
|
|
|
|
if (!this.isDragging) return;
|
|
|
|
|
this.isDragging = false;
|
|
|
|
|
const object = this.demoObject.toJSON("evented")
|
|
|
|
|
const currentTool = this.toolManager.currentTool.value
|
|
|
|
|
if (currentTool === OperationType.RECTANGLE) {
|
|
|
|
|
this.upRectangle(object)
|
|
|
|
|
} else if (currentTool === OperationType.LINE) {
|
|
|
|
|
this.upLine(object)
|
|
|
|
|
} else if (currentTool === OperationType.ELLIPSE) {
|
|
|
|
|
this.upEllipse(object)
|
|
|
|
|
} else if (currentTool === OperationType.TRIANGLE) {
|
|
|
|
|
this.upTriangle(object)
|
|
|
|
|
} else if (currentTool === OperationType.STAR) {
|
|
|
|
|
this.upStar(object)
|
|
|
|
|
} else if (currentTool === OperationType.ARROW) {
|
|
|
|
|
this.upArrow(object)
|
|
|
|
|
}
|
|
|
|
|
this.canvasManager.canvas.remove(this.demoObject)
|
|
|
|
|
this.demoObject = null
|
|
|
|
|
this.canvasManager.canvas.renderAll()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 绘制矩形 */
|
|
|
|
|
downRectangle() {
|
|
|
|
|
const rect = new fabric.Rect({
|
|
|
|
|
left: this.startX,
|
|
|
|
|
top: this.startY,
|
|
|
|
|
width: 0,
|
|
|
|
|
height: 0,
|
|
|
|
|
fill: '#000',
|
2026-03-18 17:25:19 +08:00
|
|
|
strokeWidth: 0,
|
2026-03-17 17:17:48 +08:00
|
|
|
})
|
|
|
|
|
return rect
|
|
|
|
|
}
|
|
|
|
|
moveRectangle({ width, height, left, top }) {
|
|
|
|
|
this.demoObject.set({ width, height, left, top })
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
upRectangle(object) {
|
|
|
|
|
if (object.width === 0) object.width = 100
|
|
|
|
|
if (object.height === 0) object.height = 100
|
2026-03-23 16:43:08 +08:00
|
|
|
this.layerManager.createRectLayer(object)
|
2026-03-17 17:17:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 绘制直线 */
|
|
|
|
|
downLine() {
|
2026-03-26 10:53:01 +08:00
|
|
|
// const line = new fabric.Line([this.startX, this.startY, this.startX, this.startY], {
|
|
|
|
|
// stroke: 'black', // 线条颜色
|
|
|
|
|
// strokeWidth: 2 // 线条粗细
|
|
|
|
|
// })
|
|
|
|
|
// return line
|
|
|
|
|
return new fabric.Path();
|
2026-03-17 17:17:48 +08:00
|
|
|
}
|
|
|
|
|
moveLine({ x, y }) {
|
2026-03-26 10:53:01 +08:00
|
|
|
// this.demoObject.set({
|
|
|
|
|
// x1: this.startX,
|
|
|
|
|
// y1: this.startY,
|
|
|
|
|
// x2: x,
|
|
|
|
|
// y2: y,
|
|
|
|
|
// })
|
|
|
|
|
const width = distance(this.startX, this.startY, x, y)
|
|
|
|
|
const angle = angleBetweenPointsDegrees(this.startX, this.startY, x, y)
|
|
|
|
|
this.canvasManager.canvas.remove(this.demoObject)
|
|
|
|
|
const arrow = new fabric.Path(getLinePath(width, 2), {
|
|
|
|
|
left: this.startX,
|
|
|
|
|
top: this.startY,
|
|
|
|
|
stroke: '#000', // 只设置边框颜色
|
|
|
|
|
strokeWidth: 2, // 边框宽度
|
|
|
|
|
fill: 'transparent', // 不填充
|
|
|
|
|
strokeLineCap: 'round',
|
|
|
|
|
strokeLineJoin: 'round',
|
|
|
|
|
originY: 'center',
|
|
|
|
|
angle: angle,
|
|
|
|
|
});
|
|
|
|
|
this.canvasManager.canvas.add(arrow)
|
|
|
|
|
this.demoObject = arrow
|
2026-03-17 17:17:48 +08:00
|
|
|
}
|
|
|
|
|
upLine(object) {
|
2026-03-26 10:53:01 +08:00
|
|
|
if (object.originY !== "center") {
|
|
|
|
|
this.layerManager.createLineLayer({
|
|
|
|
|
left: this.startX,
|
|
|
|
|
top: this.startY,
|
|
|
|
|
})
|
|
|
|
|
} else {
|
|
|
|
|
this.layerManager.createLineLayer(object)
|
|
|
|
|
}
|
|
|
|
|
// this.layerManager.createLineLayer(object)
|
2026-03-17 17:17:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 绘制椭圆 */
|
|
|
|
|
downEllipse() {
|
|
|
|
|
const circle = new fabric.Ellipse({
|
|
|
|
|
left: this.startX,
|
|
|
|
|
top: this.startY,
|
|
|
|
|
fill: '#000',
|
2026-03-18 17:25:19 +08:00
|
|
|
strokeWidth: 0,
|
2026-03-17 17:17:48 +08:00
|
|
|
})
|
|
|
|
|
return circle
|
|
|
|
|
}
|
|
|
|
|
moveEllipse({ width, height, left, top }) {
|
|
|
|
|
this.demoObject.set({ rx: width / 2, ry: height / 2, left, top })
|
|
|
|
|
}
|
|
|
|
|
upEllipse(object) {
|
|
|
|
|
if (object.rx === 0) object.rx = 50
|
|
|
|
|
if (object.ry === 0) object.ry = 50
|
2026-03-23 16:43:08 +08:00
|
|
|
this.layerManager.createEllipseLayer(object)
|
2026-03-17 17:17:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** 绘制三角形 */
|
|
|
|
|
downTriangle() {
|
|
|
|
|
const triangle = new fabric.Triangle({
|
|
|
|
|
left: this.startX,
|
|
|
|
|
top: this.startY,
|
|
|
|
|
width: 0,
|
|
|
|
|
height: 0,
|
|
|
|
|
fill: '#000',
|
2026-03-18 17:25:19 +08:00
|
|
|
strokeWidth: 0,
|
2026-03-17 17:17:48 +08:00
|
|
|
})
|
|
|
|
|
return triangle
|
|
|
|
|
}
|
|
|
|
|
moveTriangle({ width, height, left, top }) {
|
|
|
|
|
this.demoObject.set({ width, height, left, top })
|
|
|
|
|
}
|
|
|
|
|
upTriangle(object) {
|
|
|
|
|
if (object.width === 0) object.width = 100
|
|
|
|
|
if (object.height === 0) object.height = 100
|
2026-03-23 16:43:08 +08:00
|
|
|
this.layerManager.createTriangleLayer(object)
|
2026-03-17 17:17:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** 绘制五角星 */
|
|
|
|
|
downStar() {
|
|
|
|
|
const star = new fabric.Polygon(getStarArr(0, 0), {
|
|
|
|
|
left: this.startX,
|
|
|
|
|
top: this.startY,
|
|
|
|
|
width: 0,
|
|
|
|
|
height: 0,
|
|
|
|
|
fill: '#000',
|
|
|
|
|
strokeLineJoin: 'round', // 圆角连接
|
|
|
|
|
strokeLineCap: 'round', // 圆角端点
|
2026-03-18 17:25:19 +08:00
|
|
|
strokeWidth: 0,
|
2026-03-17 17:17:48 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return star
|
|
|
|
|
}
|
|
|
|
|
moveStar({ width, height, left, top }) {
|
|
|
|
|
this.demoObject.set({ left, top, width, height, points: getStarArr(width, height) })
|
|
|
|
|
}
|
|
|
|
|
upStar(object) {
|
|
|
|
|
if (object.width === 0) object.width = 100
|
|
|
|
|
if (object.height === 0) object.height = 100
|
2026-03-23 16:43:08 +08:00
|
|
|
this.layerManager.createStarLayer(object)
|
2026-03-17 17:17:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 绘制箭头 */
|
|
|
|
|
downArrow() {
|
|
|
|
|
return new fabric.Path();
|
|
|
|
|
}
|
|
|
|
|
moveArrow({ x, y }) {
|
|
|
|
|
const width = distance(this.startX, this.startY, x, y)
|
|
|
|
|
const angle = angleBetweenPointsDegrees(this.startX, this.startY, x, y)
|
|
|
|
|
this.canvasManager.canvas.remove(this.demoObject)
|
2026-03-26 10:53:01 +08:00
|
|
|
const arrow = new fabric.Path(getArrowPath(width, 4), {
|
2026-03-17 17:17:48 +08:00
|
|
|
left: this.startX,
|
|
|
|
|
top: this.startY,
|
|
|
|
|
stroke: '#000', // 只设置边框颜色
|
2026-03-26 10:53:01 +08:00
|
|
|
strokeWidth: 4, // 边框宽度
|
2026-03-17 17:17:48 +08:00
|
|
|
fill: 'transparent', // 不填充
|
|
|
|
|
strokeLineCap: 'round',
|
|
|
|
|
strokeLineJoin: 'round',
|
|
|
|
|
originY: 'center',
|
|
|
|
|
angle: angle,
|
|
|
|
|
});
|
|
|
|
|
this.canvasManager.canvas.add(arrow)
|
|
|
|
|
this.demoObject = arrow
|
|
|
|
|
}
|
|
|
|
|
upArrow(object) {
|
2026-03-17 17:31:51 +08:00
|
|
|
if (object.originY !== "center") {
|
|
|
|
|
this.layerManager.createArrowLayer({
|
|
|
|
|
left: this.startX,
|
|
|
|
|
top: this.startY,
|
2026-03-26 10:53:01 +08:00
|
|
|
})
|
2026-03-17 17:31:51 +08:00
|
|
|
} else {
|
2026-03-23 16:43:08 +08:00
|
|
|
this.layerManager.createArrowLayer(object)
|
2026-03-17 17:31:51 +08:00
|
|
|
}
|
2026-03-17 17:17:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
dispose() { }
|
|
|
|
|
}
|