-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: 添加编辑多边形plugin功能 * refactor: 使用mixinState.mSelectOneType判定编辑多边形按钮的显示与否 * feat: 添加动态创建多边形插件 * feat: 添加绘制多边形DrawPolygonPlugin * refactor: 抽离shiftAngle方法用于shiftKey的时候纠正角度 * refactor: 修改绘制polygon的交互逻辑 * feat: 添加shiftKey纠正点位和onEnd监听,并完善交互逻辑 * feat: 添加自由绘制功能 * refactor: 完善历史记录和绘制多边形结合交互 * refactor: 完善判定开启和关闭特殊tool时的副作用 * feat: 添加pathTextPlugin用户绘制路径文字 --------- Co-authored-by: weicheng.liang <[email protected]>
- Loading branch information
Showing
11 changed files
with
565 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
import { fabric } from 'fabric'; | ||
import Editor from '../Editor'; | ||
import { v4 as uuid } from 'uuid'; | ||
import { shiftAngle } from '../utils/utils'; | ||
|
||
type IEditor = Editor; | ||
|
||
type LineCoords = [fabric.Point, fabric.Point]; | ||
type OffListener = (ev: fabric.IEvent) => void; | ||
type OnEnd = (...args: any[]) => void; | ||
class DrawPolygonPlugin { | ||
isDrawingPolygon = false; | ||
points: fabric.Point[] = []; | ||
lines: fabric.Line[] = []; | ||
anchors: fabric.Circle[] = []; | ||
tempPoint: fabric.Point | undefined; | ||
tempLine: fabric.Line | undefined; | ||
lastPoint: fabric.Point | undefined; | ||
onEnd?: OnEnd; | ||
// 最后一点和第一点的距离为<=delta即闭合 | ||
delta = 5; | ||
static pluginName = 'DrawPolygonPlugin'; | ||
static apis = ['beginDrawPolygon', 'endDrawPolygon', 'discardPolygon']; | ||
constructor(public canvas: fabric.Canvas, public editor: IEditor) {} | ||
|
||
_isUsingHistory() { | ||
return this.canvas.offHistory && typeof this.canvas.offHistory === 'function'; | ||
} | ||
_bindEvent() { | ||
window.addEventListener('keydown', this._escListener); | ||
this.canvas.on('mouse:down', this._downHandler); | ||
this.canvas.on('mouse:move', this._moveHandler); | ||
} | ||
_escListener = (evt: KeyboardEvent) => { | ||
if (evt.key === 'Escape' || evt['keyCode'] === 27) { | ||
this._confirmBuildPolygon(); | ||
} | ||
}; | ||
_downHandler = (ev: fabric.IEvent<MouseEvent>): void => { | ||
if (!this.isDrawingPolygon) return; | ||
const absPointer = ev.absolutePointer!; | ||
const confirmPoint = new fabric.Point(absPointer.x, absPointer.y); | ||
const anchor = this._mackAnchor(absPointer); | ||
this.anchors.push(anchor); | ||
if (this.tempLine == null) { | ||
const tempPoint = new fabric.Point(absPointer.x, absPointer.y); | ||
this.tempLine = this._makeLine([tempPoint, tempPoint]); | ||
this.canvas.add(this.tempLine); | ||
} else { | ||
ev.e.shiftKey && confirmPoint.setXY(this.tempLine.x2!, this.tempLine.y2!); | ||
anchor.set({ left: confirmPoint.x, top: confirmPoint.y }); | ||
this.tempLine.set({ | ||
x1: confirmPoint.x, | ||
y1: confirmPoint.y, | ||
x2: confirmPoint.x, | ||
y2: confirmPoint.y, | ||
}); | ||
} | ||
if (this.lastPoint) { | ||
const line = this._makeLine([this.lastPoint, confirmPoint]); | ||
this.lines.push(line); | ||
this.canvas.add(line); | ||
if (this.points[0].distanceFrom(confirmPoint) / this.canvas.getZoom() <= this.delta) { | ||
this._confirmBuildPolygon(); | ||
return; | ||
} | ||
} | ||
this.canvas.add(anchor); | ||
this.lastPoint = confirmPoint; | ||
this.points.push(confirmPoint); | ||
this._ensureAnchorsForward(); | ||
}; | ||
_moveHandler = (ev: fabric.IEvent<MouseEvent>): void => { | ||
if (!this.isDrawingPolygon || !this.tempLine) return; | ||
const absPoint = ev.absolutePointer!; | ||
if (ev.e.shiftKey && this.lastPoint) { | ||
const point = shiftAngle(this.lastPoint, absPoint); | ||
this.tempLine.set({ | ||
x2: point.x, | ||
y2: point.y, | ||
}); | ||
} else { | ||
this.tempLine.set({ | ||
x2: absPoint.x, | ||
y2: absPoint.y, | ||
}); | ||
} | ||
this.canvas.renderAll(); | ||
}; | ||
_ensureAnchorsForward() { | ||
this.anchors.forEach((item) => { | ||
item.bringForward(); | ||
}); | ||
} | ||
_unbindEvent() { | ||
window.removeEventListener('keydown', this._escListener); | ||
this.canvas.off('mouse:down', this._downHandler as OffListener); | ||
this.canvas.off('mouse:move', this._moveHandler as OffListener); | ||
} | ||
_createPolygon(points: fabric.Point[]) { | ||
return new fabric.Polygon(points, { | ||
fill: '#ccc', | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-ignore | ||
id: uuid(), | ||
}); | ||
} | ||
_makeLine(coors: LineCoords) { | ||
const [p1, p2] = coors; | ||
return new fabric.Line([p1.x, p1.y, p2.x, p2.y], { | ||
fill: '#000', | ||
stroke: '#000', | ||
strokeWidth: 1, | ||
selectable: false, | ||
evented: false, | ||
}); | ||
} | ||
_mackAnchor(position: fabric.Point) { | ||
return new fabric.Circle({ | ||
radius: 5, | ||
left: position.x, | ||
top: position.y, | ||
fill: 'rgb(178, 53, 84)', | ||
scaleX: 1 / this.canvas.getZoom(), | ||
scaleY: 1 / this.canvas.getZoom(), | ||
strokeWidth: 1 / this.canvas.getZoom(), | ||
originX: 'center', | ||
originY: 'center', | ||
evented: false, | ||
selectable: false, | ||
}); | ||
} | ||
_confirmBuildPolygon() { | ||
const points = this.points; | ||
this.discardPolygon(); | ||
if (this._isUsingHistory()) { | ||
this.canvas.historyProcessing = false; | ||
} | ||
if (points.length >= 3) { | ||
const poly = this._createPolygon(points); | ||
this.canvas.add(poly); | ||
} | ||
} | ||
_prepare() { | ||
this.canvas.discardActiveObject(); | ||
this.canvas.getObjects().forEach((obj) => { | ||
obj.selectable = false; | ||
obj.hasControls = false; | ||
}); | ||
} | ||
beginDrawPolygon(onEnd?: OnEnd) { | ||
this._prepare(); | ||
this.onEnd = onEnd; | ||
if (this._isUsingHistory()) { | ||
this.canvas.historyProcessing = true; | ||
} | ||
this.canvas.requestRenderAll(); | ||
this.isDrawingPolygon = true; | ||
this._bindEvent(); | ||
} | ||
endDrawPolygon() { | ||
this.canvas.discardActiveObject(); | ||
this.isDrawingPolygon = false; | ||
this.lastPoint = undefined; | ||
this.tempPoint = undefined; | ||
this._unbindEvent(); | ||
this.onEnd && this.onEnd(); | ||
this.onEnd = undefined; | ||
} | ||
discardPolygon() { | ||
this.lines.forEach((item) => { | ||
this.canvas.remove(item); | ||
}); | ||
this.anchors.forEach((item) => { | ||
this.canvas.remove(item); | ||
}); | ||
this.tempLine && this.canvas.remove(this.tempLine); | ||
this.tempLine = undefined; | ||
this.anchors = []; | ||
this.lines = []; | ||
this.points = []; | ||
this.endDrawPolygon(); | ||
} | ||
} | ||
|
||
export default DrawPolygonPlugin; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { fabric } from 'fabric'; | ||
import Editor from '../Editor'; | ||
|
||
type IEditor = Editor; | ||
|
||
type DrawOptions = { | ||
width: number; | ||
}; | ||
|
||
export default class FreeDrawPlugin { | ||
static pluginName = 'FreeDrawPlugin'; | ||
static apis = ['startDraw', 'endDraw']; | ||
constructor(public canvas: fabric.Canvas, public editor: IEditor) {} | ||
|
||
startDraw(options: DrawOptions) { | ||
this.canvas.isDrawingMode = true; | ||
this.canvas.freeDrawingBrush = new fabric.PencilBrush(this.canvas); | ||
this.canvas.freeDrawingBrush.width = options.width; | ||
} | ||
endDraw() { | ||
if (this.canvas.isDrawingMode) { | ||
this.canvas.isDrawingMode = false; | ||
return; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import { fabric } from 'fabric'; | ||
import Editor from '../Editor'; | ||
|
||
type IEditor = Editor; | ||
type DrawOptions = { | ||
decimate: number; | ||
width: number; | ||
defaultText: string; | ||
color: string; | ||
lineColor: string; | ||
defaultFontSize: number; | ||
}; | ||
|
||
export default class PathTextPlugin { | ||
static pluginName = 'PathTextPlugin'; | ||
static apis = ['startTextPathDraw', 'endTextPathDraw']; | ||
private options?: DrawOptions; | ||
constructor(public canvas: fabric.Canvas, public editor: IEditor) {} | ||
|
||
_beforeHandler = (opt: any) => { | ||
if (this.options == null) return; | ||
const path = opt.path as any; | ||
const getPathSegmentsInfo = (fabric.util as any).getPathSegmentsInfo; | ||
path.segmentsInfo = getPathSegmentsInfo(path.path); | ||
path.set({ stroke: this.options.lineColor }); | ||
const text = this.options.defaultText; | ||
const fontSize = this.options.defaultFontSize; | ||
const textObject = new fabric.Text(text, { | ||
fontSize: fontSize, | ||
top: path.top, | ||
left: path.left, | ||
fill: this.options.color, | ||
path: path, | ||
}); | ||
this.canvas.add(textObject); | ||
}; | ||
_createdHandler = (opt: any) => { | ||
this.canvas.remove(opt.path); | ||
}; | ||
_bindEvent() { | ||
this.canvas.on('before:path:created', this._beforeHandler); | ||
this.canvas.on('path:created', this._createdHandler); | ||
} | ||
_unbindEvent() { | ||
this.canvas.off('before:path:created', this._beforeHandler); | ||
this.canvas.off('path:created', this._createdHandler); | ||
} | ||
startTextPathDraw(options: Partial<DrawOptions> = {}) { | ||
const defaultOptions = { | ||
decimate: 8, | ||
width: 2, | ||
defaultText: '诸事顺遂 万事大吉', | ||
color: '#000000', | ||
lineColor: '#000000', | ||
defaultFontSize: 20, | ||
}; | ||
this.options = { | ||
...defaultOptions, | ||
...options, | ||
}; | ||
this.canvas.isDrawingMode = true; | ||
const brush = (this.canvas.freeDrawingBrush = new fabric.PencilBrush(this.canvas)); | ||
brush.decimate = this.options.decimate; | ||
brush.width = this.options.width; | ||
brush.color = this.options.color; | ||
this._bindEvent(); | ||
} | ||
endTextPathDraw() { | ||
this.canvas.isDrawingMode = false; | ||
this._unbindEvent(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.