@leafer-in/editor 1.0.0-beta.18 → 1.0.0-rc.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,273 @@
1
+ import { IRect, IAround, IEventListenerId, IBoundsData, IRectInputData, IPointData, IKeyEvent, IGroup, IBox } from '@leafer-ui/interface'
2
+ import { Group, DragEvent, PointerEvent, Box, AroundHelper } from '@leafer-ui/core'
3
+
4
+ import { IEditBox, IEditor, IDirection8, IEditPoint, IEditPointType } from '@leafer-in/interface'
5
+
6
+ import { updateCursor, updateMoveCursor } from '../editor/cursor'
7
+ import { EditorEvent } from '../event/EditorEvent'
8
+ import { EditPoint } from './EditPoint'
9
+ import { EditDataHelper } from '../helper/EditDataHelper'
10
+
11
+
12
+ const fourDirection = ['top', 'right', 'bottom', 'left']
13
+
14
+ export class EditBox extends Group implements IEditBox {
15
+
16
+ public editor: IEditor
17
+ public dragging: boolean
18
+ public moving: boolean
19
+
20
+ public rect: IBox = new Box({ name: 'rect', hitFill: 'all', hitStroke: 'none', strokeAlign: 'center', hitRadius: 5 }) // target rect
21
+ public circle: IEditPoint = new EditPoint({ name: 'circle', strokeAlign: 'center', around: 'center', cursor: 'crosshair', hitRadius: 5 }) // rotate point
22
+
23
+ public buttons: IGroup = new Group({ around: 'center', hitSelf: false })
24
+
25
+ public resizePoints: IEditPoint[] = [] // topLeft, top, topRight, right, bottomRight, bottom, bottomLeft, left
26
+ public rotatePoints: IEditPoint[] = [] // topLeft, top, topRight, right, bottomRight, bottom, bottomLeft, left
27
+ public resizeLines: IEditPoint[] = [] // top, right, bottom, left
28
+
29
+ // fliped
30
+ public get flipped(): boolean { return this.flippedX || this.flippedY }
31
+ public get flippedX(): boolean { return this.scaleX < 0 }
32
+ public get flippedY(): boolean { return this.scaleY < 0 }
33
+ public get flippedOne(): boolean { return this.scaleX * this.scaleY < 0 }
34
+
35
+ public enterPoint: IEditPoint
36
+
37
+ protected __eventIds: IEventListenerId[] = []
38
+
39
+ constructor(editor: IEditor) {
40
+ super()
41
+ this.editor = editor
42
+ this.visible = false
43
+ this.create()
44
+ this.__listenEvents()
45
+ }
46
+
47
+ public create() {
48
+ let rotatePoint: IEditPoint, resizeLine: IEditPoint, resizePoint: IEditPoint
49
+ const { resizePoints, rotatePoints, resizeLines, rect, circle, buttons } = this
50
+ const arounds: IAround[] = [{ x: 1, y: 1 }, { x: 0.5, y: 1 }, { x: 0, y: 1 }, { x: 0, y: 0.5 }, { x: 0, y: 0 }, { x: 0.5, y: 0 }, { x: 1, y: 0 }, { x: 1, y: 0.5 }]
51
+
52
+ for (let i = 0; i < 8; i++) {
53
+ rotatePoint = new EditPoint({ name: 'rotate-point', around: arounds[i], width: 15, height: 15, hitFill: "all" })
54
+ rotatePoints.push(rotatePoint)
55
+ this.listenPointEvents(rotatePoint, 'rotate', i)
56
+
57
+ if (i % 2) {
58
+ resizeLine = new EditPoint({ name: 'resize-line', around: 'center', width: 10, height: 10, hitFill: "all" })
59
+ resizeLines.push(resizeLine)
60
+ this.listenPointEvents(resizeLine, 'resize', i)
61
+ }
62
+
63
+ resizePoint = new EditPoint({ name: 'resize-point', around: 'center', strokeAlign: 'center', hitRadius: 5 })
64
+ resizePoints.push(resizePoint)
65
+ this.listenPointEvents(resizePoint, 'resize', i)
66
+ }
67
+
68
+ buttons.add(circle)
69
+ this.listenPointEvents(circle, 'rotate', 2)
70
+
71
+ this.addMany(...rotatePoints, rect, buttons, ...resizeLines, ...resizePoints)
72
+ }
73
+
74
+ // update
75
+
76
+ public update(bounds: IBoundsData): void {
77
+ const { config, list } = this.editor
78
+ const { width, height } = bounds
79
+ const { rect, circle, resizePoints, rotatePoints, resizeLines } = this
80
+ const { middlePoint, resizeable, rotateable, stroke, strokeWidth } = config
81
+
82
+ const pointsStyle = this.getPointsStyle()
83
+ const middlePointsStyle = this.getMiddlePointsStyle()
84
+ const showPoints = width > 20 || height > 20
85
+
86
+ this.visible = list[0] && !list[0].locked // check locked
87
+
88
+ let point = {} as IPointData, style: IRectInputData, rotateP: IRect, resizeP: IRect, resizeL: IRect
89
+
90
+ for (let i = 0; i < 8; i++) {
91
+
92
+ AroundHelper.toPoint(AroundHelper.directionData[i], bounds, point)
93
+ style = this.getPointStyle((i % 2) ? middlePointsStyle[((i - 1) / 2) % middlePointsStyle.length] : pointsStyle[(i / 2) % pointsStyle.length])
94
+ resizeP = resizePoints[i], rotateP = rotatePoints[i], resizeL = resizeLines[Math.floor(i / 2)]
95
+ resizeP.set(style)
96
+ resizeP.set(point), rotateP.set(point), resizeL.set(point)
97
+
98
+ // visible
99
+ resizeP.visible = resizeL.visible = showPoints && (resizeable || rotateable)
100
+ rotateP.visible = showPoints && rotateable && resizeable
101
+
102
+ if (i % 2) { // top, right, bottom, left
103
+
104
+ resizeP.visible = rotateP.visible = showPoints && !!middlePoint
105
+
106
+ if (((i + 1) / 2) % 2) { // top, bottom
107
+ resizeL.width = width
108
+ if (resizeP.width > width - 30) resizeP.visible = false
109
+ } else {
110
+ resizeL.height = height
111
+ resizeP.rotation = 90
112
+ if (resizeP.width > height - 30) resizeP.visible = false
113
+ }
114
+ } else {
115
+ resizeP.rotation = (i / 2) * 90
116
+ }
117
+
118
+ }
119
+
120
+ // rotate
121
+ circle.visible = showPoints && rotateable && !!config.rotatePoint
122
+ circle.set(this.getPointStyle(config.rotatePoint || pointsStyle[0]))
123
+
124
+ // rect
125
+ rect.set({ stroke, strokeWidth, ...(config.rect || {}) })
126
+ rect.set({ ...bounds, visible: true })
127
+
128
+ // buttons
129
+ this.buttons.visible = showPoints
130
+ this.layoutButtons()
131
+ }
132
+
133
+ protected layoutButtons(): void {
134
+ const { buttons, resizePoints } = this
135
+ const { buttonsDirection, buttonsFixed, buttonsMargin, middlePoint } = this.editor.config
136
+
137
+ const { flippedX, flippedY } = this
138
+ let index = fourDirection.indexOf(buttonsDirection)
139
+ if ((index % 2 && flippedX) || ((index + 1) % 2 && flippedY)) {
140
+ if (buttonsFixed) index = (index + 2) % 4 // flip x / y
141
+ }
142
+ const direction = buttonsFixed ? EditDataHelper.getRotateDirection(index, this.flippedOne ? this.rotation : -this.rotation, 4) : index
143
+
144
+ const point = resizePoints[direction * 2 + 1] // 4 map 8 direction
145
+ const useX = direction % 2 // left / right
146
+ const sign = (!direction || direction === 3) ? -1 : 1 // top / left = -1
147
+
148
+ const useWidth = index % 2 // left / right origin direction
149
+ const margin = (buttonsMargin + (useWidth ? ((middlePoint ? point.width : 0) + buttons.boxBounds.width) : ((middlePoint ? point.height : 0) + buttons.boxBounds.height)) / 2) * sign
150
+
151
+ if (useX) {
152
+ buttons.x = point.x + margin
153
+ buttons.y = point.y
154
+ } else {
155
+ buttons.x = point.x
156
+ buttons.y = point.y + margin
157
+ }
158
+
159
+ if (buttonsFixed) {
160
+ buttons.rotation = (direction - index) * 90
161
+ buttons.scaleX = flippedX ? -1 : 1
162
+ buttons.scaleY = flippedY ? -1 : 1
163
+ }
164
+
165
+ }
166
+
167
+ public getPointStyle(userStyle?: IRectInputData): IRectInputData {
168
+ const { stroke, strokeWidth, pointFill, pointSize, pointRadius } = this.editor.config
169
+ const defaultStyle = { fill: pointFill, stroke, strokeWidth, width: pointSize, height: pointSize, cornerRadius: pointRadius }
170
+ return userStyle ? Object.assign(defaultStyle, userStyle) : defaultStyle
171
+ }
172
+
173
+ public getPointsStyle(): IRectInputData[] {
174
+ const { point } = this.editor.config
175
+ return point instanceof Array ? point : [point]
176
+ }
177
+
178
+ public getMiddlePointsStyle(): IRectInputData[] {
179
+ const { middlePoint } = this.editor.config
180
+ return middlePoint instanceof Array ? middlePoint : (middlePoint ? [middlePoint] : this.getPointsStyle())
181
+ }
182
+
183
+ // drag
184
+
185
+ protected onDragStart(e: DragEvent): void {
186
+ this.dragging = true
187
+ if (e.target.name === 'rect') {
188
+ this.moving = true
189
+ this.editor.opacity = this.editor.config.hideOnMove ? 0 : 1 // move
190
+ }
191
+ }
192
+
193
+ protected onDragEnd(e: DragEvent): void {
194
+ this.dragging = false
195
+ this.moving = false
196
+ if (e.target.name === 'rect') this.editor.opacity = 1 // move
197
+
198
+ }
199
+
200
+ protected onDrag(e: DragEvent): void {
201
+ const { editor } = this
202
+ const point = this.enterPoint = e.current as IEditPoint
203
+ if (point.pointType === 'rotate' || e.metaKey || e.ctrlKey || !editor.config.resizeable) {
204
+ if (editor.config.rotateable) editor.onRotate(e)
205
+ updateCursor(editor, e)
206
+ } else {
207
+ editor.onScale(e)
208
+ }
209
+ }
210
+
211
+ public onArrow(e: IKeyEvent): void {
212
+ if (this.editor.hasTarget) {
213
+ const move = { x: 0, y: 0 }
214
+ const distance = e.shiftKey ? 10 : 1
215
+ switch (e.code) {
216
+ case 'ArrowDown':
217
+ move.y = distance
218
+ break
219
+ case 'ArrowUp':
220
+ move.y = -distance
221
+ break
222
+ case 'ArrowLeft':
223
+ move.x = -distance
224
+ break
225
+ case 'ArrowRight':
226
+ move.x = distance
227
+ }
228
+ if (move.x || move.y) this.editor.move(move.x, move.y)
229
+ }
230
+ }
231
+
232
+ protected onDoubleClick(): void {
233
+ const { editor } = this
234
+ if (editor.single && editor.element.isBranch) {
235
+ //list[0].hitChildren = true
236
+ }
237
+ }
238
+
239
+ public listenPointEvents(point: IEditPoint, type: IEditPointType, direction: IDirection8): void {
240
+ const { editor } = this
241
+ point.direction = direction
242
+ point.pointType = type
243
+ point.on_(DragEvent.START, this.onDragStart, this)
244
+ point.on_(DragEvent.DRAG, this.onDrag, this)
245
+ point.on_(DragEvent.END, this.onDragEnd, this)
246
+ point.on_(PointerEvent.LEAVE, () => this.enterPoint = null)
247
+ if (point.name !== 'circle') point.on_(PointerEvent.ENTER, (e) => { this.enterPoint = point, updateCursor(editor, e) })
248
+ }
249
+
250
+ protected __listenEvents(): void {
251
+ const { rect, editor } = this
252
+ this.__eventIds = [
253
+ editor.on_(EditorEvent.SELECT, () => { this.visible = editor.hasTarget }),
254
+ rect.on_(DragEvent.START, this.onDragStart, this),
255
+ rect.on_(DragEvent.DRAG, editor.onMove, editor),
256
+ rect.on_(DragEvent.END, this.onDragEnd, this),
257
+ rect.on_(PointerEvent.ENTER, () => updateMoveCursor(editor)),
258
+ rect.on_(PointerEvent.DOUBLE_CLICK, this.onDoubleClick, this)
259
+ ]
260
+ }
261
+
262
+ protected __removeListenEvents(): void {
263
+ this.off_(this.__eventIds)
264
+ this.__eventIds.length = 0
265
+ }
266
+
267
+ public destroy(): void {
268
+ this.editor = null
269
+ this.__removeListenEvents()
270
+ super.destroy()
271
+ }
272
+
273
+ }
@@ -0,0 +1,9 @@
1
+ import { Box } from '@leafer-ui/core'
2
+
3
+ import { IDirection8, IEditPoint, IEditPointType } from '@leafer-in/interface'
4
+
5
+
6
+ export class EditPoint extends Box implements IEditPoint {
7
+ public direction: IDirection8
8
+ public pointType: IEditPointType
9
+ }
@@ -0,0 +1,236 @@
1
+ import { IBounds, ILeaf, ILeafList, IUI, IEventListenerId } from '@leafer-ui/interface'
2
+ import { Bounds, PointerEvent, DragEvent, MoveEvent, LeafList, Group, ZoomEvent } from '@leafer-ui/core'
3
+
4
+ import { IEditSelect, IEditor, ISelectArea, IStroker } from '@leafer-in/interface'
5
+
6
+ import { Stroker } from './Stroker'
7
+ import { SelectArea } from './SelectArea'
8
+ import { EditSelectHelper } from '../helper/EditSelectHelper'
9
+ import { EditorEvent } from '../event/EditorEvent'
10
+
11
+
12
+ const { findOne } = EditSelectHelper
13
+
14
+ export class EditSelect extends Group implements IEditSelect {
15
+
16
+ public editor: IEditor
17
+
18
+ public get dragging(): boolean { return !!this.originList }
19
+ public get running(): boolean { return this.editor.hittable && this.editor.config.selector }
20
+ public get isMoveMode(): boolean { return this.app && this.app.interaction.moveMode }
21
+
22
+ public hoverStroker: IStroker = new Stroker()
23
+ public targetStroker: IStroker = new Stroker()
24
+
25
+ public bounds: IBounds = new Bounds()
26
+ public selectArea: ISelectArea = new SelectArea()
27
+
28
+ protected originList: ILeafList
29
+ protected lastDownLeaf: IUI
30
+
31
+ protected __eventIds: IEventListenerId[] = []
32
+
33
+
34
+ constructor(editor: IEditor) {
35
+ super()
36
+ this.editor = editor
37
+ this.addMany(this.targetStroker, this.hoverStroker, this.selectArea)
38
+ this.__listenEvents()
39
+ }
40
+
41
+ // hover / select
42
+
43
+ protected onHover(): void {
44
+ const { editor } = this
45
+ if (this.running && !this.dragging && !editor.dragging) {
46
+ const { stroke, strokeWidth, hover } = editor.config
47
+ this.hoverStroker.setTarget(hover ? this.editor.hoverTarget : null, { stroke, strokeWidth })
48
+ } else {
49
+ this.hoverStroker.target = null
50
+ }
51
+ }
52
+
53
+ protected onSelect(): void {
54
+ if (this.running) {
55
+ const { config, list } = this.editor
56
+ const { stroke, strokeWidth } = config
57
+ this.targetStroker.setTarget(list, { stroke, strokeWidth: Math.max(1, strokeWidth / 2) })
58
+ this.hoverStroker.target = null
59
+ }
60
+ }
61
+
62
+ public update(): void {
63
+ if (this.running) this.targetStroker.forceUpdate()
64
+ }
65
+
66
+ // move / down
67
+
68
+ protected onPointerMove(e: PointerEvent): void {
69
+ if (this.running && !this.isMoveMode) {
70
+ const find = e.shiftKey ? this.findDeepOne(e) : findOne(e.path)
71
+ this.editor.hoverTarget = this.editor.hasItem(find) ? null : find
72
+ } if (this.isMoveMode) {
73
+ this.editor.hoverTarget = null // move.dragEmpty
74
+ }
75
+ }
76
+
77
+ protected onBeforeDown(e: PointerEvent): void {
78
+ if (this.running && !this.isMoveMode && !e.middle) {
79
+ const find = this.lastDownLeaf = findOne(e.path)
80
+
81
+ if (find) {
82
+
83
+ if (e.shiftKey) {
84
+ this.editor.shiftItem(find)
85
+ } else {
86
+ this.editor.target = find
87
+ }
88
+
89
+ // change down data
90
+ this.editor.updateLayout()
91
+ if (!find.locked) this.app.interaction.updateDownData(null, { findList: [this.editor.editBox.rect] })
92
+
93
+ } else if (this.allow(e.target)) {
94
+
95
+ if (!e.shiftKey) this.editor.target = null
96
+
97
+ }
98
+ }
99
+ }
100
+
101
+ protected onTap(e: PointerEvent): void {
102
+ if (this.running && e.shiftKey && !e.middle && !this.lastDownLeaf) {
103
+ const find = this.findDeepOne(e)
104
+ if (find) this.editor.shiftItem(find)
105
+ } else if (this.isMoveMode) {
106
+ this.editor.target = null // move.dragEmpty
107
+ }
108
+
109
+ this.lastDownLeaf = null
110
+ }
111
+
112
+ // drag
113
+
114
+ protected onDragStart(e: DragEvent): void {
115
+ if (this.running && this.allowDrag(e)) {
116
+ const { editor } = this
117
+ const { stroke, strokeWidth, area } = editor.config
118
+ const { x, y } = e.getInner(this)
119
+
120
+ this.bounds.set(x, y)
121
+
122
+ this.selectArea.setStyle({ visible: true, stroke, strokeWidth, x, y }, area)
123
+ this.selectArea.setBounds(this.bounds.get())
124
+
125
+ this.originList = editor.leafList.clone()
126
+ }
127
+ }
128
+
129
+ protected onDrag(e: DragEvent): void {
130
+ if (this.editor.dragging) {
131
+ this.onDragEnd()
132
+ return
133
+ }
134
+
135
+ if (this.dragging) {
136
+ const { editor } = this
137
+ const total = e.getInnerTotal(this)
138
+
139
+ const dragBounds = this.bounds.clone().unsign()
140
+ const list = new LeafList(editor.app.find(EditSelectHelper.findBounds, dragBounds))
141
+
142
+ this.bounds.width = total.x
143
+ this.bounds.height = total.y
144
+
145
+ this.selectArea.setBounds(dragBounds.get())
146
+
147
+ if (list.length) {
148
+
149
+ const selectList: ILeaf[] = []
150
+
151
+ this.originList.forEach(item => { if (!list.has(item)) selectList.push(item) })
152
+ list.forEach(item => { if (!this.originList.has(item)) selectList.push(item) })
153
+
154
+ if (selectList.length !== editor.list.length || editor.list.some((child, index) => child !== selectList[index])) {
155
+ editor.target = selectList as IUI[]
156
+ }
157
+
158
+ } else {
159
+
160
+ editor.target = this.originList.list as IUI[]
161
+ if (editor.leafList.length) editor.update()
162
+
163
+ }
164
+ }
165
+ }
166
+
167
+ protected onDragEnd(): void {
168
+ if (this.dragging) this.originList = null, this.selectArea.visible = false
169
+ }
170
+
171
+ protected onAutoMove(e: MoveEvent): void {
172
+ if (this.dragging) {
173
+ const { x, y } = e.getLocalMove(this)
174
+ this.bounds.x += x
175
+ this.bounds.y += y
176
+ }
177
+ }
178
+
179
+ // helper
180
+
181
+ protected allow(target: ILeaf): boolean {
182
+ return target.leafer !== this.editor.leafer
183
+ }
184
+
185
+ protected allowDrag(e: DragEvent) {
186
+ if (this.editor.config.boxSelect && !e.target.draggable) {
187
+ return (!this.editor.hasTarget && this.allow(e.target)) || (e.shiftKey && !findOne(e.path))
188
+ } else {
189
+ return false
190
+ }
191
+ }
192
+
193
+ protected findDeepOne(e: PointerEvent): IUI {
194
+ const options = { exclude: new LeafList(this.editor.editBox.rect) }
195
+ return findOne(e.target.leafer.interaction.findPath(e, options)) as IUI
196
+ }
197
+
198
+ protected __listenEvents(): void {
199
+ const { editor } = this
200
+ editor.waitLeafer(() => {
201
+
202
+ const { app } = editor
203
+ app.selector.proxy = editor
204
+
205
+ this.__eventIds = [
206
+ editor.on_(EditorEvent.HOVER, this.onHover, this),
207
+ editor.on_(EditorEvent.SELECT, this.onSelect, this),
208
+
209
+ app.on_(PointerEvent.MOVE, this.onPointerMove, this),
210
+ app.on_(PointerEvent.BEFORE_DOWN, this.onBeforeDown, this),
211
+ app.on_(PointerEvent.TAP, this.onTap, this),
212
+
213
+ app.on_(DragEvent.START, this.onDragStart, this),
214
+ app.on_(DragEvent.DRAG, this.onDrag, this),
215
+ app.on_(DragEvent.END, this.onDragEnd, this),
216
+
217
+ app.on_(MoveEvent.MOVE, this.onAutoMove, this),
218
+ app.on_([ZoomEvent.ZOOM, MoveEvent.MOVE], () => { this.editor.hoverTarget = null }),
219
+ ]
220
+
221
+ })
222
+ }
223
+
224
+ protected __removeListenEvents(): void {
225
+ if (this.__eventIds) {
226
+ this.off_(this.__eventIds)
227
+ this.__eventIds.length = 0
228
+ }
229
+ }
230
+
231
+ public destroy(): void {
232
+ this.editor = this.originList = this.lastDownLeaf = null
233
+ this.__removeListenEvents()
234
+ super.destroy()
235
+ }
236
+ }
@@ -0,0 +1,30 @@
1
+ import { IBoundsData, IGroupInputData, IRect, IRectInputData } from '@leafer-ui/interface'
2
+ import { Group, Rect } from '@leafer-ui/core'
3
+
4
+ import { ISelectArea } from '@leafer-in/interface'
5
+
6
+
7
+ export class SelectArea extends Group implements ISelectArea {
8
+
9
+ protected strokeArea: IRect = new Rect({ strokeAlign: 'center' })
10
+ protected fillArea: IRect = new Rect()
11
+
12
+ constructor(data?: IGroupInputData) {
13
+ super(data)
14
+ this.visible = this.hittable = false
15
+ this.addMany(this.fillArea, this.strokeArea)
16
+ }
17
+
18
+ public setStyle(style: IRectInputData, userStyle?: IRectInputData): void {
19
+ const { visible, stroke, strokeWidth } = style
20
+ this.visible = visible
21
+ this.strokeArea.reset({ stroke, strokeWidth, ...(userStyle || {}) })
22
+ this.fillArea.reset({ visible: userStyle ? false : true, fill: stroke, opacity: 0.2 })
23
+ }
24
+
25
+ public setBounds(bounds: IBoundsData): void {
26
+ this.strokeArea.set(bounds)
27
+ this.fillArea.set(bounds)
28
+ }
29
+
30
+ }
@@ -0,0 +1,89 @@
1
+ import { IUI, ILeaferCanvas, IRenderOptions, IRectInputData } from '@leafer-ui/interface'
2
+ import { Paint, UI, MatrixHelper } from '@leafer-ui/core'
3
+
4
+ import { IStroker } from '@leafer-in/interface'
5
+
6
+ import { targetAttr } from '../decorator/data'
7
+
8
+
9
+ const matrix = MatrixHelper.get()
10
+ const { abs } = Math
11
+ const { copy, scale } = MatrixHelper
12
+
13
+ export class Stroker extends UI implements IStroker {
14
+
15
+ @targetAttr(onTarget)
16
+ public target: IUI | IUI[]
17
+
18
+ public list: IUI[] = []
19
+
20
+ constructor() {
21
+ super()
22
+ this.hittable = false
23
+ this.strokeAlign = 'center'
24
+ }
25
+
26
+ public setTarget(target: IUI | IUI[], style: IRectInputData): void {
27
+ const { stroke, strokeWidth } = style
28
+ this.set({ stroke, strokeWidth })
29
+ this.target = target
30
+ }
31
+
32
+ public __draw(canvas: ILeaferCanvas, options: IRenderOptions): void {
33
+ const { list } = this
34
+ if (list.length) {
35
+
36
+ let leaf: IUI
37
+ const { stroke, strokeWidth } = this.__
38
+ const { bounds } = options
39
+
40
+ for (let i = 0; i < list.length; i++) {
41
+ leaf = list[i]
42
+ if (bounds && bounds.hit(leaf.__world, options.matrix)) {
43
+
44
+ let drewPath: boolean
45
+
46
+ if (leaf.__.editSize === 'scale') {
47
+ const aScaleX = abs(leaf.__world.scaleX), aScaleY = abs(leaf.__world.scaleY)
48
+ if (aScaleX !== aScaleY) { // need no scale stroke
49
+ copy(matrix, leaf.__world)
50
+ scale(matrix, 1 / aScaleX, 1 / aScaleY)
51
+
52
+ canvas.setWorld(matrix, options.matrix)
53
+ canvas.beginPath()
54
+ this.__.strokeWidth = strokeWidth
55
+
56
+ const { x, y, width, height } = leaf.__layout.boxBounds
57
+ canvas.rect(x * aScaleX, y * aScaleY, width * aScaleX, height * aScaleY)
58
+
59
+ drewPath = true
60
+ }
61
+ }
62
+
63
+ if (!drewPath) {
64
+ canvas.setWorld(leaf.__world, options.matrix)
65
+ canvas.beginPath()
66
+ leaf.__.__pathForRender ? leaf.__drawRenderPath(canvas) : leaf.__drawPathByBox(canvas)
67
+ this.__.strokeWidth = strokeWidth / abs(leaf.__world.scaleX)
68
+ }
69
+
70
+ typeof stroke === 'string' ? Paint.stroke(stroke, this, canvas) : Paint.strokes(stroke, this, canvas)
71
+ }
72
+ }
73
+
74
+ this.__.strokeWidth = strokeWidth
75
+ }
76
+ }
77
+
78
+ public destroy(): void {
79
+ this.target = null
80
+ super.destroy()
81
+ }
82
+
83
+ }
84
+
85
+ function onTarget(stroker: Stroker): void {
86
+ const value = stroker.target
87
+ stroker.list = value ? (value instanceof Array ? value : [value]) : []
88
+ stroker.forceUpdate()
89
+ }
@@ -0,0 +1,34 @@
1
+ import { IUIEvent } from '@leafer-ui/interface'
2
+
3
+ import { IEditor } from '@leafer-in/interface'
4
+
5
+ import { EditDataHelper } from '../helper/EditDataHelper'
6
+
7
+
8
+ export function updateCursor(editor: IEditor, e: IUIEvent): void {
9
+ const { editBox } = editor, point = editBox.enterPoint
10
+ if (!point || !editor.hasTarget || !editBox.visible) return
11
+
12
+ let { rotation } = editBox
13
+ const { resizeCursor, rotateCursor, skewCursor, resizeable, rotateable, skewable } = editor.config
14
+ const { pointType } = point, { flippedX, flippedY } = editBox
15
+
16
+ let showResize = pointType === 'resize'
17
+ if (showResize && rotateable && (e.metaKey || e.ctrlKey || !resizeable)) showResize = false
18
+ const showSkew = skewable && !showResize && point.name === 'resize-line'
19
+
20
+ const cursor = showSkew ? skewCursor : (showResize ? resizeCursor : rotateCursor)
21
+ rotation += (EditDataHelper.getFlipDirection(point.direction, flippedX, flippedY) + 1) * 45
22
+
23
+ const { url, x, y } = cursor
24
+ point.cursor = { url: toDataURL(url, rotation), x, y }
25
+ }
26
+
27
+ export function updateMoveCursor(editor: IEditor): void {
28
+ editor.editBox.rect.cursor = editor.config.moveCursor
29
+ }
30
+
31
+
32
+ function toDataURL(svg: string, rotation: number): string {
33
+ return '"data:image/svg+xml,' + encodeURIComponent(svg.replace('{{rotation}}', rotation.toString())) + '"'
34
+ }
@@ -0,0 +1,14 @@
1
+ import { IGroup, ILeaf } from '@leafer-ui/interface'
2
+ import { Bounds } from '@leafer-ui/core'
3
+
4
+ import { IEditor } from '@leafer-in/interface'
5
+
6
+
7
+ export function simulate(editor: IEditor) {
8
+ const { simulateTarget, leafList: targetList } = editor
9
+ const { x, y, width, height } = new Bounds().setListWithFn(targetList.list, (leaf: ILeaf) => leaf.worldBoxBounds)
10
+
11
+ const parent = simulateTarget.parent = targetList.list[0].leafer.zoomLayer as IGroup // follow zoomLayer zoom / move
12
+ const { scaleX, scaleY, e: worldX, f: worldY } = parent.__world
13
+ simulateTarget.reset({ x: (x - worldX) / scaleX, y: (y - worldY) / scaleY, width: width / scaleX, height: height / scaleY })
14
+ }