@visactor/vrender-core 1.0.10 → 1.0.11-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) 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 +14 -7
  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/graphic.js +4 -3
  12. package/cjs/graphic/graphic.js.map +1 -1
  13. package/cjs/interface/animation/animate.d.ts +1 -0
  14. package/cjs/interface/animation/animate.js.map +1 -1
  15. package/cjs/interface/graphic/rect.d.ts +1 -0
  16. package/cjs/interface/graphic/rect.js.map +1 -1
  17. package/cjs/interface/stage.d.ts +2 -0
  18. package/cjs/interface/stage.js.map +1 -1
  19. package/cjs/plugins/builtin-plugin/edit-module.js +4 -4
  20. package/cjs/plugins/builtin-plugin/edit-module.js.map +1 -1
  21. package/cjs/render/contributions/render/rect-render.js +2 -2
  22. package/cjs/render/contributions/render/rect-render.js.map +1 -1
  23. package/cjs/render/contributions/render/rect3d-render.js +4 -4
  24. package/cjs/render/contributions/render/rect3d-render.js.map +1 -1
  25. package/cjs/render/contributions/render/utils.d.ts +1 -1
  26. package/cjs/render/contributions/render/utils.js +2 -2
  27. package/cjs/render/contributions/render/utils.js.map +1 -1
  28. package/dist/index.es.js +111 -32
  29. package/es/common/event-listener-manager.d.ts +18 -1
  30. package/es/common/event-listener-manager.js +40 -10
  31. package/es/common/event-listener-manager.js.map +1 -1
  32. package/es/core/stage.d.ts +4 -2
  33. package/es/core/stage.js +14 -7
  34. package/es/core/stage.js.map +1 -1
  35. package/es/event/event-manager.js +4 -1
  36. package/es/event/event-manager.js.map +1 -1
  37. package/es/graphic/config.js +4 -2
  38. package/es/graphic/config.js.map +1 -1
  39. package/es/graphic/graphic.js +4 -3
  40. package/es/graphic/graphic.js.map +1 -1
  41. package/es/interface/animation/animate.d.ts +1 -0
  42. package/es/interface/animation/animate.js.map +1 -1
  43. package/es/interface/graphic/rect.d.ts +1 -0
  44. package/es/interface/graphic/rect.js.map +1 -1
  45. package/es/interface/stage.d.ts +2 -0
  46. package/es/interface/stage.js.map +1 -1
  47. package/es/plugins/builtin-plugin/edit-module.js +4 -4
  48. package/es/plugins/builtin-plugin/edit-module.js.map +1 -1
  49. package/es/render/contributions/render/rect-render.js +2 -2
  50. package/es/render/contributions/render/rect-render.js.map +1 -1
  51. package/es/render/contributions/render/rect3d-render.js +3 -3
  52. package/es/render/contributions/render/rect3d-render.js.map +1 -1
  53. package/es/render/contributions/render/utils.d.ts +1 -1
  54. package/es/render/contributions/render/utils.js +2 -2
  55. package/es/render/contributions/render/utils.js.map +1 -1
  56. package/package.json +3 -3
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) {
@@ -19577,12 +19640,12 @@ let DefaultCanvasRectRender = class DefaultCanvasRectRender extends BaseRender {
19577
19640
  }
19578
19641
  drawShape(rect, context, x, y, drawContext, params, fillCb, strokeCb, rectAttribute) {
19579
19642
  rectAttribute = rectAttribute !== null && rectAttribute !== void 0 ? rectAttribute : getTheme(rect, params === null || params === void 0 ? void 0 : params.theme).rect;
19580
- 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;
19581
19644
  let { width, height } = rect.attribute;
19582
19645
  width = (width !== null && width !== void 0 ? width : x1 - originX) || 0;
19583
19646
  height = (height !== null && height !== void 0 ? height : y1 - originY) || 0;
19584
19647
  const fVisible = rectFillVisible(opacity, fillOpacity, width, height, fill);
19585
- const sVisible = rectStrokeVisible(opacity, strokeOpacity, width, height);
19648
+ const sVisible = rectStrokeVisible(opacity, strokeOpacity, width, height, drawStrokeWhenZeroWH);
19586
19649
  const doFill = runFill(fill, background);
19587
19650
  const doStroke = runStroke(stroke, lineWidth);
19588
19651
  if (!(rect.valid && visible)) {
@@ -22668,7 +22731,7 @@ class Stage extends Group {
22668
22731
  }
22669
22732
  };
22670
22733
  this.beforeRender = (stage) => {
22671
- this._beforeRender && this._beforeRender(stage);
22734
+ this._beforeRenderList.forEach(cb => cb(stage));
22672
22735
  };
22673
22736
  this.afterClearScreen = (drawParams) => {
22674
22737
  this._afterClearScreen && this._afterClearScreen(drawParams);
@@ -22678,7 +22741,7 @@ class Stage extends Group {
22678
22741
  };
22679
22742
  this.afterRender = (stage) => {
22680
22743
  this.renderCount++;
22681
- this._afterRender && this._afterRender(stage);
22744
+ this._afterRenderList.forEach(cb => cb(stage));
22682
22745
  this._afterNextRenderCbs && this._afterNextRenderCbs.forEach(cb => cb(stage));
22683
22746
  this._afterNextRenderCbs = null;
22684
22747
  this.tickedBeforeRender = false;
@@ -22705,6 +22768,8 @@ class Stage extends Group {
22705
22768
  this.layerService = container.get(LayerService);
22706
22769
  this.graphicService = container.get(GraphicService);
22707
22770
  this.pluginService.active(this, params);
22771
+ this._beforeRenderList = [];
22772
+ this._afterRenderList = [];
22708
22773
  this.window.create({
22709
22774
  width: params.width,
22710
22775
  height: params.height,
@@ -22742,10 +22807,14 @@ class Stage extends Group {
22742
22807
  params.enableLayout && this.enableLayout();
22743
22808
  this.hooks.beforeRender.tap('constructor', this.beforeRender);
22744
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
+ }
22745
22816
  this.hooks.afterClearScreen.tap('constructor', this.afterClearScreen);
22746
22817
  this.hooks.afterClearRect.tap('constructor', this.afterClearRect);
22747
- this._beforeRender = params.beforeRender;
22748
- this._afterRender = params.afterRender;
22749
22818
  this._afterClearScreen = params.afterClearScreen;
22750
22819
  this._afterClearRect = params.afterClearRect;
22751
22820
  this.supportInteractiveLayer = params.interactiveLayer !== false;
@@ -22878,10 +22947,16 @@ class Stage extends Group {
22878
22947
  }
22879
22948
  }
22880
22949
  setBeforeRender(cb) {
22881
- this._beforeRender = cb;
22950
+ this._beforeRenderList.push(cb);
22951
+ }
22952
+ removeBeforeRender(cb) {
22953
+ this._beforeRenderList = this._beforeRenderList.filter(c => c !== cb);
22882
22954
  }
22883
22955
  setAfterRender(cb) {
22884
- this._afterRender = cb;
22956
+ this._afterRenderList.push(cb);
22957
+ }
22958
+ removeAfterRender(cb) {
22959
+ this._afterRenderList = this._afterRenderList.filter(c => c !== cb);
22885
22960
  }
22886
22961
  afterNextRender(cb) {
22887
22962
  if (!this._afterNextRenderCbs) {
@@ -23266,7 +23341,7 @@ class Stage extends Group {
23266
23341
  throw new Error('暂不支持');
23267
23342
  }
23268
23343
  release() {
23269
- var _a, _b;
23344
+ var _a, _b, _d;
23270
23345
  super.release();
23271
23346
  this.hooks.beforeRender.unTap('constructor', this.beforeRender);
23272
23347
  this.hooks.afterRender.unTap('constructor', this.afterRender);
@@ -23286,6 +23361,9 @@ class Stage extends Group {
23286
23361
  this.window.release();
23287
23362
  (_a = this._ticker) === null || _a === void 0 ? void 0 : _a.remTimeline(this === null || this === void 0 ? void 0 : this.timeline);
23288
23363
  (_b = this._ticker) === null || _b === void 0 ? void 0 : _b.removeListener('tick', this.afterTickCb);
23364
+ if (!this.params.ticker) {
23365
+ (_d = this._ticker) === null || _d === void 0 ? void 0 : _d.release();
23366
+ }
23289
23367
  this.renderService.renderTreeRoots = [];
23290
23368
  }
23291
23369
  setStage(stage) {
@@ -24920,6 +24998,7 @@ class EditModule {
24920
24998
  this.container = container !== null && container !== void 0 ? container : document.body;
24921
24999
  const textAreaDom = document.createElement('textarea');
24922
25000
  textAreaDom.autocomplete = 'off';
25001
+ textAreaDom.spellcheck = false;
24923
25002
  textAreaDom.innerText = '';
24924
25003
  this.applyStyle(textAreaDom);
24925
25004
  this.container.append(textAreaDom);
@@ -24944,7 +25023,7 @@ class EditModule {
24944
25023
  this.onFocusOutList.push(cb);
24945
25024
  }
24946
25025
  applyStyle(textAreaDom) {
24947
- 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;`);
25026
+ 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;`);
24948
25027
  textAreaDom.addEventListener('input', this.handleInput);
24949
25028
  textAreaDom.addEventListener('compositionstart', this.handleCompositionStart);
24950
25029
  textAreaDom.addEventListener('compositionend', this.handleCompositionEnd);
@@ -26692,15 +26771,15 @@ let DefaultCanvasRect3dRender = class DefaultCanvasRect3dRender extends Base3dRe
26692
26771
  this.numberType = RECT3D_NUMBER_TYPE;
26693
26772
  }
26694
26773
  drawShape(rect, context, x, y, drawContext, params, fillCb, strokeCb) {
26695
- var _a;
26774
+ var _a, _b;
26696
26775
  const rectAttribute = getTheme(rect, params === null || params === void 0 ? void 0 : params.theme).rect;
26697
- 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;
26776
+ 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;
26698
26777
  let { width, height } = rect.attribute;
26699
26778
  width = (width !== null && width !== void 0 ? width : x1 - originX) || 0;
26700
26779
  height = (height !== null && height !== void 0 ? height : y1 - originY) || 0;
26701
- const z = (_a = this.z) !== null && _a !== void 0 ? _a : 0;
26780
+ const z = (_b = this.z) !== null && _b !== void 0 ? _b : 0;
26702
26781
  const fVisible = rectFillVisible(opacity, fillOpacity, width, height, fill);
26703
- const sVisible = rectStrokeVisible(opacity, strokeOpacity, width, height);
26782
+ const sVisible = rectStrokeVisible(opacity, strokeOpacity, width, height, drawStrokeWhenZeroWH);
26704
26783
  const doFill = runFill(fill);
26705
26784
  const doStroke = runStroke(stroke, lineWidth);
26706
26785
  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);
@@ -493,7 +499,7 @@ export class Stage extends Group {
493
499
  throw new Error("暂不支持");
494
500
  }
495
501
  release() {
496
- var _a, _b;
502
+ var _a, _b, _d;
497
503
  super.release(), this.hooks.beforeRender.unTap("constructor", this.beforeRender),
498
504
  this.hooks.afterRender.unTap("constructor", this.afterRender), this.eventSystem && this.eventSystem.release(),
499
505
  this.layerService.releaseStage(this), this.pluginService.release(), this.forEach((layer => {
@@ -502,6 +508,7 @@ export class Stage extends Group {
502
508
  item.setStage && item.setStage(null, null), this.interactiveLayer.removeChild(item);
503
509
  })), this.interactiveLayer.release()), this.window.release(), null === (_a = this._ticker) || void 0 === _a || _a.remTimeline(null == this ? void 0 : this.timeline),
504
510
  null === (_b = this._ticker) || void 0 === _b || _b.removeListener("tick", this.afterTickCb),
511
+ this.params.ticker || null === (_d = this._ticker) || void 0 === _d || _d.release(),
505
512
  this.renderService.renderTreeRoots = [];
506
513
  }
507
514
  setStage(stage) {}