@leafer-in/editor 1.0.0 → 1.0.2

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,4 +1,4 @@
1
- import { PathCommandMap, MatrixHelper, Leaf, Text, Path, Line, Polygon, Group, Box, Event, defineKey, UI, Paint, Rect, Answer, Bounds, LeafList, PointHelper, AroundHelper, Direction9, MathHelper, Matrix, Debug, DataHelper, LeafHelper, RenderEvent, getPointData, Creator } from '@leafer-ui/draw';
1
+ import { PathCommandMap, MatrixHelper, Direction9, Leaf, Text, Path, Line, Polygon, Group, Box, Event, defineKey, UI, Paint, Rect, Answer, Bounds, LeafList, PointHelper, AroundHelper, MathHelper, Matrix, Debug, DataHelper, LeafHelper, RenderEvent, getPointData, Creator } from '@leafer-ui/draw';
2
2
  import { PointerEvent, DragEvent, MoveEvent, ZoomEvent, RotateEvent, KeyEvent } from '@leafer-ui/core';
3
3
 
4
4
  const { M, L, C, Q, Z, N, D, X, G, F, O, P, U } = PathCommandMap;
@@ -81,6 +81,7 @@ const PathScaler = {
81
81
  const { scalePoints } = PathScaler;
82
82
 
83
83
  const matrix$1 = MatrixHelper.get();
84
+ const { topLeft: topLeft$1, top: top$1, topRight: topRight$1, right: right$2, bottom: bottom$1, left: left$2 } = Direction9;
84
85
  function scaleResize(leaf, scaleX, scaleY) {
85
86
  if (leaf.pathInputed) {
86
87
  scaleResizePath(leaf, scaleX, scaleY);
@@ -93,14 +94,35 @@ function scaleResize(leaf, scaleX, scaleY) {
93
94
  }
94
95
  }
95
96
  function scaleResizeFontSize(leaf, scaleX, scaleY) {
96
- const { width, height } = leaf.__localBoxBounds;
97
- if (scaleX !== 1) {
98
- leaf.fontSize *= scaleX;
99
- leaf.y -= height * (scaleX - scaleY) / 2;
97
+ const { app } = leaf;
98
+ const editor = app && app.editor;
99
+ if (editor.editing) {
100
+ const layout = leaf.__layout;
101
+ let { width, height } = layout.boxBounds;
102
+ width *= (scaleY - scaleX) * (leaf.scaleX < 0 ? -1 : 1);
103
+ height *= (scaleX - scaleY) * (leaf.scaleY < 0 ? -1 : 1);
104
+ switch (editor.resizeDirection) {
105
+ case top$1:
106
+ case bottom$1:
107
+ leaf.fontSize *= scaleY;
108
+ layout.affectScaleOrRotation ? leaf.moveInner(-width / 2, 0) : leaf.x -= width / 2;
109
+ break;
110
+ case left$2:
111
+ case right$2:
112
+ leaf.fontSize *= scaleX;
113
+ layout.affectScaleOrRotation ? leaf.moveInner(0, -height / 2) : leaf.y -= height / 2;
114
+ break;
115
+ case topLeft$1:
116
+ case topRight$1:
117
+ leaf.fontSize *= scaleX;
118
+ layout.affectScaleOrRotation ? leaf.moveInner(0, -height) : leaf.y -= height;
119
+ break;
120
+ default:
121
+ leaf.fontSize *= scaleX;
122
+ }
100
123
  }
101
- else if (scaleY !== 1) {
102
- leaf.fontSize *= scaleY;
103
- leaf.x -= width * (scaleY - scaleX) / 2;
124
+ else {
125
+ leaf.fontSize *= scaleX;
104
126
  }
105
127
  }
106
128
  function scaleResizePath(leaf, scaleX, scaleY) {
@@ -433,7 +455,7 @@ class EditSelect extends Group {
433
455
  }
434
456
  onPointerMove(e) {
435
457
  const { app, editor } = this;
436
- if (this.running && !this.isMoveMode && app.config.pointer.hover && !app.interaction.dragging) {
458
+ if (this.running && !this.isMoveMode && app.interaction.canHover && !app.interaction.dragging) {
437
459
  const find = this.findUI(e);
438
460
  editor.hoverTarget = editor.hasItem(find) ? null : find;
439
461
  }
@@ -442,15 +464,27 @@ class EditSelect extends Group {
442
464
  }
443
465
  }
444
466
  onBeforeDown(e) {
467
+ if (e.multiTouch)
468
+ return;
445
469
  const { select } = this.editor.mergeConfig;
446
- if (select === 'press')
447
- this.checkAndSelect(e);
470
+ if (select === 'press') {
471
+ if (this.app.config.mobile) {
472
+ this.waitSelect = () => this.checkAndSelect(e);
473
+ }
474
+ else {
475
+ this.checkAndSelect(e);
476
+ }
477
+ }
448
478
  }
449
479
  onTap(e) {
480
+ if (e.multiTouch)
481
+ return;
450
482
  const { editor } = this;
451
483
  const { select } = editor.mergeConfig;
452
484
  if (select === 'tap')
453
485
  this.checkAndSelect(e);
486
+ else if (this.waitSelect)
487
+ this.waitSelect();
454
488
  if (this.needRemoveItem) {
455
489
  editor.removeItem(this.needRemoveItem);
456
490
  }
@@ -481,6 +515,10 @@ class EditSelect extends Group {
481
515
  }
482
516
  }
483
517
  onDragStart(e) {
518
+ if (e.multiTouch)
519
+ return;
520
+ if (this.waitSelect)
521
+ this.waitSelect();
484
522
  if (this.allowDrag(e)) {
485
523
  const { editor } = this;
486
524
  const { stroke, area } = editor.mergeConfig;
@@ -492,10 +530,10 @@ class EditSelect extends Group {
492
530
  }
493
531
  }
494
532
  onDrag(e) {
495
- if (this.editor.dragging) {
496
- this.onDragEnd();
533
+ if (e.multiTouch)
497
534
  return;
498
- }
535
+ if (this.editor.dragging)
536
+ return this.onDragEnd(e);
499
537
  if (this.dragging) {
500
538
  const { editor } = this;
501
539
  const total = e.getInnerTotal(this);
@@ -519,7 +557,9 @@ class EditSelect extends Group {
519
557
  }
520
558
  }
521
559
  }
522
- onDragEnd() {
560
+ onDragEnd(e) {
561
+ if (e.multiTouch)
562
+ return;
523
563
  if (this.dragging)
524
564
  this.originList = null, this.selectArea.visible = false;
525
565
  }
@@ -565,7 +605,7 @@ class EditSelect extends Group {
565
605
  app.on_(PointerEvent.MOVE, this.onPointerMove, this),
566
606
  app.on_(PointerEvent.BEFORE_DOWN, this.onBeforeDown, this),
567
607
  app.on_(PointerEvent.TAP, this.onTap, this),
568
- app.on_(DragEvent.START, this.onDragStart, this),
608
+ app.on_(DragEvent.START, this.onDragStart, this, true),
569
609
  app.on_(DragEvent.DRAG, this.onDrag, this),
570
610
  app.on_(DragEvent.END, this.onDragEnd, this),
571
611
  app.on_(MoveEvent.MOVE, this.onAutoMove, this),
@@ -588,22 +628,32 @@ class EditSelect extends Group {
588
628
 
589
629
  const { topLeft, top, topRight, right: right$1, bottomRight, bottom, bottomLeft, left: left$1 } = Direction9;
590
630
  const { toPoint } = AroundHelper;
631
+ const { within } = MathHelper;
591
632
  const EditDataHelper = {
592
- getScaleData(bounds, direction, pointMove, lockRatio, around) {
633
+ getScaleData(element, startBounds, direction, totalMove, lockRatio, around, flipable, scaleMode) {
593
634
  let align, origin = {}, scaleX = 1, scaleY = 1;
594
- const { width, height } = bounds;
635
+ const { boxBounds, widthRange, heightRange } = element;
636
+ const { width, height } = startBounds;
595
637
  if (around) {
596
- pointMove.x *= 2;
597
- pointMove.y *= 2;
598
- }
599
- if (Math.abs(pointMove.x) === width)
600
- pointMove.x += 0.1;
601
- if (Math.abs(pointMove.y) === height)
602
- pointMove.y += 0.1;
603
- const topScale = (-pointMove.y + height) / height;
604
- const rightScale = (pointMove.x + width) / width;
605
- const bottomScale = (pointMove.y + height) / height;
606
- const leftScale = (-pointMove.x + width) / width;
638
+ totalMove.x *= 2;
639
+ totalMove.y *= 2;
640
+ }
641
+ const originChangedScaleX = element.scaleX / startBounds.scaleX;
642
+ const originChangedScaleY = element.scaleY / startBounds.scaleY;
643
+ const signX = originChangedScaleX < 0 ? -1 : 1;
644
+ const signY = originChangedScaleY < 0 ? -1 : 1;
645
+ const changedScaleX = scaleMode ? originChangedScaleX : signX * boxBounds.width / width;
646
+ const changedScaleY = scaleMode ? originChangedScaleY : signY * boxBounds.height / height;
647
+ totalMove.x *= scaleMode ? originChangedScaleX : signX;
648
+ totalMove.y *= scaleMode ? originChangedScaleY : signY;
649
+ if (Math.abs(totalMove.x) === width)
650
+ totalMove.x += 0.1;
651
+ if (Math.abs(totalMove.y) === height)
652
+ totalMove.y += 0.1;
653
+ const topScale = (-totalMove.y + height) / height;
654
+ const rightScale = (totalMove.x + width) / width;
655
+ const bottomScale = (totalMove.y + height) / height;
656
+ const leftScale = (-totalMove.x + width) / width;
607
657
  switch (direction) {
608
658
  case top:
609
659
  scaleY = topScale;
@@ -644,12 +694,41 @@ const EditDataHelper = {
644
694
  if (lockRatio) {
645
695
  const unlockSide = lockRatio === 'corner' && direction % 2;
646
696
  if (!unlockSide) {
647
- const scale = Math.sqrt(Math.abs(scaleX * scaleY));
697
+ let scale;
698
+ switch (direction) {
699
+ case top:
700
+ case bottom:
701
+ scale = scaleY;
702
+ break;
703
+ case left$1:
704
+ case right$1:
705
+ scale = scaleX;
706
+ break;
707
+ default:
708
+ scale = Math.sqrt(Math.abs(scaleX * scaleY));
709
+ }
648
710
  scaleX = scaleX < 0 ? -scale : scale;
649
711
  scaleY = scaleY < 0 ? -scale : scale;
650
712
  }
651
713
  }
652
- toPoint(around || align, bounds, origin);
714
+ scaleX /= changedScaleX;
715
+ scaleY /= changedScaleY;
716
+ if (!flipable) {
717
+ const { worldTransform } = element;
718
+ if (scaleX < 0)
719
+ scaleX = 1 / boxBounds.width / worldTransform.scaleX;
720
+ if (scaleY < 0)
721
+ scaleY = 1 / boxBounds.height / worldTransform.scaleY;
722
+ }
723
+ if (widthRange) {
724
+ const nowWidth = boxBounds.width * element.scaleX;
725
+ scaleX = within(nowWidth * scaleX, widthRange) / nowWidth;
726
+ }
727
+ if (heightRange) {
728
+ const nowHeight = boxBounds.height * element.scaleY;
729
+ scaleY = within(nowHeight * scaleY, heightRange) / nowHeight;
730
+ }
731
+ toPoint(around || align, boxBounds, origin);
653
732
  return { origin, scaleX, scaleY, direction, lockRatio, around };
654
733
  },
655
734
  getRotateData(bounds, direction, current, last, around) {
@@ -770,6 +849,11 @@ function updateCursor(editor, e) {
770
849
  return;
771
850
  if (point.name === 'circle')
772
851
  return;
852
+ if (point.pointType === 'button') {
853
+ if (!point.cursor)
854
+ point.cursor = 'pointer';
855
+ return;
856
+ }
773
857
  let { rotation } = editBox;
774
858
  const { resizeCursor, rotateCursor, skewCursor, resizeable, rotateable, skewable } = editor.mergeConfig;
775
859
  const { pointType } = point, { flippedX, flippedY } = editBox;
@@ -838,9 +922,8 @@ class EditBox extends Group {
838
922
  resizePoints.push(resizePoint);
839
923
  this.listenPointEvents(resizePoint, 'resize', i);
840
924
  }
841
- buttons.add(circle);
842
925
  this.listenPointEvents(circle, 'rotate', 2);
843
- view.addMany(...rotatePoints, rect, buttons, ...resizeLines, ...resizePoints);
926
+ view.addMany(...rotatePoints, rect, circle, buttons, ...resizeLines, ...resizePoints);
844
927
  this.add(view);
845
928
  }
846
929
  load() {
@@ -856,9 +939,9 @@ class EditBox extends Group {
856
939
  if (!(i % 2))
857
940
  resizeP.rotation = (i / 2) * 90;
858
941
  }
859
- circle.set(this.getPointStyle(mergeConfig.rotatePoint || pointsStyle[0]));
942
+ circle.set(this.getPointStyle(mergeConfig.circle || mergeConfig.rotatePoint || pointsStyle[0]));
860
943
  rect.set(Object.assign({ stroke, strokeWidth }, (mergeConfig.rect || {})));
861
- rect.hittable = !single && moveable;
944
+ rect.hittable = !single && !!moveable;
862
945
  element.syncEventer = (single && moveable) ? rect : null;
863
946
  this.app.interaction.bottomList = (single && moveable) ? [{ target: rect, proxy: element }] : null;
864
947
  }
@@ -867,7 +950,7 @@ class EditBox extends Group {
867
950
  if (this.view.worldOpacity) {
868
951
  const { mergeConfig } = this.editor;
869
952
  const { width, height } = bounds;
870
- const { rect, circle, resizePoints, rotatePoints, resizeLines } = this;
953
+ const { rect, circle, buttons, resizePoints, rotatePoints, resizeLines } = this;
871
954
  const { middlePoint, resizeable, rotateable, hideOnSmall } = mergeConfig;
872
955
  const smallSize = typeof hideOnSmall === 'number' ? hideOnSmall : 10;
873
956
  const showPoints = !(hideOnSmall && width < smallSize && height < smallSize);
@@ -897,19 +980,25 @@ class EditBox extends Group {
897
980
  }
898
981
  }
899
982
  }
900
- circle.visible = showPoints && rotateable && !!mergeConfig.rotatePoint;
983
+ circle.visible = showPoints && rotateable && !!(mergeConfig.circle || mergeConfig.rotatePoint);
984
+ if (circle.visible)
985
+ this.layoutCircle(mergeConfig);
901
986
  if (rect.path)
902
987
  rect.path = null;
903
988
  rect.set(Object.assign(Object.assign({}, bounds), { visible: true }));
904
- const buttonVisible = showPoints && (circle.visible || this.buttons.children.length > 1);
905
- this.buttons.visible = buttonVisible;
906
- if (buttonVisible)
907
- this.layoutButtons();
989
+ buttons.visible = showPoints && buttons.children.length > 0;
990
+ if (buttons.visible)
991
+ this.layoutButtons(mergeConfig);
908
992
  }
909
993
  }
910
- layoutButtons() {
911
- const { buttons, resizePoints } = this;
912
- const { buttonsDirection, buttonsFixed, buttonsMargin, middlePoint } = this.editor.mergeConfig;
994
+ layoutCircle(config) {
995
+ const { circleDirection, circleMargin, buttonsMargin, buttonsDirection, middlePoint } = config;
996
+ const direction = fourDirection.indexOf(circleDirection || ((this.buttons.children.length && buttonsDirection === 'bottom') ? 'top' : 'bottom'));
997
+ this.setButtonPosition(this.circle, direction, circleMargin || buttonsMargin, !!middlePoint);
998
+ }
999
+ layoutButtons(config) {
1000
+ const { buttons } = this;
1001
+ const { buttonsDirection, buttonsFixed, buttonsMargin, middlePoint } = config;
913
1002
  const { flippedX, flippedY } = this;
914
1003
  let index = fourDirection.indexOf(buttonsDirection);
915
1004
  if ((index % 2 && flippedX) || ((index + 1) % 2 && flippedY)) {
@@ -917,11 +1006,18 @@ class EditBox extends Group {
917
1006
  index = (index + 2) % 4;
918
1007
  }
919
1008
  const direction = buttonsFixed ? EditDataHelper.getRotateDirection(index, this.flippedOne ? this.rotation : -this.rotation, 4) : index;
920
- const point = resizePoints[direction * 2 + 1];
1009
+ this.setButtonPosition(buttons, direction, buttonsMargin, !!middlePoint);
1010
+ if (buttonsFixed)
1011
+ buttons.rotation = (direction - index) * 90;
1012
+ buttons.scaleX = flippedX ? -1 : 1;
1013
+ buttons.scaleY = flippedY ? -1 : 1;
1014
+ }
1015
+ setButtonPosition(buttons, direction, buttonsMargin, useMiddlePoint) {
1016
+ const point = this.resizePoints[direction * 2 + 1];
921
1017
  const useX = direction % 2;
922
1018
  const sign = (!direction || direction === 3) ? -1 : 1;
923
- const useWidth = index % 2;
924
- const margin = (buttonsMargin + (useWidth ? ((middlePoint ? point.width : 0) + buttons.boxBounds.width) : ((middlePoint ? point.height : 0) + buttons.boxBounds.height)) / 2) * sign;
1019
+ const useWidth = direction % 2;
1020
+ const margin = (buttonsMargin + (useWidth ? ((useMiddlePoint ? point.width : 0) + buttons.boxBounds.width) : ((useMiddlePoint ? point.height : 0) + buttons.boxBounds.height)) / 2) * sign;
925
1021
  if (useX) {
926
1022
  buttons.x = point.x + margin;
927
1023
  buttons.y = point.y;
@@ -930,11 +1026,6 @@ class EditBox extends Group {
930
1026
  buttons.x = point.x;
931
1027
  buttons.y = point.y + margin;
932
1028
  }
933
- if (buttonsFixed) {
934
- buttons.rotation = (direction - index) * 90;
935
- buttons.scaleX = flippedX ? -1 : 1;
936
- buttons.scaleY = flippedY ? -1 : 1;
937
- }
938
1029
  }
939
1030
  unload() {
940
1031
  this.visible = false;
@@ -961,18 +1052,23 @@ class EditBox extends Group {
961
1052
  }
962
1053
  onDragStart(e) {
963
1054
  this.dragging = true;
1055
+ const { editor } = this;
964
1056
  if (e.current.name === 'rect') {
965
- const { editor } = this;
966
1057
  this.moving = true;
967
1058
  editor.dragStartPoint = { x: editor.element.x, y: editor.element.y };
968
1059
  editor.opacity = editor.mergeConfig.hideOnMove ? 0 : 1;
969
1060
  }
1061
+ else if (e.current.pointType === 'resize') {
1062
+ editor.dragStartBounds = Object.assign({}, editor.element.getLayoutBounds('box', 'local'));
1063
+ editor.resizeDirection = e.current.direction;
1064
+ }
970
1065
  }
971
1066
  onDragEnd(e) {
972
1067
  this.dragging = false;
973
1068
  this.moving = false;
974
1069
  if (e.current.name === 'rect')
975
1070
  this.editor.opacity = 1;
1071
+ this.editor.resizeDirection = undefined;
976
1072
  }
977
1073
  onDrag(e) {
978
1074
  const { editor } = this;
@@ -981,7 +1077,7 @@ class EditBox extends Group {
981
1077
  if (editor.mergeConfig.rotateable)
982
1078
  editor.onRotate(e);
983
1079
  }
984
- else {
1080
+ else if (point.pointType === 'resize') {
985
1081
  editor.onScale(e);
986
1082
  }
987
1083
  updateCursor(editor, e);
@@ -1045,8 +1141,6 @@ class EditBox extends Group {
1045
1141
  rect.on_(DragEvent.START, this.onDragStart, this),
1046
1142
  rect.on_(DragEvent.DRAG, editor.onMove, editor),
1047
1143
  rect.on_(DragEvent.END, this.onDragEnd, this),
1048
- rect.on_(ZoomEvent.BEFORE_ZOOM, editor.onScale, editor, true),
1049
- rect.on_(RotateEvent.BEFORE_ROTATE, editor.onRotate, editor, true),
1050
1144
  rect.on_(PointerEvent.ENTER, () => updateMoveCursor(editor)),
1051
1145
  rect.on_(PointerEvent.DOUBLE_TAP, this.onDoubleTap, this),
1052
1146
  rect.on_(PointerEvent.LONG_PRESS, this.onLongPress, this)
@@ -1076,7 +1170,7 @@ class EditMask extends UI {
1076
1170
  const { rect } = editor.editBox;
1077
1171
  const { width, height } = rect.__;
1078
1172
  canvas.resetTransform();
1079
- canvas.fillWorld(canvas.bounds, mask);
1173
+ canvas.fillWorld(canvas.bounds, mask === true ? 'rgba(0,0,0,0.8)' : mask);
1080
1174
  canvas.setWorld(rect.__world, options.matrix);
1081
1175
  canvas.clearRect(0, 0, width, height);
1082
1176
  }
@@ -1161,6 +1255,7 @@ const config = {
1161
1255
  boxSelect: true,
1162
1256
  moveable: true,
1163
1257
  resizeable: true,
1258
+ flipable: true,
1164
1259
  rotateable: true,
1165
1260
  skewable: true
1166
1261
  };
@@ -1318,7 +1413,7 @@ class Editor extends Group {
1318
1413
  get buttons() { return this.editBox.buttons; }
1319
1414
  constructor(userConfig, data) {
1320
1415
  super(data);
1321
- this.config = config;
1416
+ this.config = DataHelper.clone(config);
1322
1417
  this.leafList = new LeafList();
1323
1418
  this.openedGroupList = new LeafList();
1324
1419
  this.simulateTarget = new Rect({ visible: false });
@@ -1382,29 +1477,39 @@ class Editor extends Group {
1382
1477
  return this.mergeConfig.editSize;
1383
1478
  }
1384
1479
  onMove(e) {
1385
- const total = { x: e.totalX, y: e.totalY };
1386
- if (e.shiftKey) {
1387
- if (Math.abs(total.x) > Math.abs(total.y))
1388
- total.y = 0;
1389
- else
1390
- total.x = 0;
1480
+ if (e instanceof MoveEvent) {
1481
+ if (e.moveType !== 'drag') {
1482
+ const { moveable, resizeable } = this.mergeConfig;
1483
+ const move = e.getLocalMove(this.element);
1484
+ if (moveable === 'move')
1485
+ e.stop(), this.move(move.x, move.y);
1486
+ else if (resizeable === 'zoom')
1487
+ e.stop();
1488
+ }
1489
+ }
1490
+ else {
1491
+ const total = { x: e.totalX, y: e.totalY };
1492
+ if (e.shiftKey) {
1493
+ if (Math.abs(total.x) > Math.abs(total.y))
1494
+ total.y = 0;
1495
+ else
1496
+ total.x = 0;
1497
+ }
1498
+ this.move(DragEvent.getValidMove(this.element, this.dragStartPoint, total));
1391
1499
  }
1392
- this.move(DragEvent.getValidMove(this.element, this.dragStartPoint, total));
1393
1500
  }
1394
1501
  onScale(e) {
1395
1502
  const { element } = this;
1503
+ let { around, lockRatio, resizeable, flipable, editSize } = this.mergeConfig;
1396
1504
  if (e instanceof ZoomEvent) {
1397
- if (this.mergeConfig.resizeable === 'zoom') {
1398
- e.stop();
1399
- this.scaleOf(element.getInnerPoint(e), e.scale, e.scale);
1400
- }
1505
+ if (resizeable === 'zoom')
1506
+ e.stop(), this.scaleOf(element.getInnerPoint(e), e.scale, e.scale);
1401
1507
  }
1402
1508
  else {
1403
1509
  const { direction } = e.current;
1404
- let { around, lockRatio } = this.mergeConfig;
1405
1510
  if (e.shiftKey || element.lockRatio)
1406
1511
  lockRatio = true;
1407
- const data = EditDataHelper.getScaleData(element.boxBounds, direction, e.getInnerMove(element), lockRatio, EditDataHelper.getAround(around, e.altKey));
1512
+ const data = EditDataHelper.getScaleData(element, this.dragStartBounds, direction, e.getInnerTotal(element), lockRatio, EditDataHelper.getAround(around, e.altKey), flipable, this.multiple || editSize === 'scale');
1408
1513
  if (this.editTool.onScaleWithDrag) {
1409
1514
  data.drag = e;
1410
1515
  this.scaleWithDrag(data);
@@ -1415,23 +1520,21 @@ class Editor extends Group {
1415
1520
  }
1416
1521
  }
1417
1522
  onRotate(e) {
1418
- const { skewable, around, rotateGap } = this.mergeConfig;
1523
+ const { skewable, rotateable, around, rotateGap } = this.mergeConfig;
1419
1524
  const { direction, name } = e.current;
1420
1525
  if (skewable && name === 'resize-line')
1421
1526
  return this.onSkew(e);
1422
1527
  const { element } = this;
1423
1528
  let origin, rotation;
1424
1529
  if (e instanceof RotateEvent) {
1425
- if (this.mergeConfig.rotateable === 'rotate') {
1426
- e.stop();
1427
- rotation = e.rotation, origin = element.getInnerPoint(e);
1428
- }
1530
+ if (rotateable === 'rotate')
1531
+ e.stop(), rotation = e.rotation, origin = element.getInnerPoint(e);
1429
1532
  else
1430
1533
  return;
1431
1534
  }
1432
1535
  else {
1433
1536
  const last = { x: e.x - e.moveX, y: e.y - e.moveY };
1434
- const data = EditDataHelper.getRotateData(element.boxBounds, direction, e.getInner(element), element.getInnerPoint(last), e.shiftKey ? null : (around || 'center'));
1537
+ const data = EditDataHelper.getRotateData(element.boxBounds, direction, e.getInner(element), element.getInnerPoint(last), e.shiftKey ? null : (element.around || element.origin || around || 'center'));
1435
1538
  rotation = data.rotation;
1436
1539
  origin = data.origin;
1437
1540
  }
@@ -1465,8 +1568,7 @@ class Editor extends Group {
1465
1568
  if (!this.mergeConfig.resizeable || this.element.locked)
1466
1569
  return;
1467
1570
  const { element } = this;
1468
- const worldOrigin = element.getWorldPoint(data.origin);
1469
- const event = new EditorScaleEvent(EditorScaleEvent.SCALE, Object.assign(Object.assign({}, data), { target: element, editor: this, worldOrigin }));
1571
+ const event = new EditorScaleEvent(EditorScaleEvent.SCALE, Object.assign(Object.assign({}, data), { target: element, editor: this, worldOrigin: element.getWorldPoint(data.origin) }));
1470
1572
  this.editTool.onScaleWithDrag(event);
1471
1573
  this.emitEvent(event);
1472
1574
  }
@@ -1474,28 +1576,28 @@ class Editor extends Group {
1474
1576
  if (!this.mergeConfig.resizeable || this.element.locked)
1475
1577
  return;
1476
1578
  const { element } = this;
1477
- const worldOrigin = element.getWorldPoint(LeafHelper.getInnerOrigin(element, origin));
1478
- let transform;
1479
- if (this.multiple) {
1480
- const oldMatrix = new Matrix(element.worldTransform);
1481
- element.scaleOf(origin, scaleX, scaleY);
1482
- transform = new Matrix(element.worldTransform).divide(oldMatrix);
1483
- }
1579
+ const worldOrigin = this.getWorldOrigin(origin);
1580
+ const transform = this.multiple && this.getChangedTransform(() => element.scaleOf(origin, scaleX, scaleY));
1484
1581
  const event = new EditorScaleEvent(EditorScaleEvent.SCALE, { target: element, editor: this, worldOrigin, scaleX, scaleY, transform });
1485
1582
  this.editTool.onScale(event);
1486
1583
  this.emitEvent(event);
1487
1584
  }
1585
+ flip(axis) {
1586
+ if (this.element.locked)
1587
+ return;
1588
+ const { element } = this;
1589
+ const worldOrigin = this.getWorldOrigin('center');
1590
+ const transform = this.multiple ? this.getChangedTransform(() => element.flip(axis)) : new Matrix(LeafHelper.getFlipTransform(element, axis));
1591
+ const event = new EditorScaleEvent(EditorScaleEvent.SCALE, { target: element, editor: this, worldOrigin, scaleX: axis === 'x' ? -1 : 1, scaleY: axis === 'y' ? -1 : 1, transform });
1592
+ this.editTool.onScale(event);
1593
+ this.emitEvent(event);
1594
+ }
1488
1595
  rotateOf(origin, rotation) {
1489
1596
  if (!this.mergeConfig.rotateable || this.element.locked)
1490
1597
  return;
1491
1598
  const { element } = this;
1492
- const worldOrigin = element.getWorldPoint(LeafHelper.getInnerOrigin(element, origin));
1493
- let transform;
1494
- if (this.multiple) {
1495
- const oldMatrix = new Matrix(element.worldTransform);
1496
- element.rotateOf(origin, rotation);
1497
- transform = new Matrix(element.worldTransform).divide(oldMatrix);
1498
- }
1599
+ const worldOrigin = this.getWorldOrigin(origin);
1600
+ const transform = this.multiple && this.getChangedTransform(() => element.rotateOf(origin, rotation));
1499
1601
  const event = new EditorRotateEvent(EditorRotateEvent.ROTATE, { target: element, editor: this, worldOrigin, rotation, transform });
1500
1602
  this.editTool.onRotate(event);
1501
1603
  this.emitEvent(event);
@@ -1504,19 +1606,21 @@ class Editor extends Group {
1504
1606
  if (!this.mergeConfig.skewable || this.element.locked)
1505
1607
  return;
1506
1608
  const { element } = this;
1507
- const worldOrigin = element.getWorldPoint(LeafHelper.getInnerOrigin(element, origin));
1508
- let transform;
1509
- if (this.multiple) {
1510
- const oldMatrix = new Matrix(element.worldTransform);
1511
- element.skewOf(origin, skewX, skewY);
1512
- transform = new Matrix(element.worldTransform).divide(oldMatrix);
1513
- }
1514
- const event = new EditorSkewEvent(EditorSkewEvent.SKEW, {
1515
- target: element, editor: this, skewX, skewY, transform, worldOrigin
1516
- });
1609
+ const worldOrigin = this.getWorldOrigin(origin);
1610
+ const transform = this.multiple && this.getChangedTransform(() => element.skewOf(origin, skewX, skewY));
1611
+ const event = new EditorSkewEvent(EditorSkewEvent.SKEW, { target: element, editor: this, worldOrigin, skewX, skewY, transform });
1517
1612
  this.editTool.onSkew(event);
1518
1613
  this.emitEvent(event);
1519
1614
  }
1615
+ getWorldOrigin(origin) {
1616
+ return this.element.getWorldPoint(LeafHelper.getInnerOrigin(this.element, origin));
1617
+ }
1618
+ getChangedTransform(func) {
1619
+ const { element } = this;
1620
+ const oldMatrix = new Matrix(element.worldTransform);
1621
+ func();
1622
+ return new Matrix(element.worldTransform).divide(oldMatrix);
1623
+ }
1520
1624
  group(userGroup) {
1521
1625
  if (this.multiple) {
1522
1626
  this.target = EditorHelper.group(this.list, this.element, userGroup);
@@ -1627,6 +1731,9 @@ class Editor extends Group {
1627
1731
  if (!this.targetEventIds.length) {
1628
1732
  const { leafer } = this.list[0];
1629
1733
  this.targetEventIds = [
1734
+ this.app.on_(MoveEvent.BEFORE_MOVE, this.onMove, this, true),
1735
+ this.app.on_(ZoomEvent.BEFORE_ZOOM, this.onScale, this, true),
1736
+ this.app.on_(RotateEvent.BEFORE_ROTATE, this.onRotate, this, true),
1630
1737
  leafer.on_(RenderEvent.START, this.update, this),
1631
1738
  leafer.on_([KeyEvent.HOLD, KeyEvent.UP], (e) => { updateCursor(this, e); }),
1632
1739
  leafer.on_(KeyEvent.DOWN, this.editBox.onArrow, this.editBox)