Skip to content

Commit 0b2da58

Browse files
committed
Improve resource cleanup in dispose methods
Enhanced the dispose logic for renderer and shape objects to more thoroughly release resources and unbind events, including proper cleanup for WebGL and Canvas renderers. Updated checks for fill and stroke disposal to use function type checks, and refactored subdivision calls for clarity.
1 parent 289b380 commit 0b2da58

File tree

9 files changed

+233
-71
lines changed

9 files changed

+233
-71
lines changed

build/two.js

Lines changed: 65 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1220,7 +1220,7 @@ var Two = (() => {
12201220
* @name Two.PublishDate
12211221
* @property {String} - The automatically generated publish date in the build process to verify version release candidates.
12221222
*/
1223-
PublishDate: "2025-12-04T21:10:51.711Z",
1223+
PublishDate: "2025-12-05T06:10:07.715Z",
12241224
/**
12251225
* @name Two.Identifier
12261226
* @property {String} - String prefix for all Two.js object's ids. This trickles down to SVG ids.
@@ -1978,11 +1978,34 @@ var Two = (() => {
19781978
* This cleans up renderer-specific resources and unbinds all event listeners.
19791979
*/
19801980
dispose() {
1981-
const rendererType = this._renderer.type;
1982-
this._renderer = { type: rendererType };
19831981
if (typeof this.unbind === "function") {
19841982
this.unbind();
19851983
}
1984+
if (this._renderer) {
1985+
if (this._renderer.elem && this._renderer.elem.parentNode) {
1986+
this._renderer.elem.parentNode.removeChild(this._renderer.elem);
1987+
delete this._renderer.elem;
1988+
}
1989+
if (this.type === "WebGLRenderer" && this.renderer.ctx) {
1990+
const gl = this.renderer.ctx;
1991+
if (this._renderer.texture) {
1992+
gl.deleteTexture(this._renderer.texture);
1993+
delete this._renderer.texture;
1994+
}
1995+
if (this._renderer.positionBuffer) {
1996+
gl.deleteBuffer(this._renderer.positionBuffer);
1997+
delete this._renderer.positionBuffer;
1998+
}
1999+
if (this._renderer.effect) {
2000+
this._renderer.effect = null;
2001+
}
2002+
}
2003+
if (this.type === "CanvasRenderer" && this._renderer.context) {
2004+
delete this._renderer.context;
2005+
}
2006+
}
2007+
const rendererType = this._renderer.type;
2008+
this._renderer = { type: rendererType };
19862009
return this;
19872010
}
19882011
};
@@ -4113,6 +4136,21 @@ var Two = (() => {
41134136
result.matrix = this.matrix.toObject();
41144137
return result;
41154138
}
4139+
/**
4140+
* @name Two.Shape#dispose
4141+
* @function
4142+
* @description Release the element's renderer object and detach any events.
4143+
* This cleans up renderer-specific resources and unbinds all event listeners.
4144+
*/
4145+
dispose() {
4146+
super.dispose();
4147+
if (typeof this.translation === "object" && typeof this.translation.unbind === "function") {
4148+
this.translation.unbind();
4149+
}
4150+
if (typeof this.scale === "object" && typeof this.scale.unbind === "function") {
4151+
this.scale.unbind();
4152+
}
4153+
}
41164154
/**
41174155
* @name Two.Shape#_update
41184156
* @function
@@ -5170,14 +5208,14 @@ var Two = (() => {
51705208
}
51715209
}
51725210
}
5173-
if (typeof this.fill === "object" && this.fill && "dispose" in this.fill) {
5211+
if (typeof this.fill === "object" && typeof this.fill.dispose === "function") {
51745212
this.fill.dispose();
5175-
} else if (typeof this.fill === "object" && this.fill && "unbind" in this.fill) {
5213+
} else if (typeof this.fill === "object" && typeof this.fill.unbind === "function") {
51765214
this.fill.unbind();
51775215
}
5178-
if (typeof this.stroke === "object" && this.stroke && "dispose" in this.stroke) {
5216+
if (typeof this.stroke === "object" && typeof this.stroke.dispose === "function") {
51795217
this.stroke.dispose();
5180-
} else if (typeof this.stroke === "object" && this.stroke && "unbind" in this.stroke) {
5218+
} else if (typeof this.stroke === "object" && typeof this.stroke.unbind === "function") {
51815219
this.stroke.unbind();
51825220
}
51835221
return this;
@@ -5623,7 +5661,11 @@ var Two = (() => {
56235661
}
56245662
const isCurve = isSegmentCurved(currentOriginal, prevOriginal);
56255663
if (isCurve) {
5626-
const subdivided = getSubdivisions(currentOriginal, prevOriginal, limit);
5664+
const subdivided = getSubdivisions(
5665+
currentOriginal,
5666+
prevOriginal,
5667+
limit
5668+
);
56275669
const steps = subdivided.length;
56285670
const prevClone = points[points.length - 1];
56295671
let startSegment = prevClone.clone();
@@ -5666,7 +5708,11 @@ var Two = (() => {
56665708
points.push(currentClone);
56675709
}
56685710
} else {
5669-
const subdivided = getSubdivisions(currentOriginal, prevOriginal, limit);
5711+
const subdivided = getSubdivisions(
5712+
currentOriginal,
5713+
prevOriginal,
5714+
limit
5715+
);
56705716
for (let j = 1; j < subdivided.length; j += 1) {
56715717
const anchor2 = subdivided[j];
56725718
inheritRelative(anchor2, prevOriginal);
@@ -7730,14 +7776,14 @@ var Two = (() => {
77307776
}
77317777
}
77327778
}
7733-
if (typeof this.fill === "object" && this.fill && "dispose" in this.fill) {
7779+
if (typeof this.fill === "object" && typeof this.fill.dispose === "function") {
77347780
this.fill.dispose();
7735-
} else if (typeof this.fill === "object" && this.fill && "unbind" in this.fill) {
7781+
} else if (typeof this.fill === "object" && typeof this.fill.unbind === "function") {
77367782
this.fill.unbind();
77377783
}
7738-
if (typeof this.stroke === "object" && this.stroke && "dispose" in this.stroke) {
7784+
if (typeof this.stroke === "object" && typeof this.stroke.dispose === "function") {
77397785
this.stroke.dispose();
7740-
} else if (typeof this.stroke === "object" && this.stroke && "unbind" in this.stroke) {
7786+
} else if (typeof this.stroke === "object" && typeof this.stroke.unbind === "function") {
77417787
this.stroke.unbind();
77427788
}
77437789
return this;
@@ -9173,14 +9219,14 @@ var Two = (() => {
91739219
*/
91749220
dispose() {
91759221
super.dispose();
9176-
if (typeof this.fill === "object" && this.fill && "dispose" in this.fill) {
9222+
if (typeof this.fill === "object" && typeof this.fill.dispose === "function") {
91779223
this.fill.dispose();
9178-
} else if (typeof this.fill === "object" && this.fill && "unbind" in this.fill) {
9224+
} else if (typeof this.fill === "object" && typeof this.fill.unbind === "function") {
91799225
this.fill.unbind();
91809226
}
9181-
if (typeof this.stroke === "object" && this.stroke && "dispose" in this.stroke) {
9227+
if (typeof this.stroke === "object" && typeof this.stroke.dispose === "function") {
91829228
this.stroke.dispose();
9183-
} else if (typeof this.stroke === "object" && this.stroke && "unbind" in this.stroke) {
9229+
} else if (typeof this.stroke === "object" && typeof this.stroke.unbind === "function") {
91849230
this.stroke.unbind();
91859231
}
91869232
return this;
@@ -15862,10 +15908,10 @@ var Two = (() => {
1586215908
if (typeof obj.unbind === "function") {
1586315909
obj.unbind();
1586415910
}
15865-
if (typeof obj.fill === "object" && "unbind" in obj.fill) {
15911+
if (typeof obj.fill === "object" && typeof obj.fill.unbind === "function") {
1586615912
obj.fill.unbind();
1586715913
}
15868-
if (typeof obj.stroke === "object" && "unbind" in obj.stroke) {
15914+
if (typeof obj.stroke === "object" && typeof obj.stroke.unbind === "function") {
1586915915
obj.stroke.unbind();
1587015916
}
1587115917
if (obj.vertices) {

build/two.min.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

build/two.module.js

Lines changed: 65 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1228,7 +1228,7 @@ var Constants = {
12281228
* @name Two.PublishDate
12291229
* @property {String} - The automatically generated publish date in the build process to verify version release candidates.
12301230
*/
1231-
PublishDate: "2025-12-04T21:10:51.711Z",
1231+
PublishDate: "2025-12-05T06:10:07.715Z",
12321232
/**
12331233
* @name Two.Identifier
12341234
* @property {String} - String prefix for all Two.js object's ids. This trickles down to SVG ids.
@@ -1987,11 +1987,34 @@ var _Element = class _Element extends Events {
19871987
* This cleans up renderer-specific resources and unbinds all event listeners.
19881988
*/
19891989
dispose() {
1990-
const rendererType = this._renderer.type;
1991-
this._renderer = { type: rendererType };
19921990
if (typeof this.unbind === "function") {
19931991
this.unbind();
19941992
}
1993+
if (this._renderer) {
1994+
if (this._renderer.elem && this._renderer.elem.parentNode) {
1995+
this._renderer.elem.parentNode.removeChild(this._renderer.elem);
1996+
delete this._renderer.elem;
1997+
}
1998+
if (this.type === "WebGLRenderer" && this.renderer.ctx) {
1999+
const gl = this.renderer.ctx;
2000+
if (this._renderer.texture) {
2001+
gl.deleteTexture(this._renderer.texture);
2002+
delete this._renderer.texture;
2003+
}
2004+
if (this._renderer.positionBuffer) {
2005+
gl.deleteBuffer(this._renderer.positionBuffer);
2006+
delete this._renderer.positionBuffer;
2007+
}
2008+
if (this._renderer.effect) {
2009+
this._renderer.effect = null;
2010+
}
2011+
}
2012+
if (this.type === "CanvasRenderer" && this._renderer.context) {
2013+
delete this._renderer.context;
2014+
}
2015+
}
2016+
const rendererType = this._renderer.type;
2017+
this._renderer = { type: rendererType };
19952018
return this;
19962019
}
19972020
};
@@ -4121,6 +4144,21 @@ var _Shape = class _Shape extends Element {
41214144
result.matrix = this.matrix.toObject();
41224145
return result;
41234146
}
4147+
/**
4148+
* @name Two.Shape#dispose
4149+
* @function
4150+
* @description Release the element's renderer object and detach any events.
4151+
* This cleans up renderer-specific resources and unbinds all event listeners.
4152+
*/
4153+
dispose() {
4154+
super.dispose();
4155+
if (typeof this.translation === "object" && typeof this.translation.unbind === "function") {
4156+
this.translation.unbind();
4157+
}
4158+
if (typeof this.scale === "object" && typeof this.scale.unbind === "function") {
4159+
this.scale.unbind();
4160+
}
4161+
}
41244162
/**
41254163
* @name Two.Shape#_update
41264164
* @function
@@ -5164,14 +5202,14 @@ var _Path = class _Path extends Shape {
51645202
}
51655203
}
51665204
}
5167-
if (typeof this.fill === "object" && this.fill && "dispose" in this.fill) {
5205+
if (typeof this.fill === "object" && typeof this.fill.dispose === "function") {
51685206
this.fill.dispose();
5169-
} else if (typeof this.fill === "object" && this.fill && "unbind" in this.fill) {
5207+
} else if (typeof this.fill === "object" && typeof this.fill.unbind === "function") {
51705208
this.fill.unbind();
51715209
}
5172-
if (typeof this.stroke === "object" && this.stroke && "dispose" in this.stroke) {
5210+
if (typeof this.stroke === "object" && typeof this.stroke.dispose === "function") {
51735211
this.stroke.dispose();
5174-
} else if (typeof this.stroke === "object" && this.stroke && "unbind" in this.stroke) {
5212+
} else if (typeof this.stroke === "object" && typeof this.stroke.unbind === "function") {
51755213
this.stroke.unbind();
51765214
}
51775215
return this;
@@ -5617,7 +5655,11 @@ var _Path = class _Path extends Shape {
56175655
}
56185656
const isCurve = isSegmentCurved(currentOriginal, prevOriginal);
56195657
if (isCurve) {
5620-
const subdivided = getSubdivisions(currentOriginal, prevOriginal, limit);
5658+
const subdivided = getSubdivisions(
5659+
currentOriginal,
5660+
prevOriginal,
5661+
limit
5662+
);
56215663
const steps = subdivided.length;
56225664
const prevClone = points[points.length - 1];
56235665
let startSegment = prevClone.clone();
@@ -5660,7 +5702,11 @@ var _Path = class _Path extends Shape {
56605702
points.push(currentClone);
56615703
}
56625704
} else {
5663-
const subdivided = getSubdivisions(currentOriginal, prevOriginal, limit);
5705+
const subdivided = getSubdivisions(
5706+
currentOriginal,
5707+
prevOriginal,
5708+
limit
5709+
);
56645710
for (let j = 1; j < subdivided.length; j += 1) {
56655711
const anchor2 = subdivided[j];
56665712
inheritRelative(anchor2, prevOriginal);
@@ -7780,14 +7826,14 @@ var _Points = class _Points extends Shape {
77807826
}
77817827
}
77827828
}
7783-
if (typeof this.fill === "object" && this.fill && "dispose" in this.fill) {
7829+
if (typeof this.fill === "object" && typeof this.fill.dispose === "function") {
77847830
this.fill.dispose();
7785-
} else if (typeof this.fill === "object" && this.fill && "unbind" in this.fill) {
7831+
} else if (typeof this.fill === "object" && typeof this.fill.unbind === "function") {
77867832
this.fill.unbind();
77877833
}
7788-
if (typeof this.stroke === "object" && this.stroke && "dispose" in this.stroke) {
7834+
if (typeof this.stroke === "object" && typeof this.stroke.dispose === "function") {
77897835
this.stroke.dispose();
7790-
} else if (typeof this.stroke === "object" && this.stroke && "unbind" in this.stroke) {
7836+
} else if (typeof this.stroke === "object" && typeof this.stroke.unbind === "function") {
77917837
this.stroke.unbind();
77927838
}
77937839
return this;
@@ -9175,14 +9221,14 @@ var _Text = class _Text extends Shape {
91759221
*/
91769222
dispose() {
91779223
super.dispose();
9178-
if (typeof this.fill === "object" && this.fill && "dispose" in this.fill) {
9224+
if (typeof this.fill === "object" && typeof this.fill.dispose === "function") {
91799225
this.fill.dispose();
9180-
} else if (typeof this.fill === "object" && this.fill && "unbind" in this.fill) {
9226+
} else if (typeof this.fill === "object" && typeof this.fill.unbind === "function") {
91819227
this.fill.unbind();
91829228
}
9183-
if (typeof this.stroke === "object" && this.stroke && "dispose" in this.stroke) {
9229+
if (typeof this.stroke === "object" && typeof this.stroke.dispose === "function") {
91849230
this.stroke.dispose();
9185-
} else if (typeof this.stroke === "object" && this.stroke && "unbind" in this.stroke) {
9231+
} else if (typeof this.stroke === "object" && typeof this.stroke.unbind === "function") {
91869232
this.stroke.unbind();
91879233
}
91889234
return this;
@@ -15809,10 +15855,10 @@ var _Two = class _Two {
1580915855
if (typeof obj.unbind === "function") {
1581015856
obj.unbind();
1581115857
}
15812-
if (typeof obj.fill === "object" && "unbind" in obj.fill) {
15858+
if (typeof obj.fill === "object" && typeof obj.fill.unbind === "function") {
1581315859
obj.fill.unbind();
1581415860
}
15815-
if (typeof obj.stroke === "object" && "unbind" in obj.stroke) {
15861+
if (typeof obj.stroke === "object" && typeof obj.stroke.unbind === "function") {
1581615862
obj.stroke.unbind();
1581715863
}
1581815864
if (obj.vertices) {

src/element.js

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -113,17 +113,53 @@ export class Element extends Events {
113113
* This cleans up renderer-specific resources and unbinds all event listeners.
114114
*/
115115
dispose() {
116+
// Unbind all events
117+
if (typeof this.unbind === 'function') {
118+
this.unbind();
119+
}
120+
121+
// Clean up renderer-specific resources
122+
if (this._renderer) {
123+
// SVG DOM element cleanup
124+
if (this._renderer.elem && this._renderer.elem.parentNode) {
125+
this._renderer.elem.parentNode.removeChild(this._renderer.elem);
126+
delete this._renderer.elem;
127+
}
128+
129+
// WebGL resource cleanup
130+
if (this.type === 'WebGLRenderer' && this.renderer.ctx) {
131+
const gl = this.renderer.ctx;
132+
133+
// Clean up textures
134+
if (this._renderer.texture) {
135+
gl.deleteTexture(this._renderer.texture);
136+
delete this._renderer.texture;
137+
}
138+
139+
// Clean up buffers
140+
if (this._renderer.positionBuffer) {
141+
gl.deleteBuffer(this._renderer.positionBuffer);
142+
delete this._renderer.positionBuffer;
143+
}
144+
145+
// Clean up any other WebGL effects
146+
if (this._renderer.effect) {
147+
this._renderer.effect = null;
148+
}
149+
}
150+
151+
// Canvas renderer cleanup - clear cached contexts and data
152+
if (this.type === 'CanvasRenderer' && this._renderer.context) {
153+
delete this._renderer.context;
154+
}
155+
}
156+
116157
// Preserve the renderer type for potential re-attachment
117158
const rendererType = this._renderer.type;
118159

119160
// Clear renderer object but preserve type
120161
this._renderer = { type: rendererType };
121162

122-
// Unbind all events
123-
if (typeof this.unbind === 'function') {
124-
this.unbind();
125-
}
126-
127163
return this;
128164
}
129165
}

0 commit comments

Comments
 (0)