@visactor/vrender-core 1.0.9 → 1.0.11-alpha.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.
Files changed (60) hide show
  1. package/cjs/common/event-listener-manager.d.ts +18 -1
  2. package/cjs/common/event-listener-manager.js +40 -10
  3. package/cjs/common/event-listener-manager.js.map +1 -1
  4. package/cjs/core/stage.d.ts +4 -2
  5. package/cjs/core/stage.js +12 -6
  6. package/cjs/core/stage.js.map +1 -1
  7. package/cjs/event/event-manager.js +4 -1
  8. package/cjs/event/event-manager.js.map +1 -1
  9. package/cjs/graphic/config.js +4 -2
  10. package/cjs/graphic/config.js.map +1 -1
  11. package/cjs/graphic/glyph.js +1 -6
  12. package/cjs/graphic/glyph.js.map +1 -1
  13. package/cjs/graphic/graphic.js +4 -3
  14. package/cjs/graphic/graphic.js.map +1 -1
  15. package/cjs/interface/animation/animate.d.ts +1 -0
  16. package/cjs/interface/animation/animate.js.map +1 -1
  17. package/cjs/interface/graphic/rect.d.ts +1 -0
  18. package/cjs/interface/graphic/rect.js.map +1 -1
  19. package/cjs/interface/stage.d.ts +2 -0
  20. package/cjs/interface/stage.js.map +1 -1
  21. package/cjs/plugins/builtin-plugin/edit-module.js +4 -4
  22. package/cjs/plugins/builtin-plugin/edit-module.js.map +1 -1
  23. package/cjs/render/contributions/render/rect-render.js +2 -2
  24. package/cjs/render/contributions/render/rect-render.js.map +1 -1
  25. package/cjs/render/contributions/render/rect3d-render.js +4 -4
  26. package/cjs/render/contributions/render/rect3d-render.js.map +1 -1
  27. package/cjs/render/contributions/render/utils.d.ts +1 -1
  28. package/cjs/render/contributions/render/utils.js +2 -2
  29. package/cjs/render/contributions/render/utils.js.map +1 -1
  30. package/dist/index.es.js +107 -39
  31. package/es/common/event-listener-manager.d.ts +18 -1
  32. package/es/common/event-listener-manager.js +40 -10
  33. package/es/common/event-listener-manager.js.map +1 -1
  34. package/es/core/stage.d.ts +4 -2
  35. package/es/core/stage.js +12 -6
  36. package/es/core/stage.js.map +1 -1
  37. package/es/event/event-manager.js +4 -1
  38. package/es/event/event-manager.js.map +1 -1
  39. package/es/graphic/config.js +4 -2
  40. package/es/graphic/config.js.map +1 -1
  41. package/es/graphic/glyph.js +1 -6
  42. package/es/graphic/glyph.js.map +1 -1
  43. package/es/graphic/graphic.js +4 -3
  44. package/es/graphic/graphic.js.map +1 -1
  45. package/es/interface/animation/animate.d.ts +1 -0
  46. package/es/interface/animation/animate.js.map +1 -1
  47. package/es/interface/graphic/rect.d.ts +1 -0
  48. package/es/interface/graphic/rect.js.map +1 -1
  49. package/es/interface/stage.d.ts +2 -0
  50. package/es/interface/stage.js.map +1 -1
  51. package/es/plugins/builtin-plugin/edit-module.js +4 -4
  52. package/es/plugins/builtin-plugin/edit-module.js.map +1 -1
  53. package/es/render/contributions/render/rect-render.js +2 -2
  54. package/es/render/contributions/render/rect-render.js.map +1 -1
  55. package/es/render/contributions/render/rect3d-render.js +3 -3
  56. package/es/render/contributions/render/rect3d-render.js.map +1 -1
  57. package/es/render/contributions/render/utils.d.ts +1 -1
  58. package/es/render/contributions/render/utils.js +2 -2
  59. package/es/render/contributions/render/utils.js.map +1 -1
  60. package/package.json +1 -1
package/dist/index.es.js CHANGED
@@ -924,6 +924,13 @@ class EventListenerManager {
924
924
  if (!listener) {
925
925
  return;
926
926
  }
927
+ const capture = this._resolveCapture(options);
928
+ const once = this._resolveOnce(options);
929
+ const listenerTypeMap = this._getOrCreateListenerTypeMap(type);
930
+ const wrappedMap = this._getOrCreateWrappedMap(listenerTypeMap, listener);
931
+ if (wrappedMap.has(capture)) {
932
+ return;
933
+ }
927
934
  const wrappedListener = (event) => {
928
935
  const transformedEvent = this._eventListenerTransformer(event);
929
936
  if (typeof listener === 'function') {
@@ -932,38 +939,80 @@ class EventListenerManager {
932
939
  else if (listener.handleEvent) {
933
940
  listener.handleEvent(transformedEvent);
934
941
  }
942
+ if (once) {
943
+ this._deleteListenerRecord(type, listener, capture);
944
+ }
935
945
  };
936
- if (!this._listenerMap.has(type)) {
937
- this._listenerMap.set(type, new Map());
938
- }
939
- this._listenerMap.get(type).set(listener, wrappedListener);
946
+ wrappedMap.set(capture, { wrappedListener, options });
940
947
  this._nativeAddEventListener(type, wrappedListener, options);
941
948
  }
942
949
  removeEventListener(type, listener, options) {
943
- var _a;
950
+ var _a, _b;
944
951
  if (!listener) {
945
952
  return;
946
953
  }
947
- const wrappedListener = (_a = this._listenerMap.get(type)) === null || _a === void 0 ? void 0 : _a.get(listener);
948
- if (wrappedListener) {
949
- this._nativeRemoveEventListener(type, wrappedListener, options);
950
- this._listenerMap.get(type).delete(listener);
951
- if (this._listenerMap.get(type).size === 0) {
952
- this._listenerMap.delete(type);
953
- }
954
+ const capture = this._resolveCapture(options);
955
+ const wrappedRecord = (_b = (_a = this._listenerMap.get(type)) === null || _a === void 0 ? void 0 : _a.get(listener)) === null || _b === void 0 ? void 0 : _b.get(capture);
956
+ if (wrappedRecord) {
957
+ this._nativeRemoveEventListener(type, wrappedRecord.wrappedListener, capture);
958
+ this._deleteListenerRecord(type, listener, capture);
954
959
  }
955
960
  }
956
961
  dispatchEvent(event) {
957
962
  return this._nativeDispatchEvent(event);
958
963
  }
959
964
  clearAllEventListeners() {
960
- this._listenerMap.forEach((listenersMap, type) => {
961
- listenersMap.forEach((wrappedListener, originalListener) => {
962
- this._nativeRemoveEventListener(type, wrappedListener, undefined);
965
+ this._listenerMap.forEach((listenerMap, type) => {
966
+ listenerMap.forEach(wrappedMap => {
967
+ wrappedMap.forEach((wrappedRecord, capture) => {
968
+ this._nativeRemoveEventListener(type, wrappedRecord.wrappedListener, capture);
969
+ });
963
970
  });
964
971
  });
965
972
  this._listenerMap.clear();
966
973
  }
974
+ _resolveCapture(options) {
975
+ if (typeof options === 'boolean') {
976
+ return options;
977
+ }
978
+ return !!(options === null || options === void 0 ? void 0 : options.capture);
979
+ }
980
+ _resolveOnce(options) {
981
+ return typeof options === 'object' && !!(options === null || options === void 0 ? void 0 : options.once);
982
+ }
983
+ _getOrCreateListenerTypeMap(type) {
984
+ let listenerTypeMap = this._listenerMap.get(type);
985
+ if (!listenerTypeMap) {
986
+ listenerTypeMap = new Map();
987
+ this._listenerMap.set(type, listenerTypeMap);
988
+ }
989
+ return listenerTypeMap;
990
+ }
991
+ _getOrCreateWrappedMap(listenerTypeMap, listener) {
992
+ let wrappedMap = listenerTypeMap.get(listener);
993
+ if (!wrappedMap) {
994
+ wrappedMap = new Map();
995
+ listenerTypeMap.set(listener, wrappedMap);
996
+ }
997
+ return wrappedMap;
998
+ }
999
+ _deleteListenerRecord(type, listener, capture) {
1000
+ const listenerTypeMap = this._listenerMap.get(type);
1001
+ if (!listenerTypeMap) {
1002
+ return;
1003
+ }
1004
+ const wrappedMap = listenerTypeMap.get(listener);
1005
+ if (!wrappedMap) {
1006
+ return;
1007
+ }
1008
+ wrappedMap.delete(capture);
1009
+ if (wrappedMap.size === 0) {
1010
+ listenerTypeMap.delete(listener);
1011
+ }
1012
+ if (listenerTypeMap.size === 0) {
1013
+ this._listenerMap.delete(type);
1014
+ }
1015
+ }
967
1016
  _nativeAddEventListener(type, listener, options) {
968
1017
  throw new Error('_nativeAddEventListener must be implemented by derived classes');
969
1018
  }
@@ -3533,8 +3582,8 @@ const DefaultPathAttribute = Object.assign(Object.assign({}, DefaultAttribute),
3533
3582
  } });
3534
3583
  const DefaultPolygonAttribute = Object.assign(Object.assign({}, DefaultAttribute), { points: [], cornerRadius: 0, closePath: true });
3535
3584
  const DefaultStarAttribute = Object.assign(Object.assign({}, DefaultAttribute), { width: 100, height: 100, spikes: 5, thickness: 0.5 });
3536
- const DefaultRectAttribute = Object.assign(Object.assign({}, DefaultAttribute), { width: 0, height: 0, x1: 0, y1: 0, strokeBoundsBuffer: 0, cornerRadius: 0, cornerType: 'round' });
3537
- const DefaultRect3dAttribute = Object.assign(Object.assign({}, DefaultAttribute), { width: 0, height: 0, x1: 0, y1: 0, cornerRadius: 0, length: 0, cornerType: 'round' });
3585
+ const DefaultRectAttribute = Object.assign(Object.assign({}, DefaultAttribute), { width: 0, height: 0, x1: 0, y1: 0, strokeBoundsBuffer: 0, cornerRadius: 0, cornerType: 'round', drawStrokeWhenZeroWH: false });
3586
+ const DefaultRect3dAttribute = Object.assign(Object.assign({}, DefaultAttribute), { width: 0, height: 0, x1: 0, y1: 0, cornerRadius: 0, length: 0, cornerType: 'round', drawStrokeWhenZeroWH: false });
3538
3587
  const DefaultSymbolAttribute = Object.assign(Object.assign({}, DefaultAttribute), { symbolType: 'circle', size: 10, keepDirIn3d: true, clipRange: 1 });
3539
3588
  const DefaultTextAttribute = Object.assign(Object.assign(Object.assign({}, DefaultAttribute), DefaultTextStyle), { strokeBoundsBuffer: 0, keepDirIn3d: true });
3540
3589
  const DefaultRichTextAttribute = Object.assign(Object.assign(Object.assign({}, DefaultAttribute), DefaultTextStyle), { upgradeAttrs: null, editable: false, editOptions: null, ascentDescentMode: 'actual', width: 300, height: 300, ellipsis: true, wordBreak: 'break-word', verticalDirection: 'top', textAlign: 'left', textBaseline: 'top', layoutDirection: 'horizontal', textConfig: [], disableAutoWrapLine: false, maxHeight: undefined, maxWidth: undefined, singleLine: false });
@@ -7353,6 +7402,13 @@ class EventManager {
7353
7402
  }
7354
7403
  const constructor = event.constructor;
7355
7404
  if (!this.eventPool.has(constructor)) {
7405
+ this.eventPool.get(constructor).forEach(e => {
7406
+ e.eventPhase = event.NONE;
7407
+ e.currentTarget = null;
7408
+ e.path = [];
7409
+ e.detailPath = [];
7410
+ e.target = null;
7411
+ });
7356
7412
  this.eventPool.set(constructor, []);
7357
7413
  }
7358
7414
  (_a = this.eventPool.get(constructor)) === null || _a === void 0 ? void 0 : _a.push(event);
@@ -11300,6 +11356,9 @@ class Graphic extends Node {
11300
11356
  });
11301
11357
  }
11302
11358
  setAttributes(params, forceUpdateTag = false, context) {
11359
+ if (!params) {
11360
+ return;
11361
+ }
11303
11362
  params =
11304
11363
  (this.onBeforeAttributeUpdate && this.onBeforeAttributeUpdate(params, this.attribute, null, context)) || params;
11305
11364
  if (params.background) {
@@ -12022,6 +12081,7 @@ class Graphic extends Node {
12022
12081
  this.releaseStatus = 'released';
12023
12082
  this.stopAnimates();
12024
12083
  application.graphicService.onRelease(this);
12084
+ super.release();
12025
12085
  }
12026
12086
  _emitCustomEvent(type, context) {
12027
12087
  var _a, _b;
@@ -12617,7 +12677,10 @@ function rectFillVisible(opacity, fillOpacity, width, height, fill) {
12617
12677
  function strokeVisible(opacity, strokeOpacity) {
12618
12678
  return opacity * strokeOpacity > 0;
12619
12679
  }
12620
- function rectStrokeVisible(opacity, strokeOpacity, width, height) {
12680
+ function rectStrokeVisible(opacity, strokeOpacity, width, height, drawStrokeWhenZeroWH) {
12681
+ if (drawStrokeWhenZeroWH) {
12682
+ return opacity * strokeOpacity > 0;
12683
+ }
12621
12684
  return opacity * strokeOpacity > 0 && width !== 0 && height !== 0;
12622
12685
  }
12623
12686
  function intersect(x0, y0, x1, y1, x2, y2, x3, y3) {
@@ -14611,10 +14674,6 @@ class Glyph extends Graphic {
14611
14674
  }
14612
14675
  }
14613
14676
  });
14614
- this.subGraphic.forEach((graphic, index) => {
14615
- graphic.updateNormalAttrs(subAttrs[index]);
14616
- graphic.applyStateAttrs(subAttrs[index], states, hasAnimation);
14617
- });
14618
14677
  this.updateNormalAttrs(stateAttrs);
14619
14678
  this.currentStates = states;
14620
14679
  this.applyStateAttrs(stateAttrs, states, hasAnimation);
@@ -14623,10 +14682,6 @@ class Glyph extends Graphic {
14623
14682
  this.stopStateAnimates();
14624
14683
  if (this.hasState() && this.normalAttrs) {
14625
14684
  this.currentStates = [];
14626
- this.subGraphic.forEach(graphic => {
14627
- graphic.applyStateAttrs(graphic.normalAttrs, this.currentStates, hasAnimation, true);
14628
- graphic.normalAttrs = null;
14629
- });
14630
14685
  this.applyStateAttrs(this.normalAttrs, this.currentStates, hasAnimation, true);
14631
14686
  }
14632
14687
  else {
@@ -19585,12 +19640,12 @@ let DefaultCanvasRectRender = class DefaultCanvasRectRender extends BaseRender {
19585
19640
  }
19586
19641
  drawShape(rect, context, x, y, drawContext, params, fillCb, strokeCb, rectAttribute) {
19587
19642
  rectAttribute = rectAttribute !== null && rectAttribute !== void 0 ? rectAttribute : getTheme(rect, params === null || params === void 0 ? void 0 : params.theme).rect;
19588
- const { fill = rectAttribute.fill, background, stroke = rectAttribute.stroke, cornerRadius = rectAttribute.cornerRadius, cornerType = rectAttribute.cornerType, opacity = rectAttribute.opacity, fillOpacity = rectAttribute.fillOpacity, lineWidth = rectAttribute.lineWidth, strokeOpacity = rectAttribute.strokeOpacity, visible = rectAttribute.visible, x1, y1, x: originX = rectAttribute.x, y: originY = rectAttribute.y, fillStrokeOrder = rectAttribute.fillStrokeOrder } = rect.attribute;
19643
+ const { fill = rectAttribute.fill, background, stroke = rectAttribute.stroke, cornerRadius = rectAttribute.cornerRadius, cornerType = rectAttribute.cornerType, opacity = rectAttribute.opacity, fillOpacity = rectAttribute.fillOpacity, lineWidth = rectAttribute.lineWidth, strokeOpacity = rectAttribute.strokeOpacity, visible = rectAttribute.visible, x1, y1, x: originX = rectAttribute.x, y: originY = rectAttribute.y, fillStrokeOrder = rectAttribute.fillStrokeOrder, drawStrokeWhenZeroWH = rectAttribute.drawStrokeWhenZeroWH } = rect.attribute;
19589
19644
  let { width, height } = rect.attribute;
19590
19645
  width = (width !== null && width !== void 0 ? width : x1 - originX) || 0;
19591
19646
  height = (height !== null && height !== void 0 ? height : y1 - originY) || 0;
19592
19647
  const fVisible = rectFillVisible(opacity, fillOpacity, width, height, fill);
19593
- const sVisible = rectStrokeVisible(opacity, strokeOpacity, width, height);
19648
+ const sVisible = rectStrokeVisible(opacity, strokeOpacity, width, height, drawStrokeWhenZeroWH);
19594
19649
  const doFill = runFill(fill, background);
19595
19650
  const doStroke = runStroke(stroke, lineWidth);
19596
19651
  if (!(rect.valid && visible)) {
@@ -22676,7 +22731,7 @@ class Stage extends Group {
22676
22731
  }
22677
22732
  };
22678
22733
  this.beforeRender = (stage) => {
22679
- this._beforeRender && this._beforeRender(stage);
22734
+ this._beforeRenderList.forEach(cb => cb(stage));
22680
22735
  };
22681
22736
  this.afterClearScreen = (drawParams) => {
22682
22737
  this._afterClearScreen && this._afterClearScreen(drawParams);
@@ -22686,7 +22741,7 @@ class Stage extends Group {
22686
22741
  };
22687
22742
  this.afterRender = (stage) => {
22688
22743
  this.renderCount++;
22689
- this._afterRender && this._afterRender(stage);
22744
+ this._afterRenderList.forEach(cb => cb(stage));
22690
22745
  this._afterNextRenderCbs && this._afterNextRenderCbs.forEach(cb => cb(stage));
22691
22746
  this._afterNextRenderCbs = null;
22692
22747
  this.tickedBeforeRender = false;
@@ -22713,6 +22768,8 @@ class Stage extends Group {
22713
22768
  this.layerService = container.get(LayerService);
22714
22769
  this.graphicService = container.get(GraphicService);
22715
22770
  this.pluginService.active(this, params);
22771
+ this._beforeRenderList = [];
22772
+ this._afterRenderList = [];
22716
22773
  this.window.create({
22717
22774
  width: params.width,
22718
22775
  height: params.height,
@@ -22750,10 +22807,14 @@ class Stage extends Group {
22750
22807
  params.enableLayout && this.enableLayout();
22751
22808
  this.hooks.beforeRender.tap('constructor', this.beforeRender);
22752
22809
  this.hooks.afterRender.tap('constructor', this.afterRender);
22810
+ if (params.beforeRender) {
22811
+ this._beforeRenderList.push(params.beforeRender);
22812
+ }
22813
+ if (params.afterRender) {
22814
+ this._afterRenderList.push(params.afterRender);
22815
+ }
22753
22816
  this.hooks.afterClearScreen.tap('constructor', this.afterClearScreen);
22754
22817
  this.hooks.afterClearRect.tap('constructor', this.afterClearRect);
22755
- this._beforeRender = params.beforeRender;
22756
- this._afterRender = params.afterRender;
22757
22818
  this._afterClearScreen = params.afterClearScreen;
22758
22819
  this._afterClearRect = params.afterClearRect;
22759
22820
  this.supportInteractiveLayer = params.interactiveLayer !== false;
@@ -22886,10 +22947,16 @@ class Stage extends Group {
22886
22947
  }
22887
22948
  }
22888
22949
  setBeforeRender(cb) {
22889
- this._beforeRender = cb;
22950
+ this._beforeRenderList.push(cb);
22951
+ }
22952
+ removeBeforeRender(cb) {
22953
+ this._beforeRenderList = this._beforeRenderList.filter(c => c !== cb);
22890
22954
  }
22891
22955
  setAfterRender(cb) {
22892
- this._afterRender = cb;
22956
+ this._afterRenderList.push(cb);
22957
+ }
22958
+ removeAfterRender(cb) {
22959
+ this._afterRenderList = this._afterRenderList.filter(c => c !== cb);
22893
22960
  }
22894
22961
  afterNextRender(cb) {
22895
22962
  if (!this._afterNextRenderCbs) {
@@ -24928,6 +24995,7 @@ class EditModule {
24928
24995
  this.container = container !== null && container !== void 0 ? container : document.body;
24929
24996
  const textAreaDom = document.createElement('textarea');
24930
24997
  textAreaDom.autocomplete = 'off';
24998
+ textAreaDom.spellcheck = false;
24931
24999
  textAreaDom.innerText = '';
24932
25000
  this.applyStyle(textAreaDom);
24933
25001
  this.container.append(textAreaDom);
@@ -24952,7 +25020,7 @@ class EditModule {
24952
25020
  this.onFocusOutList.push(cb);
24953
25021
  }
24954
25022
  applyStyle(textAreaDom) {
24955
- textAreaDom.setAttribute('style', `width: 100px; height: 30px; left: 0; top: 0; position: absolute; z-index: -1; outline: none; resize: none; border: none; overflow: hidden; color: transparent; user-select: none; caret-color: transparent;background-color: transparent;`);
25023
+ textAreaDom.setAttribute('style', `width: 100px; height: 30px; left: 0; top: 0; position: absolute; z-index: -1; outline: none; resize: none; border: none; overflow: hidden; color: transparent; user-select: none; caret-color: transparent;background-color: transparent;opacity: 0;pointer-events: none;`);
24956
25024
  textAreaDom.addEventListener('input', this.handleInput);
24957
25025
  textAreaDom.addEventListener('compositionstart', this.handleCompositionStart);
24958
25026
  textAreaDom.addEventListener('compositionend', this.handleCompositionEnd);
@@ -26700,15 +26768,15 @@ let DefaultCanvasRect3dRender = class DefaultCanvasRect3dRender extends Base3dRe
26700
26768
  this.numberType = RECT3D_NUMBER_TYPE;
26701
26769
  }
26702
26770
  drawShape(rect, context, x, y, drawContext, params, fillCb, strokeCb) {
26703
- var _a;
26771
+ var _a, _b;
26704
26772
  const rectAttribute = getTheme(rect, params === null || params === void 0 ? void 0 : params.theme).rect;
26705
- const { fill = rectAttribute.fill, stroke = rectAttribute.stroke, x1, y1, x: originX, y: originY, opacity = rectAttribute.opacity, fillOpacity = rectAttribute.fillOpacity, lineWidth = rectAttribute.lineWidth, strokeOpacity = rectAttribute.strokeOpacity, visible = rectAttribute.visible } = rect.attribute;
26773
+ const { fill = rectAttribute.fill, stroke = rectAttribute.stroke, x1, y1, x: originX, y: originY, opacity = rectAttribute.opacity, fillOpacity = rectAttribute.fillOpacity, lineWidth = rectAttribute.lineWidth, strokeOpacity = rectAttribute.strokeOpacity, visible = rectAttribute.visible, drawStrokeWhenZeroWH = (_a = rectAttribute.drawStrokeWhenZeroWH) !== null && _a !== void 0 ? _a : false } = rect.attribute;
26706
26774
  let { width, height } = rect.attribute;
26707
26775
  width = (width !== null && width !== void 0 ? width : x1 - originX) || 0;
26708
26776
  height = (height !== null && height !== void 0 ? height : y1 - originY) || 0;
26709
- const z = (_a = this.z) !== null && _a !== void 0 ? _a : 0;
26777
+ const z = (_b = this.z) !== null && _b !== void 0 ? _b : 0;
26710
26778
  const fVisible = rectFillVisible(opacity, fillOpacity, width, height, fill);
26711
- const sVisible = rectStrokeVisible(opacity, strokeOpacity, width, height);
26779
+ const sVisible = rectStrokeVisible(opacity, strokeOpacity, width, height, drawStrokeWhenZeroWH);
26712
26780
  const doFill = runFill(fill);
26713
26781
  const doStroke = runStroke(stroke, lineWidth);
26714
26782
  if (!(rect.valid && visible)) {
@@ -1,6 +1,9 @@
1
1
  import type { IEventListenerManager } from '../interface/event-listener-manager';
2
2
  export declare class EventListenerManager implements IEventListenerManager {
3
- protected _listenerMap: Map<string, Map<EventListenerOrEventListenerObject, EventListener>>;
3
+ protected _listenerMap: Map<string, Map<EventListenerOrEventListenerObject, Map<boolean, {
4
+ wrappedListener: EventListener;
5
+ options?: boolean | AddEventListenerOptions;
6
+ }>>>;
4
7
  protected _eventListenerTransformer: (event: Event) => Event;
5
8
  constructor();
6
9
  setEventListenerTransformer(transformer: (event: Event) => Event): void;
@@ -8,6 +11,20 @@ export declare class EventListenerManager implements IEventListenerManager {
8
11
  removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
9
12
  dispatchEvent(event: Event): boolean;
10
13
  clearAllEventListeners(): void;
14
+ protected _resolveCapture(options?: boolean | EventListenerOptions | AddEventListenerOptions): boolean;
15
+ protected _resolveOnce(options?: boolean | AddEventListenerOptions): boolean;
16
+ protected _getOrCreateListenerTypeMap(type: string): Map<EventListenerOrEventListenerObject, Map<boolean, {
17
+ wrappedListener: EventListener;
18
+ options?: boolean | AddEventListenerOptions;
19
+ }>>;
20
+ protected _getOrCreateWrappedMap(listenerTypeMap: Map<EventListenerOrEventListenerObject, Map<boolean, {
21
+ wrappedListener: EventListener;
22
+ options?: boolean | AddEventListenerOptions;
23
+ }>>, listener: EventListenerOrEventListenerObject): Map<boolean, {
24
+ wrappedListener: EventListener;
25
+ options?: boolean | AddEventListenerOptions;
26
+ }>;
27
+ protected _deleteListenerRecord(type: string, listener: EventListenerOrEventListenerObject, capture: boolean): void;
11
28
  protected _nativeAddEventListener(type: string, listener: EventListener, options?: boolean | AddEventListenerOptions): void;
12
29
  protected _nativeRemoveEventListener(type: string, listener: EventListener, options?: boolean | EventListenerOptions): void;
13
30
  protected _nativeDispatchEvent(event: Event): boolean;
@@ -7,30 +7,60 @@ export class EventListenerManager {
7
7
  }
8
8
  addEventListener(type, listener, options) {
9
9
  if (!listener) return;
10
+ const capture = this._resolveCapture(options), once = this._resolveOnce(options), listenerTypeMap = this._getOrCreateListenerTypeMap(type), wrappedMap = this._getOrCreateWrappedMap(listenerTypeMap, listener);
11
+ if (wrappedMap.has(capture)) return;
10
12
  const wrappedListener = event => {
11
13
  const transformedEvent = this._eventListenerTransformer(event);
12
- "function" == typeof listener ? listener(transformedEvent) : listener.handleEvent && listener.handleEvent(transformedEvent);
14
+ "function" == typeof listener ? listener(transformedEvent) : listener.handleEvent && listener.handleEvent(transformedEvent),
15
+ once && this._deleteListenerRecord(type, listener, capture);
13
16
  };
14
- this._listenerMap.has(type) || this._listenerMap.set(type, new Map), this._listenerMap.get(type).set(listener, wrappedListener),
15
- this._nativeAddEventListener(type, wrappedListener, options);
17
+ wrappedMap.set(capture, {
18
+ wrappedListener: wrappedListener,
19
+ options: options
20
+ }), this._nativeAddEventListener(type, wrappedListener, options);
16
21
  }
17
22
  removeEventListener(type, listener, options) {
18
- var _a;
23
+ var _a, _b;
19
24
  if (!listener) return;
20
- const wrappedListener = null === (_a = this._listenerMap.get(type)) || void 0 === _a ? void 0 : _a.get(listener);
21
- wrappedListener && (this._nativeRemoveEventListener(type, wrappedListener, options),
22
- this._listenerMap.get(type).delete(listener), 0 === this._listenerMap.get(type).size && this._listenerMap.delete(type));
25
+ const capture = this._resolveCapture(options), wrappedRecord = null === (_b = null === (_a = this._listenerMap.get(type)) || void 0 === _a ? void 0 : _a.get(listener)) || void 0 === _b ? void 0 : _b.get(capture);
26
+ wrappedRecord && (this._nativeRemoveEventListener(type, wrappedRecord.wrappedListener, capture),
27
+ this._deleteListenerRecord(type, listener, capture));
23
28
  }
24
29
  dispatchEvent(event) {
25
30
  return this._nativeDispatchEvent(event);
26
31
  }
27
32
  clearAllEventListeners() {
28
- this._listenerMap.forEach(((listenersMap, type) => {
29
- listenersMap.forEach(((wrappedListener, originalListener) => {
30
- this._nativeRemoveEventListener(type, wrappedListener, void 0);
33
+ this._listenerMap.forEach(((listenerMap, type) => {
34
+ listenerMap.forEach((wrappedMap => {
35
+ wrappedMap.forEach(((wrappedRecord, capture) => {
36
+ this._nativeRemoveEventListener(type, wrappedRecord.wrappedListener, capture);
37
+ }));
31
38
  }));
32
39
  })), this._listenerMap.clear();
33
40
  }
41
+ _resolveCapture(options) {
42
+ return "boolean" == typeof options ? options : !!(null == options ? void 0 : options.capture);
43
+ }
44
+ _resolveOnce(options) {
45
+ return "object" == typeof options && !!(null == options ? void 0 : options.once);
46
+ }
47
+ _getOrCreateListenerTypeMap(type) {
48
+ let listenerTypeMap = this._listenerMap.get(type);
49
+ return listenerTypeMap || (listenerTypeMap = new Map, this._listenerMap.set(type, listenerTypeMap)),
50
+ listenerTypeMap;
51
+ }
52
+ _getOrCreateWrappedMap(listenerTypeMap, listener) {
53
+ let wrappedMap = listenerTypeMap.get(listener);
54
+ return wrappedMap || (wrappedMap = new Map, listenerTypeMap.set(listener, wrappedMap)),
55
+ wrappedMap;
56
+ }
57
+ _deleteListenerRecord(type, listener, capture) {
58
+ const listenerTypeMap = this._listenerMap.get(type);
59
+ if (!listenerTypeMap) return;
60
+ const wrappedMap = listenerTypeMap.get(listener);
61
+ wrappedMap && (wrappedMap.delete(capture), 0 === wrappedMap.size && listenerTypeMap.delete(listener),
62
+ 0 === listenerTypeMap.size && this._listenerMap.delete(type));
63
+ }
34
64
  _nativeAddEventListener(type, listener, options) {
35
65
  throw new Error("_nativeAddEventListener must be implemented by derived classes");
36
66
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/common/event-listener-manager.ts"],"names":[],"mappings":"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","file":"event-listener-manager.js","sourcesContent":["import type { IEventListenerManager } from '../interface/event-listener-manager';\n\n/**\n * Base class to manage event listeners with support for event transformation\n * Used by DefaultGlobal and DefaultWindow to handle the transformation of event coordinates\n */\nexport class EventListenerManager implements IEventListenerManager {\n /**\n * Map that stores the mapping from original listeners to wrapped listeners\n * Structure: Map<eventType, Map<originalListener, wrappedListener>>\n */\n protected _listenerMap: Map<string, Map<EventListenerOrEventListenerObject, EventListener>>;\n\n /**\n * Transformer function that transforms the event\n */\n protected _eventListenerTransformer: (event: Event) => Event;\n\n constructor() {\n this._listenerMap = new Map();\n this._eventListenerTransformer = event => event; // Default: no transformation\n }\n\n /**\n * Set the event transformer function\n * @param transformer Function that transforms events\n */\n setEventListenerTransformer(transformer: (event: Event) => Event): void {\n this._eventListenerTransformer = transformer || (event => event);\n }\n\n /**\n * Add an event listener with event transformation\n * @param type Event type\n * @param listener Original event listener\n * @param options Event listener options\n */\n addEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | AddEventListenerOptions\n ): void {\n if (!listener) {\n return;\n }\n\n // Create a wrapped listener that applies the transformation\n const wrappedListener = (event: Event) => {\n const transformedEvent = this._eventListenerTransformer(event);\n if (typeof listener === 'function') {\n listener(transformedEvent);\n } else if (listener.handleEvent) {\n listener.handleEvent(transformedEvent);\n }\n };\n\n // Store the mapping between original and wrapped listener\n if (!this._listenerMap.has(type)) {\n this._listenerMap.set(type, new Map());\n }\n this._listenerMap.get(type)!.set(listener, wrappedListener);\n\n // Add the wrapped listener\n this._nativeAddEventListener(type, wrappedListener, options);\n }\n\n /**\n * Remove an event listener\n * @param type Event type\n * @param listener Event listener to remove\n * @param options Event listener options\n */\n removeEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | EventListenerOptions\n ): void {\n if (!listener) {\n return;\n }\n\n // Get the wrapped listener from our map\n const wrappedListener = this._listenerMap.get(type)?.get(listener);\n if (wrappedListener) {\n // Remove the wrapped listener\n this._nativeRemoveEventListener(type, wrappedListener, options);\n\n // Remove from our map\n this._listenerMap.get(type)!.delete(listener);\n if (this._listenerMap.get(type)!.size === 0) {\n this._listenerMap.delete(type);\n }\n }\n }\n\n /**\n * Dispatch an event\n * @param event Event to dispatch\n */\n dispatchEvent(event: Event): boolean {\n return this._nativeDispatchEvent(event);\n }\n\n /**\n * Clear all event listeners\n */\n clearAllEventListeners(): void {\n this._listenerMap.forEach((listenersMap, type) => {\n listenersMap.forEach((wrappedListener, originalListener) => {\n this._nativeRemoveEventListener(type, wrappedListener, undefined);\n });\n });\n this._listenerMap.clear();\n }\n\n /**\n * Native implementation of addEventListener\n * To be implemented by derived classes\n */\n protected _nativeAddEventListener(\n type: string,\n listener: EventListener,\n options?: boolean | AddEventListenerOptions\n ): void {\n throw new Error('_nativeAddEventListener must be implemented by derived classes');\n }\n\n /**\n * Native implementation of removeEventListener\n * To be implemented by derived classes\n */\n protected _nativeRemoveEventListener(\n type: string,\n listener: EventListener,\n options?: boolean | EventListenerOptions\n ): void {\n throw new Error('_nativeRemoveEventListener must be implemented by derived classes');\n }\n\n /**\n * Native implementation of dispatchEvent\n * To be implemented by derived classes\n */\n protected _nativeDispatchEvent(event: Event): boolean {\n throw new Error('_nativeDispatchEvent must be implemented by derived classes');\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/common/event-listener-manager.ts"],"names":[],"mappings":"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","file":"event-listener-manager.js","sourcesContent":["import type { IEventListenerManager } from '../interface/event-listener-manager';\n\n/**\n * Base class to manage event listeners with support for event transformation\n * Used by DefaultGlobal and DefaultWindow to handle the transformation of event coordinates\n */\nexport class EventListenerManager implements IEventListenerManager {\n /**\n * Map that stores the mapping from original listeners to wrapped listeners\n * Structure: Map<eventType, Map<originalListener, Map<capture, wrappedRecord>>>\n */\n protected _listenerMap: Map<\n string,\n Map<\n EventListenerOrEventListenerObject,\n Map<boolean, { wrappedListener: EventListener; options?: boolean | AddEventListenerOptions }>\n >\n >;\n\n /**\n * Transformer function that transforms the event\n */\n protected _eventListenerTransformer: (event: Event) => Event;\n\n constructor() {\n this._listenerMap = new Map();\n this._eventListenerTransformer = event => event; // Default: no transformation\n }\n\n /**\n * Set the event transformer function\n * @param transformer Function that transforms events\n */\n setEventListenerTransformer(transformer: (event: Event) => Event): void {\n this._eventListenerTransformer = transformer || (event => event);\n }\n\n /**\n * Add an event listener with event transformation\n * @param type Event type\n * @param listener Original event listener\n * @param options Event listener options\n */\n addEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | AddEventListenerOptions\n ): void {\n if (!listener) {\n return;\n }\n\n const capture = this._resolveCapture(options);\n const once = this._resolveOnce(options);\n const listenerTypeMap = this._getOrCreateListenerTypeMap(type);\n const wrappedMap = this._getOrCreateWrappedMap(listenerTypeMap, listener);\n\n // Align with native behavior: adding same (type, listener, capture) repeatedly is a no-op.\n if (wrappedMap.has(capture)) {\n return;\n }\n\n // Create a wrapped listener that applies the transformation\n const wrappedListener = (event: Event) => {\n const transformedEvent = this._eventListenerTransformer(event);\n if (typeof listener === 'function') {\n listener(transformedEvent);\n } else if (listener.handleEvent) {\n listener.handleEvent(transformedEvent);\n }\n\n // Native once listeners are removed automatically after dispatch, clear the mapping as well.\n if (once) {\n this._deleteListenerRecord(type, listener, capture);\n }\n };\n\n // Store the mapping between original and wrapped listener\n wrappedMap.set(capture, { wrappedListener, options });\n\n // Add the wrapped listener\n this._nativeAddEventListener(type, wrappedListener, options);\n }\n\n /**\n * Remove an event listener\n * @param type Event type\n * @param listener Event listener to remove\n * @param options Event listener options\n */\n removeEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | EventListenerOptions\n ): void {\n if (!listener) {\n return;\n }\n\n const capture = this._resolveCapture(options);\n const wrappedRecord = this._listenerMap.get(type)?.get(listener)?.get(capture);\n if (wrappedRecord) {\n // Remove the wrapped listener\n this._nativeRemoveEventListener(type, wrappedRecord.wrappedListener, capture);\n this._deleteListenerRecord(type, listener, capture);\n }\n }\n\n /**\n * Dispatch an event\n * @param event Event to dispatch\n */\n dispatchEvent(event: Event): boolean {\n return this._nativeDispatchEvent(event);\n }\n\n /**\n * Clear all event listeners\n */\n clearAllEventListeners(): void {\n this._listenerMap.forEach((listenerMap, type) => {\n listenerMap.forEach(wrappedMap => {\n wrappedMap.forEach((wrappedRecord, capture) => {\n this._nativeRemoveEventListener(type, wrappedRecord.wrappedListener, capture);\n });\n });\n });\n this._listenerMap.clear();\n }\n\n protected _resolveCapture(options?: boolean | EventListenerOptions | AddEventListenerOptions): boolean {\n if (typeof options === 'boolean') {\n return options;\n }\n return !!options?.capture;\n }\n\n protected _resolveOnce(options?: boolean | AddEventListenerOptions): boolean {\n return typeof options === 'object' && !!options?.once;\n }\n\n protected _getOrCreateListenerTypeMap(\n type: string\n ): Map<\n EventListenerOrEventListenerObject,\n Map<boolean, { wrappedListener: EventListener; options?: boolean | AddEventListenerOptions }>\n > {\n let listenerTypeMap = this._listenerMap.get(type);\n if (!listenerTypeMap) {\n listenerTypeMap = new Map();\n this._listenerMap.set(type, listenerTypeMap);\n }\n return listenerTypeMap;\n }\n\n protected _getOrCreateWrappedMap(\n listenerTypeMap: Map<\n EventListenerOrEventListenerObject,\n Map<boolean, { wrappedListener: EventListener; options?: boolean | AddEventListenerOptions }>\n >,\n listener: EventListenerOrEventListenerObject\n ): Map<boolean, { wrappedListener: EventListener; options?: boolean | AddEventListenerOptions }> {\n let wrappedMap = listenerTypeMap.get(listener);\n if (!wrappedMap) {\n wrappedMap = new Map();\n listenerTypeMap.set(listener, wrappedMap);\n }\n return wrappedMap;\n }\n\n protected _deleteListenerRecord(type: string, listener: EventListenerOrEventListenerObject, capture: boolean): void {\n const listenerTypeMap = this._listenerMap.get(type);\n if (!listenerTypeMap) {\n return;\n }\n const wrappedMap = listenerTypeMap.get(listener);\n if (!wrappedMap) {\n return;\n }\n wrappedMap.delete(capture);\n if (wrappedMap.size === 0) {\n listenerTypeMap.delete(listener);\n }\n if (listenerTypeMap.size === 0) {\n this._listenerMap.delete(type);\n }\n }\n\n /**\n * Native implementation of addEventListener\n * To be implemented by derived classes\n */\n protected _nativeAddEventListener(\n type: string,\n listener: EventListener,\n options?: boolean | AddEventListenerOptions\n ): void {\n throw new Error('_nativeAddEventListener must be implemented by derived classes');\n }\n\n /**\n * Native implementation of removeEventListener\n * To be implemented by derived classes\n */\n protected _nativeRemoveEventListener(\n type: string,\n listener: EventListener,\n options?: boolean | EventListenerOptions\n ): void {\n throw new Error('_nativeRemoveEventListener must be implemented by derived classes');\n }\n\n /**\n * Native implementation of dispatchEvent\n * To be implemented by derived classes\n */\n protected _nativeDispatchEvent(event: Event): boolean {\n throw new Error('_nativeDispatchEvent must be implemented by derived classes');\n }\n}\n"]}
@@ -58,8 +58,8 @@ export declare class Stage extends Group implements IStage {
58
58
  readonly graphicService: IGraphicService;
59
59
  private _eventSystem?;
60
60
  private get eventSystem();
61
- protected _beforeRender?: (stage: IStage) => void;
62
- protected _afterRender?: (stage: IStage) => void;
61
+ protected _beforeRenderList: Array<(stage: IStage) => void>;
62
+ protected _afterRenderList: Array<(stage: IStage) => void>;
63
63
  protected _afterClearScreen?: (drawParams: any) => void;
64
64
  protected _afterClearRect?: (drawParams: any) => void;
65
65
  protected _skipRender?: number;
@@ -113,7 +113,9 @@ export declare class Stage extends Group implements IStage {
113
113
  protected afterRender: (stage: IStage) => void;
114
114
  protected afterTickCb: () => void;
115
115
  setBeforeRender(cb: (stage: IStage) => void): void;
116
+ removeBeforeRender(cb: (stage: IStage) => void): void;
116
117
  setAfterRender(cb: (stage: IStage) => void): void;
118
+ removeAfterRender(cb: (stage: IStage) => void): void;
117
119
  afterNextRender(cb: (stage: IStage) => void): void;
118
120
  enableView3dTransform(): void;
119
121
  disableView3dTranform(): void;
package/es/core/stage.js CHANGED
@@ -126,13 +126,13 @@ export class Stage extends Group {
126
126
  this._skipRender > 1 && this.renderNextFrame(), this._skipRender = 0;
127
127
  } else this._skipRender = 1;
128
128
  }, this.beforeRender = stage => {
129
- this._beforeRender && this._beforeRender(stage);
129
+ this._beforeRenderList.forEach((cb => cb(stage)));
130
130
  }, this.afterClearScreen = drawParams => {
131
131
  this._afterClearScreen && this._afterClearScreen(drawParams);
132
132
  }, this.afterClearRect = drawParams => {
133
133
  this._afterClearRect && this._afterClearRect(drawParams);
134
134
  }, this.afterRender = stage => {
135
- this.renderCount++, this._afterRender && this._afterRender(stage), this._afterNextRenderCbs && this._afterNextRenderCbs.forEach((cb => cb(stage))),
135
+ this.renderCount++, this._afterRenderList.forEach((cb => cb(stage))), this._afterNextRenderCbs && this._afterNextRenderCbs.forEach((cb => cb(stage))),
136
136
  this._afterNextRenderCbs = null, this.tickedBeforeRender = !1;
137
137
  }, this.afterTickCb = () => {
138
138
  this.tickedBeforeRender = !0, "rendering" !== this.state && this.renderNextFrame();
@@ -145,7 +145,7 @@ export class Stage extends Group {
145
145
  this.window = container.get(VWindow), this.renderService = container.get(RenderService),
146
146
  this.pluginService = container.get(PluginService), this.layerService = container.get(LayerService),
147
147
  this.graphicService = container.get(GraphicService), this.pluginService.active(this, params),
148
- this.window.create({
148
+ this._beforeRenderList = [], this._afterRenderList = [], this.window.create({
149
149
  width: params.width,
150
150
  height: params.height,
151
151
  viewBox: params.viewBox,
@@ -163,8 +163,8 @@ export class Stage extends Group {
163
163
  params.enableHtmlAttribute && this.enableHtmlAttribute(params.enableHtmlAttribute),
164
164
  params.ReactDOM && this.enableReactAttribute(params.ReactDOM), params.enableLayout && this.enableLayout(),
165
165
  this.hooks.beforeRender.tap("constructor", this.beforeRender), this.hooks.afterRender.tap("constructor", this.afterRender),
166
+ params.beforeRender && this._beforeRenderList.push(params.beforeRender), params.afterRender && this._afterRenderList.push(params.afterRender),
166
167
  this.hooks.afterClearScreen.tap("constructor", this.afterClearScreen), this.hooks.afterClearRect.tap("constructor", this.afterClearRect),
167
- this._beforeRender = params.beforeRender, this._afterRender = params.afterRender,
168
168
  this._afterClearScreen = params.afterClearScreen, this._afterClearRect = params.afterClearRect,
169
169
  this.supportInteractiveLayer = !1 !== params.interactiveLayer, params.optimize || (params.optimize = {
170
170
  tickRenderMode: "effect"
@@ -259,10 +259,16 @@ export class Stage extends Group {
259
259
  options.enableView3dTransform && this.enableView3dTransform();
260
260
  }
261
261
  setBeforeRender(cb) {
262
- this._beforeRender = cb;
262
+ this._beforeRenderList.push(cb);
263
+ }
264
+ removeBeforeRender(cb) {
265
+ this._beforeRenderList = this._beforeRenderList.filter((c => c !== cb));
263
266
  }
264
267
  setAfterRender(cb) {
265
- this._afterRender = cb;
268
+ this._afterRenderList.push(cb);
269
+ }
270
+ removeAfterRender(cb) {
271
+ this._afterRenderList = this._afterRenderList.filter((c => c !== cb));
266
272
  }
267
273
  afterNextRender(cb) {
268
274
  this._afterNextRenderCbs || (this._afterNextRenderCbs = []), this._afterNextRenderCbs.push(cb);