@leafer-in/editor 1.6.7 → 1.8.0

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.
@@ -10,10 +10,10 @@ const { within } = MathHelper
10
10
 
11
11
  export const EditDataHelper = {
12
12
 
13
- getScaleData(element: IUI, startBounds: ILayoutBoundsData, direction: Direction9, totalMove: IPointData, lockRatio: boolean | 'corner', around: IAround, flipable: boolean, scaleMode: boolean): IEditorScaleEvent {
13
+ getScaleData(target: IUI, startBounds: ILayoutBoundsData, direction: Direction9, totalMove: IPointData, lockRatio: boolean | 'corner', around: IAround, flipable: boolean, scaleMode: boolean): IEditorScaleEvent {
14
14
  let align: IAlign, origin = {} as IPointData, scaleX: number = 1, scaleY: number = 1
15
15
 
16
- const { boxBounds, widthRange, heightRange, dragBounds, worldBoxBounds } = element
16
+ const { boxBounds, widthRange, heightRange, dragBounds, worldBoxBounds } = target
17
17
  const { width, height } = startBounds
18
18
 
19
19
  if (around) {
@@ -23,8 +23,8 @@ export const EditDataHelper = {
23
23
 
24
24
 
25
25
  // 获取已经改变的比例
26
- const originChangedScaleX = element.scaleX / startBounds.scaleX
27
- const originChangedScaleY = element.scaleY / startBounds.scaleY
26
+ const originChangedScaleX = target.scaleX / startBounds.scaleX
27
+ const originChangedScaleY = target.scaleY / startBounds.scaleY
28
28
  const signX = originChangedScaleX < 0 ? -1 : 1
29
29
  const signY = originChangedScaleY < 0 ? -1 : 1
30
30
 
@@ -105,7 +105,7 @@ export const EditDataHelper = {
105
105
  if (useScaleY) scaleY /= changedScaleY
106
106
 
107
107
  if (!flipable) {
108
- const { worldTransform } = element
108
+ const { worldTransform } = target
109
109
  if (scaleX < 0) scaleX = 1 / boxBounds.width / worldTransform.scaleX
110
110
  if (scaleY < 0) scaleY = 1 / boxBounds.height / worldTransform.scaleY
111
111
  }
@@ -115,25 +115,29 @@ export const EditDataHelper = {
115
115
  toPoint(around || align, boxBounds, origin, true)
116
116
 
117
117
  if (dragBounds) {
118
- const allowBounds = dragBounds === 'parent' ? element.parent.boxBounds : dragBounds
119
- const localBounds = new Bounds(element.__localBoxBounds)
120
- localBounds.scaleOf(element.getLocalPointByInner(origin), scaleX, scaleY)
121
-
122
- if (!BoundsHelper.includes(allowBounds, localBounds)) {
123
- const realBounds = localBounds.getIntersect(allowBounds)
124
- const fitScaleX = realBounds.width / localBounds.width, fitScaleY = realBounds.height / localBounds.height
125
- if (useScaleX) scaleX *= fitScaleX
126
- if (useScaleY) scaleY *= fitScaleY // 后续需优化带旋转的场景
118
+ const allowBounds = dragBounds === 'parent' ? target.parent.boxBounds : dragBounds
119
+ const childBounds = new Bounds(target.__localBoxBounds)
120
+
121
+ if (BoundsHelper.includes(new Bounds(allowBounds).spread(0.1), childBounds)) {
122
+
123
+ childBounds.scaleOf(target.getLocalPointByInner(origin), scaleX, scaleY)
124
+
125
+ if (!BoundsHelper.includes(allowBounds, childBounds)) {
126
+ const realBounds = childBounds.getIntersect(allowBounds)
127
+ const fitScaleX = realBounds.width / childBounds.width, fitScaleY = realBounds.height / childBounds.height
128
+ if (useScaleX) scaleX *= fitScaleX
129
+ if (useScaleY) scaleY *= fitScaleY // 后续需优化带旋转的场景
130
+ }
127
131
  }
128
132
  }
129
133
 
130
134
  if (useScaleX && widthRange) {
131
- const nowWidth = boxBounds.width * element.scaleX
135
+ const nowWidth = boxBounds.width * target.scaleX
132
136
  scaleX = within(nowWidth * scaleX, widthRange) / nowWidth
133
137
  }
134
138
 
135
139
  if (useScaleY && heightRange) {
136
- const nowHeight = boxBounds.height * element.scaleY
140
+ const nowHeight = boxBounds.height * target.scaleY
137
141
  scaleY = within(nowHeight * scaleY, heightRange) / nowHeight
138
142
  }
139
143
 
@@ -146,7 +150,7 @@ export const EditDataHelper = {
146
150
  return { origin, scaleX, scaleY, direction, lockRatio, around }
147
151
  },
148
152
 
149
- getRotateData(bounds: IBoundsData, direction: Direction9, current: IPointData, last: IPointData, around: IAround): IEditorRotateEvent {
153
+ getRotateData(target: IUI, direction: Direction9, current: IPointData, last: IPointData, around: IAround): IEditorRotateEvent {
150
154
  let align: IAlign, origin = {} as IPointData
151
155
 
152
156
  switch (direction) {
@@ -166,9 +170,9 @@ export const EditDataHelper = {
166
170
  align = 'center'
167
171
  }
168
172
 
169
- toPoint(around || align, bounds, origin, true)
173
+ toPoint(around || align, target.boxBounds, origin, true)
170
174
 
171
- return { origin, rotation: PointHelper.getRotation(last, origin, current) }
175
+ return { origin, rotation: PointHelper.getRotation(last, target.getWorldPointByBox(origin), current) }
172
176
  },
173
177
 
174
178
  getSkewData(bounds: IBoundsData, direction: Direction9, move: IPointData, around: IAround): IEditorSkewEvent {
package/src/index.ts CHANGED
@@ -19,6 +19,8 @@ export { EditToolCreator, registerEditTool, registerInnerEditor } from './tool/E
19
19
  export { InnerEditor } from './tool/InnerEditor'
20
20
  export { EditTool } from './tool/EditTool'
21
21
  export { LineEditTool } from './tool/LineEditTool'
22
+ export { TransformTool } from './tool/TransformTool'
23
+
22
24
 
23
25
  export { EditorHelper } from './helper/EditorHelper'
24
26
  export { EditDataHelper } from './helper/EditDataHelper'
@@ -45,7 +47,7 @@ Creator.editor = function (options?: IEditorConfig, app?: IApp): IEditor {
45
47
  Box.addAttr('textBox', false, dataType)
46
48
 
47
49
  UI.addAttr('editConfig', undefined, dataType)
48
- UI.addAttr('editOuter', (ui: UI) => ui.__.__isLinePath ? 'LineEditTool' : 'EditTool', dataType)
50
+ UI.addAttr('editOuter', (ui: UI) => { ui.updateLayout(); return ui.__.__isLinePath ? 'LineEditTool' : 'EditTool' }, dataType) // fix: Line 需要更新布局才能精准确定
49
51
 
50
52
  UI.addAttr('editInner', 'PathEditor', dataType)
51
53
  Group.addAttr('editInner', '', dataType) // 必须设为空
@@ -69,10 +69,7 @@ export class EditTool extends InnerEditor implements IEditTool {
69
69
  }
70
70
 
71
71
  public update(): void {
72
- const { editor, editBox } = this
73
- const { x, y, scaleX, scaleY, rotation, skewX, skewY, width, height } = editor.element.getLayoutBounds('box', editor, true)
74
- editBox.set({ x, y, scaleX, scaleY, rotation, skewX, skewY })
75
- editBox.update({ x: 0, y: 0, width, height })
72
+ this.editBox.update()
76
73
  this.onUpdate()
77
74
  }
78
75
 
@@ -1,5 +1,5 @@
1
1
  import { IGroup, IEventListenerId, IUI, IObject } from '@leafer-ui/interface'
2
- import { IInnerEditor, IEditor, IEditBox } from '@leafer-in/interface'
2
+ import { IInnerEditor, IEditor, IEditBox, IInnerEditorMode } from '@leafer-in/interface'
3
3
 
4
4
  import { Group } from '@leafer-ui/draw'
5
5
  import { EditToolCreator } from './EditToolCreator'
@@ -13,6 +13,8 @@ export class InnerEditor implements IInnerEditor {
13
13
 
14
14
  public get tag() { return 'InnerEditor' }
15
15
 
16
+ public get mode(): IInnerEditorMode { return 'focus' } // 专注模式
17
+
16
18
  public editTarget: IUI
17
19
 
18
20
  public config: IObject
@@ -22,7 +24,7 @@ export class InnerEditor implements IInnerEditor {
22
24
 
23
25
  public view: IGroup
24
26
 
25
- public eventIds: IEventListenerId[]
27
+ public eventIds: IEventListenerId[] = []
26
28
 
27
29
 
28
30
  constructor(editor: IEditor) {
@@ -44,7 +46,7 @@ export class InnerEditor implements IInnerEditor {
44
46
  public load(): void {
45
47
  const { editor } = this
46
48
  if (editor) {
47
- if (editor.app) editor.selector.hittable = editor.app.tree.hitChildren = false
49
+ if (editor.app && this.mode === 'focus') editor.selector.hittable = editor.app.tree.hitChildren = false
48
50
  this.onLoad()
49
51
  }
50
52
  }
@@ -56,7 +58,7 @@ export class InnerEditor implements IInnerEditor {
56
58
  public unload(): void {
57
59
  const { editor } = this
58
60
  if (editor) {
59
- if (editor.app) editor.selector.hittable = editor.app.tree.hitChildren = true
61
+ if (editor.app && this.mode === 'focus') editor.selector.hittable = editor.app.tree.hitChildren = true
60
62
  this.onUnload()
61
63
  }
62
64
  }
@@ -0,0 +1,282 @@
1
+ import { IEvent, IPointData, IAlign, IAxis, IFunction, IMatrix } from '@leafer-ui/interface'
2
+ import { MathHelper, Matrix, LeafHelper, AroundHelper } from '@leafer-ui/draw'
3
+ import { DragEvent, RotateEvent, ZoomEvent, MoveEvent } from '@leafer-ui/core'
4
+
5
+ import { IEditBox, IEditPoint, IEditTool, IEditorScaleEvent, ISimulateElement, IEditorMoveEvent, IEditorRotateEvent, IEditorSkewEvent } from '@leafer-in/interface'
6
+
7
+ import { EditorMoveEvent } from '../event/EditorMoveEvent'
8
+ import { EditorScaleEvent } from '../event/EditorScaleEvent'
9
+ import { EditorRotateEvent } from '../event/EditorRotateEvent'
10
+ import { EditorSkewEvent } from '../event/EditorSkewEvent'
11
+
12
+ import { EditDataHelper } from '../helper/EditDataHelper'
13
+ import { ITransformTool } from '@leafer-ui/interface'
14
+
15
+ export class TransformTool implements ITransformTool { // Editor use
16
+
17
+ public editBox: IEditBox
18
+
19
+ public editTool?: IEditTool // 可能不存在值
20
+
21
+
22
+ // operate
23
+
24
+ public onMove(e: DragEvent | MoveEvent): void {
25
+
26
+ const { target, dragStartData } = this.editBox
27
+
28
+ if (e instanceof MoveEvent) {
29
+
30
+ const move = e.getLocalMove(target)
31
+ this.move(move.x, move.y)
32
+
33
+ } else {
34
+
35
+ const total = { x: e.totalX, y: e.totalY }
36
+
37
+ if (e.shiftKey) {
38
+ if (Math.abs(total.x) > Math.abs(total.y)) total.y = 0
39
+ else total.x = 0
40
+ }
41
+
42
+ this.move(DragEvent.getValidMove(target, dragStartData.point, total))
43
+
44
+ }
45
+ }
46
+
47
+ public onScale(e: DragEvent | ZoomEvent): void {
48
+
49
+ const { target, mergeConfig, single, dragStartData } = this.editBox
50
+ let { around, lockRatio, flipable, editSize } = mergeConfig
51
+
52
+ if (e instanceof ZoomEvent) {
53
+
54
+ this.scaleOf(target.getBoxPoint(e), e.scale, e.scale)
55
+
56
+ } else {
57
+
58
+ const { direction } = e.current as IEditPoint
59
+ if (e.shiftKey || target.lockRatio) lockRatio = true
60
+
61
+ const data = EditDataHelper.getScaleData(target, dragStartData.bounds, direction, e.getInnerTotal(target), lockRatio, EditDataHelper.getAround(around, e.altKey), flipable, !single || editSize === 'scale')
62
+
63
+ if (this.editTool && this.editTool.onScaleWithDrag) {
64
+ data.drag = e
65
+ this.scaleWithDrag(data)
66
+ } else {
67
+ this.scaleOf(data.origin, data.scaleX, data.scaleY)
68
+ }
69
+
70
+ }
71
+ }
72
+
73
+ public onRotate(e: DragEvent | RotateEvent): void {
74
+
75
+ const { target, mergeConfig, dragStartData } = this.editBox
76
+ const { around, rotateAround, rotateGap } = mergeConfig
77
+ const { direction } = e.current as IEditPoint
78
+
79
+ let origin: IPointData, rotation: number
80
+
81
+ if (e instanceof RotateEvent) {
82
+
83
+ rotation = e.rotation
84
+ origin = rotateAround ? AroundHelper.getPoint(rotateAround, target.boxBounds) : target.getBoxPoint(e)
85
+
86
+ } else {
87
+
88
+ const data = EditDataHelper.getRotateData(target, direction, e, dragStartData, e.shiftKey ? null : (rotateAround || target.around || target.origin || around || 'center'))
89
+ rotation = dragStartData.rotation + data.rotation - target.rotation
90
+ origin = data.origin
91
+
92
+ }
93
+
94
+ rotation = MathHelper.float(MathHelper.getGapRotation(rotation, rotateGap, target.rotation), 2)
95
+ if (!rotation) return
96
+
97
+ this.rotateOf(origin, rotation)
98
+ }
99
+
100
+ public onSkew(e: DragEvent): void {
101
+
102
+ const { target, mergeConfig } = this.editBox
103
+ const { around } = mergeConfig
104
+
105
+ const { origin, skewX, skewY } = EditDataHelper.getSkewData(target.boxBounds, (e.current as IEditPoint).direction, e.getInnerMove(target), EditDataHelper.getAround(around, e.altKey))
106
+ if (!skewX && !skewY) return
107
+
108
+ this.skewOf(origin, skewX, skewY)
109
+ }
110
+
111
+
112
+ // transform
113
+
114
+ public move(x: number | IPointData, y = 0): void {
115
+ if (!this.checkTransform('moveable')) return
116
+ if (typeof x === 'object') y = x.y, x = x.x
117
+
118
+ const { target, mergeConfig, single, editor } = this.editBox
119
+ const { beforeMove } = mergeConfig
120
+ if (beforeMove) {
121
+ const check = beforeMove({ target, x, y })
122
+ if (typeof check === 'object') x = check.x, y = check.y
123
+ else if (check === false) return
124
+ }
125
+
126
+ const world = target.getWorldPointByLocal({ x, y }, null, true)
127
+ if (!single) (target as ISimulateElement).safeChange(() => target.move(x, y))
128
+ const data: IEditorMoveEvent = { target, editor, moveX: world.x, moveY: world.y }
129
+
130
+ this.emitEvent(new EditorMoveEvent(EditorMoveEvent.BEFORE_MOVE, data))
131
+ const event = new EditorMoveEvent(EditorMoveEvent.MOVE, data)
132
+ this.doMove(event)
133
+ this.emitEvent(event)
134
+ }
135
+
136
+ public scaleWithDrag(data: IEditorScaleEvent): void {
137
+ if (!this.checkTransform('resizeable')) return
138
+
139
+ const { target, mergeConfig, editor } = this.editBox
140
+ const { beforeScale } = mergeConfig
141
+ if (beforeScale) {
142
+ const { origin, scaleX, scaleY, drag } = data
143
+ const check = beforeScale({ target, drag, origin, scaleX, scaleY })
144
+ if (check === false) return
145
+ }
146
+
147
+ data = { ...data, target, editor, worldOrigin: target.getWorldPoint(data.origin) }
148
+
149
+ this.emitEvent(new EditorScaleEvent(EditorScaleEvent.BEFORE_SCALE, data))
150
+ const event = new EditorScaleEvent(EditorScaleEvent.SCALE, data)
151
+ this.editTool.onScaleWithDrag(event)
152
+ this.emitEvent(event)
153
+ }
154
+
155
+ public scaleOf(origin: IPointData | IAlign, scaleX: number, scaleY = scaleX, _resize?: boolean): void {
156
+ if (!this.checkTransform('resizeable')) return
157
+
158
+ const { target, mergeConfig, single, editor } = this.editBox
159
+
160
+ const { beforeScale } = mergeConfig
161
+ if (beforeScale) {
162
+ const check = beforeScale({ target, origin, scaleX, scaleY })
163
+ if (typeof check === 'object') scaleX = check.scaleX, scaleY = check.scaleY
164
+ else if (check === false) return
165
+ }
166
+
167
+ const worldOrigin = this.getWorldOrigin(origin)
168
+ const transform = !single && this.getChangedTransform(() => (target as ISimulateElement).safeChange(() => target.scaleOf(origin, scaleX, scaleY)))
169
+ const data: IEditorScaleEvent = { target, editor, worldOrigin, scaleX, scaleY, transform }
170
+
171
+ this.emitEvent(new EditorScaleEvent(EditorScaleEvent.BEFORE_SCALE, data))
172
+ const event = new EditorScaleEvent(EditorScaleEvent.SCALE, data)
173
+ this.doScale(event)
174
+ this.emitEvent(event)
175
+ }
176
+
177
+ public flip(axis: IAxis): void {
178
+ if (!this.checkTransform('resizeable')) return
179
+
180
+ const { target, single, editor } = this.editBox
181
+
182
+ const worldOrigin = this.getWorldOrigin('center')
183
+ const transform = !single ? this.getChangedTransform(() => (target as ISimulateElement).safeChange(() => target.flip(axis))) : new Matrix(LeafHelper.getFlipTransform(target, axis))
184
+ const data: IEditorScaleEvent = { target, editor, worldOrigin, scaleX: axis === 'x' ? -1 : 1, scaleY: axis === 'y' ? -1 : 1, transform }
185
+
186
+ this.emitEvent(new EditorScaleEvent(EditorScaleEvent.BEFORE_SCALE, data))
187
+ const event = new EditorScaleEvent(EditorScaleEvent.SCALE, data)
188
+ this.doScale(event)
189
+ this.emitEvent(event)
190
+ }
191
+
192
+ public rotateOf(origin: IPointData | IAlign, rotation: number): void {
193
+ if (!this.checkTransform('rotateable')) return
194
+
195
+ const { target, mergeConfig, single, editor } = this.editBox
196
+
197
+ const { beforeRotate } = mergeConfig
198
+ if (beforeRotate) {
199
+ const check = beforeRotate({ target, origin, rotation })
200
+ if (typeof check === 'number') rotation = check
201
+ else if (check === false) return
202
+ }
203
+
204
+ const worldOrigin = this.getWorldOrigin(origin)
205
+ const transform = !single && this.getChangedTransform(() => (target as ISimulateElement).safeChange(() => target.rotateOf(origin, rotation)))
206
+ const data: IEditorRotateEvent = { target, editor, worldOrigin, rotation, transform }
207
+
208
+ this.emitEvent(new EditorRotateEvent(EditorRotateEvent.BEFORE_ROTATE, data))
209
+ const event = new EditorRotateEvent(EditorRotateEvent.ROTATE, data)
210
+ this.doRotate(event)
211
+ this.emitEvent(event)
212
+ }
213
+
214
+ public skewOf(origin: IPointData | IAlign, skewX: number, skewY = 0, _resize?: boolean): void {
215
+ if (!this.checkTransform('skewable')) return
216
+
217
+ const { target, mergeConfig, single, editor } = this.editBox
218
+
219
+ const { beforeSkew } = mergeConfig
220
+ if (beforeSkew) {
221
+ const check = beforeSkew({ target, origin, skewX, skewY })
222
+ if (typeof check === 'object') skewX = check.skewX, skewY = check.skewY
223
+ else if (check === false) return
224
+ }
225
+
226
+ const worldOrigin = this.getWorldOrigin(origin)
227
+ const transform = !single && this.getChangedTransform(() => (target as ISimulateElement).safeChange(() => target.skewOf(origin, skewX, skewY)))
228
+ const data: IEditorSkewEvent = { target, editor, worldOrigin, skewX, skewY, transform }
229
+
230
+ this.emitEvent(new EditorSkewEvent(EditorSkewEvent.BEFORE_SKEW, data))
231
+ const event = new EditorSkewEvent(EditorSkewEvent.SKEW, data)
232
+ this.doSkew(event)
233
+ this.emitEvent(event)
234
+ }
235
+
236
+
237
+ // do
238
+
239
+ protected doMove(event: IEditorMoveEvent) {
240
+ this.editTool.onMove(event)
241
+ }
242
+
243
+ protected doScale(event: IEditorScaleEvent): void {
244
+ this.editTool.onScale(event)
245
+ }
246
+
247
+ protected doRotate(event: IEditorRotateEvent): void {
248
+ this.editTool.onRotate(event)
249
+ }
250
+
251
+ protected doSkew(event: IEditorSkewEvent): void {
252
+ this.editTool.onSkew(event)
253
+ }
254
+
255
+ // helper
256
+
257
+ public checkTransform(type: 'moveable' | 'resizeable' | 'rotateable' | 'skewable'): boolean {
258
+ const { target, mergeConfig } = this.editBox
259
+ return target && !target.locked && mergeConfig[type] as boolean
260
+ }
261
+
262
+ protected getWorldOrigin(origin: IPointData | IAlign): IPointData {
263
+ const { target } = this.editBox
264
+ return target.getWorldPoint(LeafHelper.getInnerOrigin(target, origin))
265
+ }
266
+
267
+ protected getChangedTransform(func: IFunction): IMatrix {
268
+
269
+ const { target, single } = this.editBox
270
+ if (!single && !(target as ISimulateElement).canChange) return (target as ISimulateElement).changedTransform
271
+
272
+ const oldMatrix = new Matrix(target.worldTransform)
273
+ func()
274
+ return new Matrix(target.worldTransform).divide(oldMatrix) // world change transform
275
+ }
276
+
277
+ // need rewrite
278
+ public emitEvent(event?: IEvent, capture?: boolean): void {
279
+ this.editBox.editor.emitEvent(event, capture)
280
+ }
281
+
282
+ }