@leafer-in/editor 1.7.0 → 1.9.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leafer-in/editor",
3
- "version": "1.7.0",
3
+ "version": "1.9.0",
4
4
  "description": "@leafer-in/editor",
5
5
  "author": "Chao (Leafer) Wan",
6
6
  "license": "MIT",
@@ -34,10 +34,10 @@
34
34
  "leaferjs"
35
35
  ],
36
36
  "peerDependencies": {
37
- "@leafer-ui/draw": "^1.7.0",
38
- "@leafer-ui/core": "^1.7.0",
39
- "@leafer-in/resize": "^1.7.0",
40
- "@leafer-ui/interface": "^1.7.0",
41
- "@leafer-in/interface": "^1.7.0"
37
+ "@leafer-ui/draw": "^1.9.0",
38
+ "@leafer-ui/core": "^1.9.0",
39
+ "@leafer-in/resize": "^1.9.0",
40
+ "@leafer-ui/interface": "^1.9.0",
41
+ "@leafer-in/interface": "^1.9.0"
42
42
  }
43
43
  }
package/src/Editor.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { IGroupInputData, IUI, IEventListenerId, IPointData, ILeafList, IEditSize, IGroup, IObject, IAlign, IAxis, IFunction, IMatrix, IApp } from '@leafer-ui/interface'
2
- import { Group, DataHelper, LeafList, RenderEvent, LeafHelper, Direction9, Plugin } from '@leafer-ui/draw'
2
+ import { Group, DataHelper, LeafList, RenderEvent, LeafHelper, Direction9, Plugin, isString } from '@leafer-ui/draw'
3
3
  import { DragEvent, RotateEvent, ZoomEvent, MoveEvent, useModule } from '@leafer-ui/core'
4
4
 
5
5
  import { IEditBox, IEditPoint, IEditor, IEditorConfig, IEditTool, IEditorScaleEvent, IInnerEditor, ISimulateElement } from '@leafer-in/interface'
@@ -52,9 +52,15 @@ export class Editor extends Group implements IEditor {
52
52
  public get multiple(): boolean { return this.list.length > 1 }
53
53
  public get single(): boolean { return this.list.length === 1 }
54
54
 
55
+ public get dragPoint(): IEditPoint { return this.editBox.dragPoint }
56
+
55
57
  public get dragging(): boolean { return this.editBox.dragging }
58
+ public get gesturing(): boolean { return this.editBox.gesturing } // 手势操作元素中
59
+
56
60
  public get moving(): boolean { return this.editBox.moving }
57
- public get dragPoint(): IEditPoint { return this.editBox.dragPoint }
61
+ public get resizing(): boolean { return this.editBox.resizing }
62
+ public get rotating(): boolean { return this.editBox.rotating }
63
+ public get skewing(): boolean { return this.editBox.skewing }
58
64
 
59
65
  // 组件
60
66
 
@@ -131,22 +137,26 @@ export class Editor extends Group implements IEditor {
131
137
  }
132
138
 
133
139
  public updateEditTool(): void {
134
- let tool = this.editTool
135
- if (tool) {
136
- this.editBox.unload()
137
- tool.unload()
138
- this.editTool = null
139
- }
140
+ this.unloadEditTool()
140
141
 
141
142
  if (this.editing) {
142
- const tag = this.element.editOuter || 'EditTool'
143
- tool = this.editTool = this.editToolList[tag] = this.editToolList[tag] || EditToolCreator.get(tag, this)
143
+ const name = this.element.editOuter || 'EditTool'
144
+ const tool = this.editTool = this.editToolList[name] = this.editToolList[name] || EditToolCreator.get(name, this)
144
145
  this.editBox.load()
145
146
  tool.load()
146
147
  this.update()
147
148
  }
148
149
  }
149
150
 
151
+ public unloadEditTool(): void {
152
+ let tool = this.editTool
153
+ if (tool) {
154
+ this.editBox.unload()
155
+ tool.unload()
156
+ this.editTool = null
157
+ }
158
+ }
159
+
150
160
 
151
161
  // get
152
162
 
@@ -249,31 +259,37 @@ export class Editor extends Group implements IEditor {
249
259
 
250
260
  public emitGroupEvent(type: string, group?: IGroup): void {
251
261
  const event = new EditorGroupEvent(type, { editTarget: group })
252
- this.emitEvent(event)
262
+ if (!group || !group.syncEventer) this.emitEvent(event) // 单选时,元素会自动将事件传递给 editor,避免重复触发
253
263
  if (group) group.emitEvent(event)
254
264
  }
255
265
 
256
266
  // inner
257
267
 
258
- public openInnerEditor(target?: IUI, select?: boolean): void {
268
+ public openInnerEditor(target?: IUI, nameOrSelect?: string | boolean, select?: boolean): void {
269
+ let name: string
270
+ if (isString(nameOrSelect)) name = nameOrSelect
271
+ else if (!select) select = nameOrSelect
272
+
259
273
  if (target && select) this.target = target
274
+
260
275
  if (this.single) {
261
276
  const editTarget = target || this.element
262
- const tag = editTarget.editInner
263
- if (tag && EditToolCreator.list[tag]) {
277
+ name || (name = editTarget.editInner)
278
+ if (name && EditToolCreator.list[name]) {
264
279
  this.editTool.unload()
265
280
  this.innerEditing = true
266
- this.innerEditor = this.editToolList[tag] || EditToolCreator.get(tag, this)
281
+ this.innerEditor = this.editToolList[name] = this.editToolList[name] || EditToolCreator.get(name, this)
267
282
  this.innerEditor.editTarget = editTarget
268
283
 
269
284
  this.emitInnerEvent(InnerEditorEvent.BEFORE_OPEN)
270
285
  this.innerEditor.load()
271
286
  this.emitInnerEvent(InnerEditorEvent.OPEN)
287
+ console.log('hello')
272
288
  }
273
289
  }
274
290
  }
275
291
 
276
- public closeInnerEditor(): void {
292
+ public closeInnerEditor(onlyInnerEditor?: boolean): void {
277
293
  if (this.innerEditing) {
278
294
  this.innerEditing = false
279
295
 
@@ -281,7 +297,7 @@ export class Editor extends Group implements IEditor {
281
297
  this.innerEditor.unload()
282
298
  this.emitInnerEvent(InnerEditorEvent.CLOSE)
283
299
 
284
- this.editTool.load()
300
+ if (!onlyInnerEditor) this.updateEditTool()
285
301
  this.innerEditor = null
286
302
  }
287
303
  }
@@ -289,7 +305,7 @@ export class Editor extends Group implements IEditor {
289
305
  public emitInnerEvent(type: string): void {
290
306
  const { innerEditor } = this, { editTarget } = innerEditor
291
307
  const event = new InnerEditorEvent(type, { editTarget, innerEditor })
292
- this.emitEvent(event)
308
+ if (!editTarget.syncEventer) this.emitEvent(event) // 单选时,元素会自动将事件传递给 editor,避免重复触发
293
309
  editTarget.emitEvent(event)
294
310
  }
295
311
 
@@ -337,14 +353,7 @@ export class Editor extends Group implements IEditor {
337
353
  const { app, leafer, editMask } = this
338
354
  this.targetEventIds = [
339
355
  leafer.on_(RenderEvent.START, this.onRenderStart, this),
340
-
341
- app.on_([
342
- [RenderEvent.CHILD_START, this.onAppRenderStart, this],
343
-
344
- [MoveEvent.BEFORE_MOVE, this.onMove, this, true],
345
- [ZoomEvent.BEFORE_ZOOM, this.onScale, this, true],
346
- [RotateEvent.BEFORE_ROTATE, this.onRotate, this, true],
347
- ])
356
+ app.on_(RenderEvent.CHILD_START, this.onAppRenderStart, this)
348
357
  ]
349
358
  if (editMask.visible) editMask.forceRender()
350
359
  }
@@ -1,6 +1,6 @@
1
1
  import { IFunction, ILeaf, IObject, IUI, } from '@leafer-ui/interface'
2
2
  import { IEditor } from '@leafer-in/interface'
3
- import { defineKey, isNull } from '@leafer-ui/draw'
3
+ import { defineKey, isNull, isArray, isObject } from '@leafer-ui/draw'
4
4
 
5
5
  import { EditorEvent } from '../event/EditorEvent'
6
6
 
@@ -18,12 +18,13 @@ export function targetAttr(fn: IFunction) {
18
18
 
19
19
  const isSelect = key === 'target'
20
20
  if (isSelect) {
21
- if (value instanceof Array && value.length > 1 && value[0].locked) value.splice(0, 1) // fix: 单个锁定 + shift多选
21
+ if (isArray(value) && value.length > 1 && value[0].locked) value.splice(0, 1) // fix: 单个锁定 + shift多选
22
+ if ((this as IEditor).single) (this as IEditor).element.syncEventer = null // 重置 EditBox.load() 设置
22
23
 
23
24
  const { beforeSelect } = (this as IEditor).config
24
25
  if (beforeSelect) {
25
26
  const check = beforeSelect({ target: value })
26
- if (typeof check === 'object') value = check
27
+ if (isObject(check)) value = check
27
28
  else if (check === false) return
28
29
  }
29
30
  }
@@ -44,8 +45,9 @@ export function mergeConfigAttr() {
44
45
  return (target: IEditor, key: string) => {
45
46
  defineKey(target, key, {
46
47
  get() {
47
- const { config, element, dragPoint } = this, mergeConfig = { ...config } // 实时合并,后期可优化
48
- if (element && element.editConfig) Object.assign(mergeConfig, element.editConfig)
48
+ const { config, element, dragPoint, editBox } = this, mergeConfig = { ...config } // 实时合并,后期可优化
49
+ if (element && element.editConfig) Object.assign(mergeConfig, element.editConfig) // 元素上的配置
50
+ if (editBox.config) Object.assign(mergeConfig, editBox.config) // EditBox 上的配置
49
51
  if (dragPoint) {
50
52
  if (dragPoint.editConfig) Object.assign(mergeConfig, dragPoint.editConfig)
51
53
  if (mergeConfig.editSize === 'font-size') mergeConfig.lockRatio = true // 强制锁定比例
@@ -1,11 +1,10 @@
1
- import { IRect, IEventListenerId, IBoundsData, IPointData, IKeyEvent, IGroup, IBox, IBoxInputData, IAlign, IUI, IEditorConfig, IEditorDragStartData, IEventParams, ITransformTool } from '@leafer-ui/interface'
2
- import { Group, Box, Text, AroundHelper, Direction9, ResizeEvent } from '@leafer-ui/draw'
3
- import { DragEvent, PointerEvent, KeyEvent } from '@leafer-ui/core'
1
+ import { IRect, IEventListenerId, IBoundsData, IPointData, IKeyEvent, IGroup, IBox, IBoxInputData, IAlign, IUI, IEditorConfig, IEditorDragStartData, IEventParams, ITransformTool, IUIEvent } from '@leafer-ui/interface'
2
+ import { Group, Box, Text, AroundHelper, Direction9, ResizeEvent, BoundsHelper, isArray, isString, isNumber } from '@leafer-ui/draw'
3
+ import { DragEvent, PointerEvent, KeyEvent, RotateEvent, ZoomEvent, MoveEvent } from '@leafer-ui/core'
4
4
 
5
5
  import { IEditBox, IEditor, IEditPoint, IEditPointType } from '@leafer-in/interface'
6
6
 
7
- import { updateCursor, updateMoveCursor } from '../editor/cursor'
8
- import { EditorEvent } from '../event/EditorEvent'
7
+ import { updatePointCursor, updateMoveCursor } from '../editor/cursor'
9
8
  import { EditPoint } from './EditPoint'
10
9
  import { EditDataHelper } from '../helper/EditDataHelper'
11
10
 
@@ -17,7 +16,12 @@ export class EditBox extends Group implements IEditBox {
17
16
  public editor: IEditor
18
17
 
19
18
  public dragging: boolean
19
+ public gesturing: boolean
20
+
20
21
  public moving: boolean
22
+ public resizing: boolean
23
+ public rotating: boolean
24
+ public skewing: boolean
21
25
 
22
26
  public view: IGroup = new Group() // 放置默认编辑工具控制点
23
27
 
@@ -38,8 +42,8 @@ export class EditBox extends Group implements IEditBox {
38
42
  public mergedConfig: IEditorConfig
39
43
 
40
44
  public get mergeConfig(): IEditorConfig {
41
- const { config } = this, { mergeConfig } = this.editor
42
- return this.mergedConfig = config ? { ...mergeConfig, ...config } : mergeConfig
45
+ const { config } = this, { mergeConfig, editBox } = this.editor
46
+ return this.mergedConfig = config && (editBox !== this) ? { ...mergeConfig, ...config } : mergeConfig // 可能会出现多个editBox的情况
43
47
  }
44
48
 
45
49
  protected _target: IUI
@@ -58,6 +62,13 @@ export class EditBox extends Group implements IEditBox {
58
62
  public get flippedY(): boolean { return this.scaleY < 0 }
59
63
  public get flippedOne(): boolean { return this.scaleX * this.scaleY < 0 }
60
64
 
65
+ public get canUse(): boolean { return (this.visible && this.view.visible) as boolean } // 编辑框是否处于激活状态
66
+ public get canGesture(): boolean { // 是否支持手势
67
+ if (!this.canUse) return false
68
+ const { moveable, resizeable, rotateable } = this.mergeConfig
69
+ return isString(moveable) || isString(resizeable) || isString(rotateable)
70
+ }
71
+
61
72
  protected __eventIds: IEventListenerId[] = []
62
73
 
63
74
  constructor(editor: IEditor) {
@@ -95,6 +106,7 @@ export class EditBox extends Group implements IEditBox {
95
106
  this.add(view)
96
107
  }
97
108
 
109
+
98
110
  public load(): void {
99
111
  const { target, mergeConfig, single, rect, circle, resizePoints } = this
100
112
  const { stroke, strokeWidth } = mergeConfig
@@ -102,12 +114,14 @@ export class EditBox extends Group implements IEditBox {
102
114
  const pointsStyle = this.getPointsStyle()
103
115
  const middlePointsStyle = this.getMiddlePointsStyle()
104
116
 
117
+ this.visible = !target.locked
118
+
105
119
  let resizeP: IRect
106
120
 
107
121
  for (let i = 0; i < 8; i++) {
108
122
  resizeP = resizePoints[i]
109
123
  resizeP.set(this.getPointStyle((i % 2) ? middlePointsStyle[((i - 1) / 2) % middlePointsStyle.length] : pointsStyle[(i / 2) % pointsStyle.length]))
110
- if (!(i % 2)) resizeP.rotation = (i / 2) * 90
124
+ resizeP.rotation = ((i - (i % 2 ? 1 : 0)) / 2) * 90
111
125
  }
112
126
 
113
127
  // rotate
@@ -118,12 +132,12 @@ export class EditBox extends Group implements IEditBox {
118
132
 
119
133
  const syncEventer = single && this.transformTool.editTool
120
134
 
135
+ // 编辑框作为底部虚拟元素, 在 unload() 中重置
121
136
  rect.hittable = !syncEventer
122
137
  rect.syncEventer = syncEventer && this.editor // 单选下 rect 的事件不会冒泡,需要手动传递给editor
123
138
 
124
- // 编辑框作为底部虚拟元素, 在 onSelect 方法移除
125
139
  if (syncEventer) {
126
- target.syncEventer = rect
140
+ target.syncEventer = rect // 在 target 属性装饰中重置
127
141
  this.app.interaction.bottomList = [{ target: rect, proxy: target }]
128
142
  }
129
143
 
@@ -133,21 +147,29 @@ export class EditBox extends Group implements IEditBox {
133
147
  public update(): void {
134
148
  const { editor } = this
135
149
  const { x, y, scaleX, scaleY, rotation, skewX, skewY, width, height } = this.target.getLayoutBounds('box', editor, true)
150
+ this.visible = !this.target.locked
136
151
  this.set({ x, y, scaleX, scaleY, rotation, skewX, skewY })
137
152
  this.updateBounds({ x: 0, y: 0, width, height })
138
153
  }
139
154
 
155
+ public unload(): void {
156
+ this.visible = false
157
+ if (this.app) this.rect.syncEventer = this.app.interaction.bottomList = null
158
+ }
159
+
160
+
140
161
  public updateBounds(bounds: IBoundsData): void {
141
162
  const { editMask } = this.editor
142
163
  const { mergeConfig, single, rect, circle, buttons, resizePoints, rotatePoints, resizeLines } = this
143
- const { middlePoint, resizeable, rotateable, hideOnSmall, editBox, mask } = mergeConfig
164
+ const { middlePoint, resizeable, rotateable, hideOnSmall, editBox, mask, spread, hideRotatePoints, hideResizeLines } = mergeConfig
144
165
 
145
- this.visible = !this.target.locked
146
166
  editMask.visible = mask ? true : 0
147
167
 
168
+ if (spread) BoundsHelper.spread(bounds, spread)
169
+
148
170
  if (this.view.worldOpacity) {
149
171
  const { width, height } = bounds
150
- const smallSize = typeof hideOnSmall === 'number' ? hideOnSmall : 10
172
+ const smallSize = isNumber(hideOnSmall) ? hideOnSmall : 10
151
173
  const showPoints = editBox && !(hideOnSmall && width < smallSize && height < smallSize)
152
174
 
153
175
  let point = {} as IPointData, rotateP: IRect, resizeP: IRect, resizeL: IRect
@@ -157,17 +179,19 @@ export class EditBox extends Group implements IEditBox {
157
179
  AroundHelper.toPoint(AroundHelper.directionData[i], bounds, point)
158
180
  resizeP = resizePoints[i]
159
181
  rotateP = rotatePoints[i]
160
- resizeL = resizeLines[Math.floor(i / 2)]
161
182
  resizeP.set(point)
162
183
  rotateP.set(point)
163
- resizeL.set(point)
164
184
 
165
185
  // visible
166
- resizeP.visible = resizeL.visible = showPoints && !!(resizeable || rotateable)
167
- rotateP.visible = showPoints && rotateable && resizeable && !mergeConfig.rotatePoint
186
+ resizeP.visible = showPoints && !!(resizeable || rotateable)
187
+ rotateP.visible = showPoints && rotateable && resizeable && !hideRotatePoints
168
188
 
169
189
  if (i % 2) { // top, right, bottom, left
170
190
 
191
+ resizeL = resizeLines[(i - 1) / 2]
192
+ resizeL.set(point)
193
+
194
+ resizeL.visible = resizeP.visible && !hideResizeLines
171
195
  resizeP.visible = rotateP.visible = showPoints && !!middlePoint
172
196
 
173
197
  if (((i + 1) / 2) % 2) { // top, bottom
@@ -175,7 +199,6 @@ export class EditBox extends Group implements IEditBox {
175
199
  if (hideOnSmall && resizeP.width * 2 > width) resizeP.visible = false
176
200
  } else {
177
201
  resizeL.height = height
178
- resizeP.rotation = 90
179
202
  if (hideOnSmall && resizeP.width * 2 > height) resizeP.visible = false
180
203
  }
181
204
  }
@@ -238,11 +261,6 @@ export class EditBox extends Group implements IEditBox {
238
261
  }
239
262
 
240
263
 
241
- public unload(): void {
242
- this.visible = false
243
- }
244
-
245
-
246
264
  public getPointStyle(userStyle?: IBoxInputData): IBoxInputData {
247
265
  const { stroke, strokeWidth, pointFill, pointSize, pointRadius } = this.mergedConfig
248
266
  const defaultStyle = { fill: pointFill, stroke, strokeWidth, around: 'center', strokeAlign: 'center', width: pointSize, height: pointSize, cornerRadius: pointRadius, offsetX: 0, offsetY: 0, editConfig } as IBoxInputData
@@ -251,31 +269,35 @@ export class EditBox extends Group implements IEditBox {
251
269
 
252
270
  public getPointsStyle(): IBoxInputData[] {
253
271
  const { point } = this.mergedConfig
254
- return point instanceof Array ? point : [point]
272
+ return isArray(point) ? point : [point]
255
273
  }
256
274
 
257
275
  public getMiddlePointsStyle(): IBoxInputData[] {
258
276
  const { middlePoint } = this.mergedConfig
259
- return middlePoint instanceof Array ? middlePoint : (middlePoint ? [middlePoint] : this.getPointsStyle())
277
+ return isArray(middlePoint) ? middlePoint : (middlePoint ? [middlePoint] : this.getPointsStyle())
260
278
  }
261
279
 
262
- protected onSelect(e: EditorEvent): void {
263
- if (e.oldList.length === 1) {
264
- e.oldList[0].syncEventer = null
265
- if (this.app) this.app.interaction.bottomList = null
266
- }
267
- }
268
280
 
269
281
  // drag
270
282
 
271
283
  protected onDragStart(e: DragEvent): void {
272
284
  this.dragging = true
273
285
  const point = this.dragPoint = e.current as IEditPoint, { pointType } = point
274
- const { editor, dragStartData } = this, { target } = this
286
+ const { editor, dragStartData } = this, { target } = this, { moveable, resizeable, rotateable, skewable, hideOnMove } = this.mergeConfig
287
+
288
+ // 确定模式
275
289
  if (point.name === 'rect') {
276
- this.moving = true
277
- editor.opacity = this.mergeConfig.hideOnMove ? 0 : 1 // move
290
+ moveable && (this.moving = true)
291
+ editor.opacity = hideOnMove ? 0 : 1 // move
292
+ } else {
293
+ if (pointType.includes('rotate') || this.isHoldRotateKey(e) || !resizeable) {
294
+ rotateable && (this.rotating = true)
295
+ if (pointType === 'resize-rotate') resizeable && (this.resizing = true)
296
+ else if (point.name === 'resize-line') skewable && (this.skewing = true), this.rotating = false
297
+ } else if (pointType === 'resize') resizeable && (this.resizing = true)
298
+ if (pointType === 'skew') skewable && (this.skewing = true)
278
299
  }
300
+
279
301
  dragStartData.x = e.x
280
302
  dragStartData.y = e.y
281
303
  dragStartData.point = { x: target.x, y: target.y } // 用于移动
@@ -285,37 +307,80 @@ export class EditBox extends Group implements IEditBox {
285
307
  }
286
308
 
287
309
  protected onDragEnd(e: DragEvent): void {
288
- this.dragging = false
310
+ if (this.mergeConfig.dragLimitAnimate && this.moving) this.transformTool.onMove(e)
311
+
289
312
  this.dragPoint = null
290
- this.moving = false
313
+ this.resetDoing()
291
314
  const { name, pointType } = e.current as IEditPoint
292
315
  if (name === 'rect') this.editor.opacity = 1 // move
293
316
  if (pointType && pointType.includes('resize')) ResizeEvent.resizingKeys = null
294
317
  }
295
318
 
296
319
  protected onDrag(e: DragEvent): void {
297
- const { transformTool } = this, point = e.current as IEditPoint
298
- if (point.name === 'rect') {
320
+ const { transformTool, moving, resizing, rotating, skewing } = this
321
+ if (moving) {
299
322
  transformTool.onMove(e)
300
323
  updateMoveCursor(this)
301
- } else {
302
- const { pointType } = this.enterPoint = point
303
- if (pointType.includes('rotate') || e.metaKey || e.ctrlKey || !this.mergeConfig.resizeable) {
304
- transformTool.onRotate(e)
305
- if (pointType === 'resize-rotate') transformTool.onScale(e)
306
- } else if (pointType === 'resize') transformTool.onScale(e)
307
- if (pointType === 'skew') transformTool.onSkew(e)
308
- updateCursor(this, e)
324
+ } else if (resizing || rotating || skewing) {
325
+ const point = e.current as IEditPoint
326
+ if (point.pointType) this.enterPoint = point// 防止变化
327
+ if (rotating) transformTool.onRotate(e)
328
+ if (resizing) transformTool.onScale(e)
329
+ if (skewing) transformTool.onSkew(e)
330
+ updatePointCursor(this, e)
331
+ }
332
+ }
333
+
334
+ protected resetDoing(): void {
335
+ if (this.canUse) this.dragging = this.gesturing = this.moving = this.resizing = this.rotating = this.skewing = false
336
+ }
337
+
338
+ // 手势控制元素
339
+
340
+ public onMove(e: MoveEvent): void {
341
+ if (this.canGesture && e.moveType !== 'drag') {
342
+ e.stop()
343
+ if (isString(this.mergeConfig.moveable)) {
344
+ this.gesturing = this.moving = true
345
+ this.transformTool.onMove(e)
346
+ }
309
347
  }
310
348
  }
311
349
 
350
+ public onScale(e: ZoomEvent): void {
351
+ if (this.canGesture) {
352
+ e.stop()
353
+ if (isString(this.mergeConfig.resizeable)) {
354
+ this.gesturing = this.resizing = true
355
+ this.transformTool.onScale(e)
356
+ }
357
+ }
358
+ }
359
+
360
+ public onRotate(e: RotateEvent): void {
361
+ if (this.canGesture) {
362
+ e.stop()
363
+ if (isString(this.mergeConfig.rotateable)) {
364
+ this.gesturing = this.rotating = true
365
+ this.transformTool.onRotate(e)
366
+ }
367
+ }
368
+ }
369
+
370
+ // 键盘
371
+ public isHoldRotateKey(e: IUIEvent): boolean { // 按住ctrl在控制点上变旋转功能
372
+ const { rotateKey } = this.mergedConfig
373
+ if (rotateKey) return e.isHoldKeys(rotateKey)
374
+ return e.metaKey || e.ctrlKey
375
+ }
376
+
312
377
  protected onKey(e: KeyEvent): void {
313
- updateCursor(this, e)
378
+ updatePointCursor(this, e)
314
379
  }
315
380
 
316
381
  public onArrow(e: IKeyEvent): void {
317
- const { editor } = this
318
- if (editor.editing && this.mergeConfig.keyEvent) {
382
+ const { editor, transformTool } = this
383
+ if (this.canUse && editor.editing && this.mergeConfig.keyEvent) {
319
384
  let x = 0, y = 0
320
385
  const distance = e.shiftKey ? 10 : 1
321
386
  switch (e.code) {
@@ -331,7 +396,7 @@ export class EditBox extends Group implements IEditBox {
331
396
  case 'ArrowRight':
332
397
  x = distance
333
398
  }
334
- if (x || y) editor.move(x, y)
399
+ if (x || y) transformTool.move(x, y)
335
400
  }
336
401
  }
337
402
 
@@ -376,7 +441,7 @@ export class EditBox extends Group implements IEditBox {
376
441
  [DragEvent.END, this.onDragEnd, this],
377
442
  [PointerEvent.LEAVE, () => { this.enterPoint = null }],
378
443
  ]
379
- if (point.name !== 'circle') events.push([PointerEvent.ENTER, (e: PointerEvent) => { this.enterPoint = point, updateCursor(this, e) }])
444
+ if (point.name !== 'circle') events.push([PointerEvent.ENTER, (e: PointerEvent) => { this.enterPoint = point, updatePointCursor(this, e) }])
380
445
  this.__eventIds.push(point.on_(events))
381
446
  }
382
447
 
@@ -384,8 +449,6 @@ export class EditBox extends Group implements IEditBox {
384
449
  const { rect, editor, __eventIds: events } = this
385
450
 
386
451
  events.push(
387
- editor.on_(EditorEvent.SELECT, this.onSelect, this),
388
-
389
452
  rect.on_([
390
453
  [DragEvent.START, this.onDragStart, this],
391
454
  [DragEvent.DRAG, this.onDrag, this],
@@ -401,7 +464,13 @@ export class EditBox extends Group implements IEditBox {
401
464
  events.push(
402
465
  editor.app.on_([
403
466
  [[KeyEvent.HOLD, KeyEvent.UP], this.onKey, this],
404
- [KeyEvent.DOWN, this.onArrow, this]
467
+ [KeyEvent.DOWN, this.onArrow, this],
468
+ [MoveEvent.BEFORE_MOVE, this.onMove, this, true],
469
+ [ZoomEvent.BEFORE_ZOOM, this.onScale, this, true],
470
+ [RotateEvent.BEFORE_ROTATE, this.onRotate, this, true],
471
+ [MoveEvent.END, this.resetDoing, this],
472
+ [ZoomEvent.END, this.resetDoing, this],
473
+ [RotateEvent.END, this.resetDoing, this],
405
474
  ])
406
475
  )
407
476
  })
@@ -54,16 +54,16 @@ export class EditSelect extends Group implements IEditSelect {
54
54
 
55
55
  protected onSelect(): void {
56
56
  if (this.running) {
57
- const { mergeConfig, list } = this.editor
58
- const { stroke, strokeWidth, selectedStyle } = mergeConfig
59
- this.targetStroker.setTarget(list, { stroke, strokeWidth: Math.max(1, strokeWidth / 2), ...(selectedStyle || {}) })
57
+ this.targetStroker.setTarget(this.editor.list)
60
58
  this.hoverStroker.target = null
61
59
  }
62
60
  }
63
61
 
64
62
  public update(): void {
65
63
  this.hoverStroker.update()
66
- this.targetStroker.update()
64
+
65
+ const { stroke, strokeWidth, selectedStyle } = this.editor.mergedConfig
66
+ this.targetStroker.update({ stroke, strokeWidth: strokeWidth && Math.max(1, strokeWidth / 2), ...(selectedStyle || {}) })
67
67
  }
68
68
 
69
69
  // move / down
@@ -125,7 +125,7 @@ export class EditSelect extends Group implements IEditSelect {
125
125
 
126
126
  } else if (this.allow(e.target)) {
127
127
 
128
- if (!e.shiftKey) editor.target = null
128
+ if (!this.isHoldMultipleSelectKey(e)) editor.target = null
129
129
 
130
130
  }
131
131
  }
@@ -209,7 +209,7 @@ export class EditSelect extends Group implements IEditSelect {
209
209
  protected allowDrag(e: DragEvent) {
210
210
  const { boxSelect, multipleSelect } = this.editor.mergeConfig
211
211
  if (this.running && (multipleSelect && boxSelect) && !e.target.draggable) {
212
- return (!this.editor.editing && this.allow(e.target)) || (e.shiftKey && !findOne(e.path))
212
+ return (!this.editor.editing && this.allow(e.target)) || (this.isHoldMultipleSelectKey(e) && !findOne(e.path))
213
213
  } else {
214
214
  return false
215
215
  }
@@ -230,7 +230,13 @@ export class EditSelect extends Group implements IEditSelect {
230
230
 
231
231
  public isMultipleSelect(e: IPointerEvent): boolean {
232
232
  const { multipleSelect, continuousSelect } = this.editor.mergeConfig
233
- return multipleSelect && (e.shiftKey || continuousSelect)
233
+ return multipleSelect && (this.isHoldMultipleSelectKey(e) || continuousSelect)
234
+ }
235
+
236
+ public isHoldMultipleSelectKey(e: IPointerEvent): boolean {
237
+ const { multipleSelectKey } = this.editor.mergedConfig
238
+ if (multipleSelectKey) return e.isHoldKeys(multipleSelectKey)
239
+ return e.shiftKey
234
240
  }
235
241
 
236
242
  protected __listenEvents(): void {
@@ -1,5 +1,5 @@
1
1
  import { IUI, ILeaferCanvas, IRenderOptions, IRectInputData, IMatrixWithOptionHalfData } from '@leafer-ui/interface'
2
- import { Paint, UI, MatrixHelper, getBoundsData, getMatrixData, BoundsHelper, LeafBoundsHelper } from '@leafer-ui/draw'
2
+ import { Paint, UI, MatrixHelper, getBoundsData, getMatrixData, BoundsHelper, LeafBoundsHelper, isArray, isString } from '@leafer-ui/draw'
3
3
 
4
4
  import { IStroker } from '@leafer-in/interface'
5
5
 
@@ -27,16 +27,17 @@ export class Stroker extends UI implements IStroker {
27
27
  this.strokeAlign = 'center'
28
28
  }
29
29
 
30
- public setTarget(target: IUI | IUI[], style: IRectInputData): void {
31
- this.set(style)
30
+ public setTarget(target: IUI | IUI[], style?: IRectInputData): void {
31
+ if (style) this.set(style)
32
32
  this.target = target
33
33
  this.update()
34
34
  }
35
35
 
36
- public update(): void {
36
+ public update(style?: IRectInputData): void {
37
37
  const { list } = this
38
38
  if (list.length) {
39
39
  setListWithFn(bounds, list, worldBounds)
40
+ if (style) this.set(style)
40
41
  this.set(bounds)
41
42
  this.visible = true
42
43
  } else this.visible = 0
@@ -83,8 +84,8 @@ export class Stroker extends UI implements IStroker {
83
84
 
84
85
  }
85
86
 
86
- if (stroke) typeof stroke === 'string' ? Paint.stroke(stroke, this, canvas) : Paint.strokes(stroke, this, canvas)
87
- if (fill) typeof fill === 'string' ? Paint.fill(fill, this, canvas) : Paint.fills(fill, this, canvas)
87
+ if (stroke) isString(stroke) ? Paint.stroke(stroke, this, canvas) : Paint.strokes(stroke, this, canvas)
88
+ if (fill) isString(fill) ? Paint.fill(fill, this, canvas) : Paint.fills(fill, this, canvas)
88
89
  }
89
90
  }
90
91
 
@@ -101,5 +102,5 @@ export class Stroker extends UI implements IStroker {
101
102
 
102
103
  function onTarget(stroker: Stroker): void {
103
104
  const value = stroker.target
104
- stroker.list = value ? (value instanceof Array ? value : [value]) : []
105
+ stroker.list = value ? (isArray(value) ? value : [value]) : []
105
106
  }