@leafer-in/editor 1.0.0-rc.9 → 1.0.1

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.
@@ -1,7 +1,8 @@
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'
1
+ import { IRect, IEventListenerId, IBoundsData, IPointData, IKeyEvent, IGroup, IBox, IBoxInputData, IAlign, IUI, IEditorConfig } from '@leafer-ui/interface'
2
+ import { Group, Box, AroundHelper, Direction9 } from '@leafer-ui/draw'
3
+ import { DragEvent, PointerEvent } from '@leafer-ui/core'
3
4
 
4
- import { IEditBox, IEditor, IDirection8, IEditPoint, IEditPointType } from '@leafer-in/interface'
5
+ import { IEditBox, IEditor, IEditPoint, IEditPointType } from '@leafer-in/interface'
5
6
 
6
7
  import { updateCursor, updateMoveCursor } from '../editor/cursor'
7
8
  import { EditorEvent } from '../event/EditorEvent'
@@ -15,10 +16,12 @@ export class EditBox extends Group implements IEditBox {
15
16
 
16
17
  public editor: IEditor
17
18
  public dragging: boolean
19
+ public moving: boolean
18
20
 
19
- public rect: IBox = new Box({ name: 'rect', hitFill: 'all', hitStroke: 'none', strokeAlign: 'center', hitRadius: 5 }) // target rect
20
- public circle: IEditPoint = new EditPoint({ name: 'circle', strokeAlign: 'outside', around: 'center', cursor: 'crosshair', hitRadius: 5 }) // rotate point
21
+ public view: IGroup = new Group() // 放置默认编辑工具控制点
21
22
 
23
+ public rect: IBox = new Box({ name: 'rect', hitFill: 'all', hitStroke: 'none', strokeAlign: 'center', hitRadius: 5 }) // target rect
24
+ public circle: IEditPoint = new EditPoint({ name: 'circle', strokeAlign: 'center', around: 'center', cursor: 'crosshair', hitRadius: 5 }) // rotate point
22
25
  public buttons: IGroup = new Group({ around: 'center', hitSelf: false })
23
26
 
24
27
  public resizePoints: IEditPoint[] = [] // topLeft, top, topRight, right, bottomRight, bottom, bottomLeft, left
@@ -40,16 +43,17 @@ export class EditBox extends Group implements IEditBox {
40
43
  this.editor = editor
41
44
  this.visible = false
42
45
  this.create()
46
+ this.rect.syncEventer = editor // rect的事件不会冒泡,需要手动传递给editor
43
47
  this.__listenEvents()
44
48
  }
45
49
 
46
50
  public create() {
47
51
  let rotatePoint: IEditPoint, resizeLine: IEditPoint, resizePoint: IEditPoint
48
- const { resizePoints, rotatePoints, resizeLines, rect, circle, buttons } = this
49
- 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 }]
52
+ const { view, resizePoints, rotatePoints, resizeLines, rect, circle, buttons } = this
53
+ const arounds: IAlign[] = ['bottom-right', 'bottom', 'bottom-left', 'left', 'top-left', 'top', 'top-right', 'right']
50
54
 
51
55
  for (let i = 0; i < 8; i++) {
52
- rotatePoint = new EditPoint({ around: arounds[i], width: 15, height: 15, hitFill: "all" })
56
+ rotatePoint = new EditPoint({ name: 'rotate-point', around: arounds[i], width: 15, height: 15, hitFill: "all" })
53
57
  rotatePoints.push(rotatePoint)
54
58
  this.listenPointEvents(rotatePoint, 'rotate', i)
55
59
 
@@ -59,91 +63,135 @@ export class EditBox extends Group implements IEditBox {
59
63
  this.listenPointEvents(resizeLine, 'resize', i)
60
64
  }
61
65
 
62
- resizePoint = new EditPoint({ name: 'resize-point', around: 'center', strokeAlign: 'outside', hitRadius: 5 })
66
+ resizePoint = new EditPoint({ name: 'resize-point', hitRadius: 5 })
63
67
  resizePoints.push(resizePoint)
64
68
  this.listenPointEvents(resizePoint, 'resize', i)
65
69
  }
66
70
 
67
- buttons.add(circle)
68
71
  this.listenPointEvents(circle, 'rotate', 2)
69
72
 
70
- this.addMany(...rotatePoints, rect, buttons, ...resizeLines, ...resizePoints)
73
+ view.addMany(...rotatePoints, rect, circle, buttons, ...resizeLines, ...resizePoints)
74
+ this.add(view)
71
75
  }
72
76
 
73
- // update
74
-
75
- public update(bounds: IBoundsData): void {
76
- const { config, list } = this.editor
77
- const { width, height } = bounds
78
- const { rect, circle, resizePoints, rotatePoints, resizeLines } = this
79
- const { middlePoint, resizeable, rotateable, stroke, strokeWidth } = config
77
+ public load(): void {
78
+ const { mergeConfig, element, single } = this.editor
79
+ const { rect, circle, resizePoints } = this
80
+ const { stroke, strokeWidth, moveable } = mergeConfig
80
81
 
81
82
  const pointsStyle = this.getPointsStyle()
82
83
  const middlePointsStyle = this.getMiddlePointsStyle()
83
84
 
84
- this.visible = list[0] && !list[0].locked // check locked
85
-
86
- let point = {} as IPointData, style: IRectInputData, rotateP: IRect, resizeP: IRect, resizeL: IRect
85
+ let resizeP: IRect
87
86
 
88
87
  for (let i = 0; i < 8; i++) {
88
+ resizeP = resizePoints[i]
89
+ resizeP.set(this.getPointStyle((i % 2) ? middlePointsStyle[((i - 1) / 2) % middlePointsStyle.length] : pointsStyle[(i / 2) % pointsStyle.length]))
90
+ if (!(i % 2)) resizeP.rotation = (i / 2) * 90
91
+ }
89
92
 
90
- AroundHelper.toPoint(AroundHelper.directionData[i], bounds, point)
91
- style = this.getPointStyle((i % 2) ? middlePointsStyle[((i - 1) / 2) % middlePointsStyle.length] : pointsStyle[(i / 2) % pointsStyle.length])
92
- resizeP = resizePoints[i], rotateP = rotatePoints[i], resizeL = resizeLines[Math.floor(i / 2)]
93
- resizeP.set(style)
94
- resizeP.set(point), rotateP.set(point), resizeL.set(point)
93
+ // rotate
94
+ circle.set(this.getPointStyle(mergeConfig.circle || mergeConfig.rotatePoint || pointsStyle[0]))
95
95
 
96
- // visible
97
- resizeP.visible = resizeL.visible = resizeable || rotateable
98
- rotateP.visible = rotateable && resizeable
96
+ // rect
97
+ rect.set({ stroke, strokeWidth, ...(mergeConfig.rect || {}) })
98
+ rect.hittable = !single && !!moveable
99
+
100
+ // 编辑框作为底部虚拟元素, 在 onSelect 方法移除
101
+ element.syncEventer = (single && moveable) ? rect : null
102
+ this.app.interaction.bottomList = (single && moveable) ? [{ target: rect, proxy: element }] : null
103
+
104
+ }
105
+
106
+ public update(bounds: IBoundsData): void {
107
+ this.visible = !this.editor.element.locked
99
108
 
100
- if (i % 2) { // top, right, bottom, left
109
+ if (this.view.worldOpacity) {
110
+ const { mergeConfig } = this.editor
111
+ const { width, height } = bounds
112
+ const { rect, circle, buttons, resizePoints, rotatePoints, resizeLines } = this
113
+ const { middlePoint, resizeable, rotateable, hideOnSmall } = mergeConfig
101
114
 
102
- resizeP.visible = rotateP.visible = !!middlePoint
115
+ const smallSize = typeof hideOnSmall === 'number' ? hideOnSmall : 10
116
+ const showPoints = !(hideOnSmall && width < smallSize && height < smallSize)
103
117
 
104
- if (((i + 1) / 2) % 2) { // top, bottom
105
- resizeL.width = width
106
- if (resizeP.width > width - 30) resizeP.visible = false
107
- } else {
108
- resizeL.height = height
109
- resizeP.rotation = 90
110
- if (resizeP.width > height - 30) resizeP.visible = false
118
+ let point = {} as IPointData, rotateP: IRect, resizeP: IRect, resizeL: IRect
119
+
120
+ for (let i = 0; i < 8; i++) {
121
+
122
+ AroundHelper.toPoint(AroundHelper.directionData[i], bounds, point)
123
+ resizeP = resizePoints[i]
124
+ rotateP = rotatePoints[i]
125
+ resizeL = resizeLines[Math.floor(i / 2)]
126
+ resizeP.set(point)
127
+ rotateP.set(point)
128
+ resizeL.set(point)
129
+
130
+ // visible
131
+ resizeP.visible = resizeL.visible = showPoints && !!(resizeable || rotateable)
132
+ rotateP.visible = showPoints && rotateable && resizeable && !mergeConfig.rotatePoint
133
+
134
+ if (i % 2) { // top, right, bottom, left
135
+
136
+ resizeP.visible = rotateP.visible = showPoints && !!middlePoint
137
+
138
+ if (((i + 1) / 2) % 2) { // top, bottom
139
+ resizeL.width = width
140
+ if (resizeP.width > width - 30) resizeP.visible = false
141
+ } else {
142
+ resizeL.height = height
143
+ resizeP.rotation = 90
144
+ if (resizeP.width > height - 30) resizeP.visible = false
145
+ }
111
146
  }
112
- } else {
113
- resizeP.rotation = (i / 2) * 90
147
+
114
148
  }
115
149
 
116
- }
150
+ // rotate
151
+ circle.visible = showPoints && rotateable && !!(mergeConfig.circle || mergeConfig.rotatePoint)
152
+ if (circle.visible) this.layoutCircle(mergeConfig)
117
153
 
118
- // rotate
119
- circle.visible = rotateable && !!config.rotatePoint
120
- circle.set(this.getPointStyle(config.rotatePoint || pointsStyle[0]))
154
+ // rect
155
+ if (rect.path) rect.path = null // line可能会变成path优先模式
156
+ rect.set({ ...bounds, visible: true })
121
157
 
122
- // rect
123
- rect.set({ stroke, strokeWidth, ...(config.rect || {}) })
124
- rect.set({ ...bounds, visible: true })
158
+ // buttons
159
+ buttons.visible = showPoints && buttons.children.length > 0
160
+ if (buttons.visible) this.layoutButtons(mergeConfig)
161
+ }
162
+ }
125
163
 
126
- // buttons
127
- this.layoutButtons()
164
+ protected layoutCircle(config: IEditorConfig): void {
165
+ const { circleDirection, circleMargin, buttonsMargin, buttonsDirection, middlePoint } = config
166
+ const direction = fourDirection.indexOf(circleDirection || ((this.buttons.children.length && buttonsDirection === 'bottom') ? 'top' : 'bottom'))
167
+ this.setButtonPosition(this.circle, direction, circleMargin || buttonsMargin, !!middlePoint)
128
168
  }
129
169
 
130
- protected layoutButtons(): void {
131
- const { buttons, resizePoints } = this
132
- const { buttonsDirection, buttonsFixed, buttonsMargin, middlePoint } = this.editor.config
170
+ protected layoutButtons(config: IEditorConfig): void {
171
+ const { buttons } = this
172
+ const { buttonsDirection, buttonsFixed, buttonsMargin, middlePoint } = config
133
173
 
134
174
  const { flippedX, flippedY } = this
135
175
  let index = fourDirection.indexOf(buttonsDirection)
136
176
  if ((index % 2 && flippedX) || ((index + 1) % 2 && flippedY)) {
137
177
  if (buttonsFixed) index = (index + 2) % 4 // flip x / y
138
178
  }
179
+
139
180
  const direction = buttonsFixed ? EditDataHelper.getRotateDirection(index, this.flippedOne ? this.rotation : -this.rotation, 4) : index
181
+ this.setButtonPosition(buttons, direction, buttonsMargin, !!middlePoint)
182
+
183
+ if (buttonsFixed) buttons.rotation = (direction - index) * 90
184
+ buttons.scaleX = flippedX ? -1 : 1
185
+ buttons.scaleY = flippedY ? -1 : 1
186
+ }
140
187
 
141
- const point = resizePoints[direction * 2 + 1] // 4 map 8 direction
188
+ protected setButtonPosition(buttons: IUI, direction: number, buttonsMargin: number, useMiddlePoint: boolean): void {
189
+ const point = this.resizePoints[direction * 2 + 1] // 4 map 8 direction
142
190
  const useX = direction % 2 // left / right
143
191
  const sign = (!direction || direction === 3) ? -1 : 1 // top / left = -1
144
192
 
145
- const useWidth = index % 2 // left / right origin direction
146
- const margin = (buttonsMargin + (useWidth ? ((middlePoint ? point.width : 0) + buttons.boxBounds.width) : ((middlePoint ? point.height : 0) + buttons.boxBounds.height)) / 2) * sign
193
+ const useWidth = direction % 2 // left / right origin direction
194
+ const margin = (buttonsMargin + (useWidth ? ((useMiddlePoint ? point.width : 0) + buttons.boxBounds.width) : ((useMiddlePoint ? point.height : 0) + buttons.boxBounds.height)) / 2) * sign
147
195
 
148
196
  if (useX) {
149
197
  buttons.x = point.x + margin
@@ -152,55 +200,71 @@ export class EditBox extends Group implements IEditBox {
152
200
  buttons.x = point.x
153
201
  buttons.y = point.y + margin
154
202
  }
203
+ }
155
204
 
156
- if (buttonsFixed) {
157
- buttons.rotation = (direction - index) * 90
158
- buttons.scaleX = flippedX ? -1 : 1
159
- buttons.scaleY = flippedY ? -1 : 1
160
- }
161
205
 
206
+ public unload(): void {
207
+ this.visible = false
162
208
  }
163
209
 
164
- public getPointStyle(userStyle?: IRectInputData): IRectInputData {
165
- const { stroke, strokeWidth, pointFill, pointSize, pointRadius } = this.editor.config
166
- const defaultStyle = { fill: pointFill, stroke, strokeWidth, width: pointSize, height: pointSize, cornerRadius: pointRadius }
210
+
211
+ public getPointStyle(userStyle?: IBoxInputData): IBoxInputData {
212
+ const { stroke, strokeWidth, pointFill, pointSize, pointRadius } = this.editor.mergeConfig
213
+ const defaultStyle = { fill: pointFill, stroke, strokeWidth, around: 'center', strokeAlign: 'center', width: pointSize, height: pointSize, cornerRadius: pointRadius } as IBoxInputData
167
214
  return userStyle ? Object.assign(defaultStyle, userStyle) : defaultStyle
168
215
  }
169
216
 
170
- public getPointsStyle(): IRectInputData[] {
171
- const { point } = this.editor.config
217
+ public getPointsStyle(): IBoxInputData[] {
218
+ const { point } = this.editor.mergeConfig
172
219
  return point instanceof Array ? point : [point]
173
220
  }
174
221
 
175
- public getMiddlePointsStyle(): IRectInputData[] {
176
- const { middlePoint } = this.editor.config
222
+ public getMiddlePointsStyle(): IBoxInputData[] {
223
+ const { middlePoint } = this.editor.mergeConfig
177
224
  return middlePoint instanceof Array ? middlePoint : (middlePoint ? [middlePoint] : this.getPointsStyle())
178
225
  }
179
226
 
227
+ protected onSelect(e: EditorEvent): void {
228
+ if (e.oldList.length === 1) {
229
+ e.oldList[0].syncEventer = null
230
+ if (this.app) this.app.interaction.bottomList = null
231
+ }
232
+ }
233
+
180
234
  // drag
181
235
 
182
236
  protected onDragStart(e: DragEvent): void {
183
237
  this.dragging = true
184
- if (e.target.name === 'rect') this.editor.opacity = this.editor.config.hideOnMove ? 0 : 1 // move
238
+ const { editor } = this
239
+ if (e.current.name === 'rect') {
240
+ this.moving = true
241
+ editor.dragStartPoint = { x: editor.element.x, y: editor.element.y }
242
+ editor.opacity = editor.mergeConfig.hideOnMove ? 0 : 1 // move
243
+ } else if ((e.current as IEditPoint).pointType === 'resize') {
244
+ editor.dragStartBounds = { ...editor.element.getLayoutBounds('box', 'local') }
245
+ }
185
246
  }
186
247
 
187
248
  protected onDragEnd(e: DragEvent): void {
188
249
  this.dragging = false
189
- if (e.target.name === 'rect') this.editor.opacity = 1 // move
250
+ this.moving = false
251
+ if (e.current.name === 'rect') this.editor.opacity = 1 // move
252
+
190
253
  }
191
254
 
192
255
  protected onDrag(e: DragEvent): void {
193
256
  const { editor } = this
194
- const point = e.current as IEditPoint
195
- if (point.pointType === 'rotate' || e.metaKey || e.ctrlKey || !editor.config.resizeable) {
196
- if (editor.config.rotateable) editor.onRotate(e)
197
- } else {
257
+ const point = this.enterPoint = e.current as IEditPoint
258
+ if (point.pointType === 'rotate' || e.metaKey || e.ctrlKey || !editor.mergeConfig.resizeable) {
259
+ if (editor.mergeConfig.rotateable) editor.onRotate(e)
260
+ } else if (point.pointType === 'resize') {
198
261
  editor.onScale(e)
199
262
  }
263
+ updateCursor(editor, e)
200
264
  }
201
265
 
202
266
  public onArrow(e: IKeyEvent): void {
203
- if (this.editor.hasTarget) {
267
+ if (this.editor.editing && this.editor.mergeConfig.keyEvent) {
204
268
  const move = { x: 0, y: 0 }
205
269
  const distance = e.shiftKey ? 10 : 1
206
270
  switch (e.code) {
@@ -216,18 +280,34 @@ export class EditBox extends Group implements IEditBox {
216
280
  case 'ArrowRight':
217
281
  move.x = distance
218
282
  }
219
- if (move.x || move.y) this.editor.move(move.x, move.y)
283
+ this.editor.move(move)
220
284
  }
221
285
  }
222
286
 
223
- protected onDoubleClick(): void {
287
+
288
+ protected onDoubleTap(e: PointerEvent): void {
289
+ if (this.editor.mergeConfig.openInner === 'double') this.openInner(e)
290
+ }
291
+
292
+ protected onLongPress(e: PointerEvent): void {
293
+ if (this.editor.mergeConfig.openInner === 'long') this.openInner(e)
294
+ }
295
+
296
+ protected openInner(e: PointerEvent): void {
224
297
  const { editor } = this
225
- if (editor.single && editor.element.isBranch) {
226
- //list[0].hitChildren = true
298
+ if (editor.single) {
299
+ const { element } = editor
300
+ if (element.isBranch) {
301
+ editor.openGroup(element as IGroup)
302
+ editor.target = editor.selector.findDeepOne(e)
303
+ } else {
304
+ editor.openInnerEditor()
305
+ }
227
306
  }
228
307
  }
229
308
 
230
- public listenPointEvents(point: IEditPoint, type: IEditPointType, direction: IDirection8): void {
309
+
310
+ public listenPointEvents(point: IEditPoint, type: IEditPointType, direction: Direction9): void {
231
311
  const { editor } = this
232
312
  point.direction = direction
233
313
  point.pointType = type
@@ -241,12 +321,15 @@ export class EditBox extends Group implements IEditBox {
241
321
  protected __listenEvents(): void {
242
322
  const { rect, editor } = this
243
323
  this.__eventIds = [
244
- editor.on_(EditorEvent.SELECT, () => { this.visible = editor.hasTarget }),
324
+ editor.on_(EditorEvent.SELECT, this.onSelect, this),
325
+
245
326
  rect.on_(DragEvent.START, this.onDragStart, this),
246
327
  rect.on_(DragEvent.DRAG, editor.onMove, editor),
247
328
  rect.on_(DragEvent.END, this.onDragEnd, this),
329
+
248
330
  rect.on_(PointerEvent.ENTER, () => updateMoveCursor(editor)),
249
- rect.on_(PointerEvent.DOUBLE_CLICK, this.onDoubleClick, this)
331
+ rect.on_(PointerEvent.DOUBLE_TAP, this.onDoubleTap, this),
332
+ rect.on_(PointerEvent.LONG_PRESS, this.onLongPress, this)
250
333
  ]
251
334
  }
252
335
 
@@ -0,0 +1,37 @@
1
+ import { ILeaferCanvas, IRenderOptions } from '@leafer-ui/interface'
2
+ import { UI } from '@leafer-ui/draw'
3
+
4
+ import { IEditor } from '@leafer-in/interface'
5
+
6
+
7
+ export class EditMask extends UI {
8
+
9
+ public editor: IEditor
10
+
11
+ constructor(editor: IEditor) {
12
+ super()
13
+ this.editor = editor
14
+ this.hittable = false
15
+ }
16
+
17
+ public __draw(canvas: ILeaferCanvas, options: IRenderOptions): void {
18
+ const { editor } = this
19
+ const { mask } = editor.mergeConfig
20
+
21
+ if (mask && editor.list.length) {
22
+ const { rect } = editor.editBox
23
+ const { width, height } = rect.__
24
+
25
+ canvas.resetTransform()
26
+ canvas.fillWorld(canvas.bounds, mask === true ? 'rgba(0,0,0,0.8)' : mask)
27
+ canvas.setWorld(rect.__world, options.matrix)
28
+ canvas.clearRect(0, 0, width, height)
29
+ }
30
+ }
31
+
32
+ public destroy(): void {
33
+ this.editor = null
34
+ super.destroy()
35
+ }
36
+
37
+ }
@@ -1,9 +1,9 @@
1
- import { Box } from '@leafer-ui/core'
1
+ import { Box, Direction9 } from '@leafer-ui/draw'
2
2
 
3
- import { IDirection8, IEditPoint, IEditPointType } from '@leafer-in/interface'
3
+ import { IEditPoint, IEditPointType } from '@leafer-in/interface'
4
4
 
5
5
 
6
6
  export class EditPoint extends Box implements IEditPoint {
7
- public direction: IDirection8
7
+ public direction: Direction9
8
8
  public pointType: IEditPointType
9
9
  }