Skip to content

Commit e155f14

Browse files
xuefei1313skie1997
authored andcommitted
fix: fix memory leaks
1 parent feff35c commit e155f14

5 files changed

Lines changed: 116 additions & 28 deletions

File tree

packages/vrender-components/src/player/base-player.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,4 +390,11 @@ export abstract class BasePlayer<T> extends AbstractComponent<Required<PlayerAtt
390390
value: this._data[dataIndex]
391391
});
392392
}
393+
394+
release(all: boolean) {
395+
if (!this._sliderVisible) {
396+
this._slider.release(all);
397+
}
398+
super.release(all);
399+
}
393400
}

packages/vrender-components/src/slider/slider.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -620,8 +620,8 @@ export class Slider extends AbstractComponent<Required<SliderAttributes>> {
620620
text: textConfig.formatter
621621
? textConfig.formatter(this._tooltipState.value)
622622
: !this._isHorizontal && align === 'left'
623-
? `${this._tooltipState.value.toFixed(textConfig.precision ?? 0)} ≈`
624-
: `≈ ${this._tooltipState.value.toFixed(textConfig.precision ?? 0)}`
623+
? `${this._tooltipState.value.toFixed(textConfig.precision ?? 0)} ≈`
624+
: `≈ ${this._tooltipState.value.toFixed(textConfig.precision ?? 0)}`
625625
});
626626
}
627627
}
@@ -1121,7 +1121,7 @@ export class Slider extends AbstractComponent<Required<SliderAttributes>> {
11211121
* 浏览器上的事件必须解绑,防止内存泄漏,场景树上的事件会自动解绑
11221122
*/
11231123
super.release(all);
1124-
(vglobal.env === 'browser' ? vglobal : this.stage).addEventListener('touchmove', this._handleTouchMove, {
1124+
(vglobal.env === 'browser' ? vglobal : this.stage).removeEventListener('touchmove', this._handleTouchMove, {
11251125
passive: false
11261126
});
11271127
this._clearAllDragEvents();

packages/vrender-core/src/common/event-listener-manager.ts

Lines changed: 92 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,15 @@ import type { IEventListenerManager } from '../interface/event-listener-manager'
77
export class EventListenerManager implements IEventListenerManager {
88
/**
99
* Map that stores the mapping from original listeners to wrapped listeners
10-
* Structure: Map<eventType, Map<originalListener, wrappedListener>>
10+
* Structure: Map<eventType, Map<originalListener, Map<capture, wrappedRecord>>>
1111
*/
12-
protected _listenerMap: Map<string, Map<EventListenerOrEventListenerObject, EventListener>>;
12+
protected _listenerMap: Map<
13+
string,
14+
Map<
15+
EventListenerOrEventListenerObject,
16+
Map<boolean, { wrappedListener: EventListener; options?: boolean | AddEventListenerOptions }>
17+
>
18+
>;
1319

1420
/**
1521
* Transformer function that transforms the event
@@ -44,6 +50,16 @@ export class EventListenerManager implements IEventListenerManager {
4450
return;
4551
}
4652

53+
const capture = this._resolveCapture(options);
54+
const once = this._resolveOnce(options);
55+
const listenerTypeMap = this._getOrCreateListenerTypeMap(type);
56+
const wrappedMap = this._getOrCreateWrappedMap(listenerTypeMap, listener);
57+
58+
// Align with native behavior: adding same (type, listener, capture) repeatedly is a no-op.
59+
if (wrappedMap.has(capture)) {
60+
return;
61+
}
62+
4763
// Create a wrapped listener that applies the transformation
4864
const wrappedListener = (event: Event) => {
4965
const transformedEvent = this._eventListenerTransformer(event);
@@ -52,13 +68,15 @@ export class EventListenerManager implements IEventListenerManager {
5268
} else if (listener.handleEvent) {
5369
listener.handleEvent(transformedEvent);
5470
}
71+
72+
// Native once listeners are removed automatically after dispatch, clear the mapping as well.
73+
if (once) {
74+
this._deleteListenerRecord(type, listener, capture);
75+
}
5576
};
5677

5778
// Store the mapping between original and wrapped listener
58-
if (!this._listenerMap.has(type)) {
59-
this._listenerMap.set(type, new Map());
60-
}
61-
this._listenerMap.get(type)!.set(listener, wrappedListener);
79+
wrappedMap.set(capture, { wrappedListener, options });
6280

6381
// Add the wrapped listener
6482
this._nativeAddEventListener(type, wrappedListener, options);
@@ -79,17 +97,12 @@ export class EventListenerManager implements IEventListenerManager {
7997
return;
8098
}
8199

82-
// Get the wrapped listener from our map
83-
const wrappedListener = this._listenerMap.get(type)?.get(listener);
84-
if (wrappedListener) {
100+
const capture = this._resolveCapture(options);
101+
const wrappedRecord = this._listenerMap.get(type)?.get(listener)?.get(capture);
102+
if (wrappedRecord) {
85103
// Remove the wrapped listener
86-
this._nativeRemoveEventListener(type, wrappedListener, options);
87-
88-
// Remove from our map
89-
this._listenerMap.get(type)!.delete(listener);
90-
if (this._listenerMap.get(type)!.size === 0) {
91-
this._listenerMap.delete(type);
92-
}
104+
this._nativeRemoveEventListener(type, wrappedRecord.wrappedListener, capture);
105+
this._deleteListenerRecord(type, listener, capture);
93106
}
94107
}
95108

@@ -105,14 +118,74 @@ export class EventListenerManager implements IEventListenerManager {
105118
* Clear all event listeners
106119
*/
107120
clearAllEventListeners(): void {
108-
this._listenerMap.forEach((listenersMap, type) => {
109-
listenersMap.forEach((wrappedListener, originalListener) => {
110-
this._nativeRemoveEventListener(type, wrappedListener, undefined);
121+
this._listenerMap.forEach((listenerMap, type) => {
122+
listenerMap.forEach(wrappedMap => {
123+
wrappedMap.forEach((wrappedRecord, capture) => {
124+
this._nativeRemoveEventListener(type, wrappedRecord.wrappedListener, capture);
125+
});
111126
});
112127
});
113128
this._listenerMap.clear();
114129
}
115130

131+
protected _resolveCapture(options?: boolean | EventListenerOptions | AddEventListenerOptions): boolean {
132+
if (typeof options === 'boolean') {
133+
return options;
134+
}
135+
return !!options?.capture;
136+
}
137+
138+
protected _resolveOnce(options?: boolean | AddEventListenerOptions): boolean {
139+
return typeof options === 'object' && !!options?.once;
140+
}
141+
142+
protected _getOrCreateListenerTypeMap(
143+
type: string
144+
): Map<
145+
EventListenerOrEventListenerObject,
146+
Map<boolean, { wrappedListener: EventListener; options?: boolean | AddEventListenerOptions }>
147+
> {
148+
let listenerTypeMap = this._listenerMap.get(type);
149+
if (!listenerTypeMap) {
150+
listenerTypeMap = new Map();
151+
this._listenerMap.set(type, listenerTypeMap);
152+
}
153+
return listenerTypeMap;
154+
}
155+
156+
protected _getOrCreateWrappedMap(
157+
listenerTypeMap: Map<
158+
EventListenerOrEventListenerObject,
159+
Map<boolean, { wrappedListener: EventListener; options?: boolean | AddEventListenerOptions }>
160+
>,
161+
listener: EventListenerOrEventListenerObject
162+
): Map<boolean, { wrappedListener: EventListener; options?: boolean | AddEventListenerOptions }> {
163+
let wrappedMap = listenerTypeMap.get(listener);
164+
if (!wrappedMap) {
165+
wrappedMap = new Map();
166+
listenerTypeMap.set(listener, wrappedMap);
167+
}
168+
return wrappedMap;
169+
}
170+
171+
protected _deleteListenerRecord(type: string, listener: EventListenerOrEventListenerObject, capture: boolean): void {
172+
const listenerTypeMap = this._listenerMap.get(type);
173+
if (!listenerTypeMap) {
174+
return;
175+
}
176+
const wrappedMap = listenerTypeMap.get(listener);
177+
if (!wrappedMap) {
178+
return;
179+
}
180+
wrappedMap.delete(capture);
181+
if (wrappedMap.size === 0) {
182+
listenerTypeMap.delete(listener);
183+
}
184+
if (listenerTypeMap.size === 0) {
185+
this._listenerMap.delete(type);
186+
}
187+
}
188+
116189
/**
117190
* Native implementation of addEventListener
118191
* To be implemented by derived classes

packages/vrender-core/src/event/event-manager.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -790,6 +790,13 @@ export class EventManager {
790790
const constructor = event.constructor;
791791

792792
if (!this.eventPool.has(constructor as any)) {
793+
this.eventPool.get(constructor as any).forEach(e => {
794+
e.eventPhase = event.NONE;
795+
e.currentTarget = null;
796+
e.path = [];
797+
e.detailPath = [];
798+
e.target = null;
799+
});
793800
this.eventPool.set(constructor as any, []);
794801
}
795802

packages/vrender-core/src/graphic/graphic.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -259,29 +259,29 @@ export abstract class Graphic<T extends Partial<IGraphicAttribute> = Partial<IGr
259259
declare y1WithoutTransform?: number;
260260

261261
// aabbBounds,所有图形都需要有,所以初始化即赋值
262-
protected declare _AABBBounds: IAABBBounds;
262+
declare protected _AABBBounds: IAABBBounds;
263263
get AABBBounds(): IAABBBounds {
264264
return this.tryUpdateAABBBounds();
265265
}
266266
// 具有旋转的包围盒,部分图元需要,动态初始化
267-
protected declare _OBBBounds?: IOBBBounds;
267+
declare protected _OBBBounds?: IOBBBounds;
268268
get OBBBounds(): IOBBBounds {
269269
return this.tryUpdateOBBBounds();
270270
}
271-
protected declare _globalAABBBounds: IAABBBounds;
271+
declare protected _globalAABBBounds: IAABBBounds;
272272
// 全局包围盒,部分图元需要,动态初始化,建议使用AABBBounds
273273
get globalAABBBounds(): IAABBBounds {
274274
return this.tryUpdateGlobalAABBBounds();
275275
}
276-
protected declare _transMatrix: Matrix;
276+
declare protected _transMatrix: Matrix;
277277
get transMatrix(): Matrix {
278278
return this.tryUpdateLocalTransMatrix(true);
279279
}
280-
protected declare _globalTransMatrix: Matrix;
280+
declare protected _globalTransMatrix: Matrix;
281281
get globalTransMatrix(): Matrix {
282282
return this.tryUpdateGlobalTransMatrix(true);
283283
}
284-
protected declare _updateTag: number;
284+
declare protected _updateTag: number;
285285

286286
// 上次更新的stamp
287287
declare stamp?: number;
@@ -1569,6 +1569,7 @@ export abstract class Graphic<T extends Partial<IGraphicAttribute> = Partial<IGr
15691569
this.releaseStatus = 'released';
15701570
this.stopAnimates();
15711571
application.graphicService.onRelease(this);
1572+
super.release();
15721573
}
15731574

15741575
protected _emitCustomEvent(type: string, context?: any) {

0 commit comments

Comments
 (0)