Merge branch 'zarv1k-release-canvas' into master

This commit is contained in:
Anton Lavrenov 2022-11-09 13:27:05 -05:00
commit 4a273df683
12 changed files with 89 additions and 11 deletions

View File

@ -8,7 +8,7 @@
* Konva JavaScript Framework v8.3.13 * Konva JavaScript Framework v8.3.13
* http://konvajs.org/ * http://konvajs.org/
* Licensed under the MIT * Licensed under the MIT
* Date: Mon Oct 03 2022 * Date: Wed Nov 09 2022
* *
* Original work Copyright (C) 2011 - 2013 by Eric Rowell (KineticJS) * Original work Copyright (C) 2011 - 2013 by Eric Rowell (KineticJS)
* Modified work Copyright (C) 2014 - present by Anton Lavrenov (Konva) * Modified work Copyright (C) 2014 - present by Anton Lavrenov (Konva)
@ -157,6 +157,17 @@
isDragReady() { isDragReady() {
return !!Konva$2['DD'].node; return !!Konva$2['DD'].node;
}, },
/**
* Should Konva release canvas elements on destroy. Default is true.
* Useful to avoid memory leak issues in Safari on macOS/iOS.
* @property releaseCanvasOnDestroy
* @default true
* @name releaseCanvasOnDestroy
* @memberof Konva
* @example
* Konva.releaseCanvasOnDestroy = true;
*/
releaseCanvasOnDestroy: true,
// user agent // user agent
document: glob.document, document: glob.document,
// insert Konva into global namespace (window) // insert Konva into global namespace (window)
@ -1071,6 +1082,14 @@
return evt.changedTouches[0].identifier; return evt.changedTouches[0].identifier;
} }
}, },
releaseCanvas(...canvases) {
if (!Konva$2.releaseCanvasOnDestroy)
return;
canvases.forEach(c => {
c.width = 0;
c.height = 0;
});
}
}; };
function _formatValue(val) { function _formatValue(val) {
@ -2164,6 +2183,7 @@
1; 1;
return devicePixelRatio / backingStoreRatio; return devicePixelRatio / backingStoreRatio;
})(); })();
Util.releaseCanvas(canvas);
return _pixelRatio; return _pixelRatio;
} }
/** /**
@ -2556,7 +2576,11 @@
* node.clearCache(); * node.clearCache();
*/ */
clearCache() { clearCache() {
this._cache.delete(CANVAS); if (this._cache.has(CANVAS)) {
const { scene, filter, hit } = this._cache.get(CANVAS);
Util.releaseCanvas(scene, filter, hit);
this._cache.delete(CANVAS);
}
this._clearSelfAndDescendantCache(); this._clearSelfAndDescendantCache();
this._requestDraw(); this._requestDraw();
return this; return this;
@ -3042,6 +3066,7 @@
*/ */
destroy() { destroy() {
this.remove(); this.remove();
this.clearCache();
return this; return this;
} }
/** /**
@ -5997,6 +6022,7 @@
if (index > -1) { if (index > -1) {
stages.splice(index, 1); stages.splice(index, 1);
} }
Util.releaseCanvas(this.bufferCanvas._canvas, this.bufferHitCanvas._canvas);
return this; return this;
} }
/** /**
@ -8620,6 +8646,10 @@
parent.content.appendChild(this.hitCanvas._canvas); parent.content.appendChild(this.hitCanvas._canvas);
} }
} }
destroy() {
Util.releaseCanvas(this.getNativeCanvasElement(), this.getHitCanvas()._canvas);
return super.destroy();
}
} }
Layer.prototype.nodeType = 'Layer'; Layer.prototype.nodeType = 'Layer';
_registerNode(Layer); _registerNode(Layer);
@ -14407,6 +14437,10 @@
height: maxY - minY + fontSize, height: maxY - minY + fontSize,
}; };
} }
destroy() {
Util.releaseCanvas(this.dummyCanvas);
return super.destroy();
}
} }
TextPath.prototype._fillFunc = _fillFunc; TextPath.prototype._fillFunc = _fillFunc;
TextPath.prototype._strokeFunc = _strokeFunc; TextPath.prototype._strokeFunc = _strokeFunc;
@ -17593,6 +17627,7 @@
var scratchData = tempCanvas var scratchData = tempCanvas
.getContext('2d') .getContext('2d')
.getImageData(0, 0, xSize, ySize); .getImageData(0, 0, xSize, ySize);
Util.releaseCanvas(tempCanvas);
// Convert thhe original to polar coordinates // Convert thhe original to polar coordinates
ToPolar(imageData, scratchData, { ToPolar(imageData, scratchData, {
polarCenterX: xSize / 2, polarCenterX: xSize / 2,

4
konva.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -23,6 +23,7 @@ function getDevicePixelRatio() {
1; 1;
return devicePixelRatio / backingStoreRatio; return devicePixelRatio / backingStoreRatio;
})(); })();
Util.releaseCanvas(canvas);
return _pixelRatio; return _pixelRatio;
} }

View File

@ -86,7 +86,7 @@ var CONTEXT_PROPERTIES = [
'globalAlpha', 'globalAlpha',
'globalCompositeOperation', 'globalCompositeOperation',
'imageSmoothingEnabled', 'imageSmoothingEnabled',
]; ] as const;
const traceArrMax = 100; const traceArrMax = 100;
/** /**
@ -701,6 +701,11 @@ export class Context {
} }
} }
// supported context properties
type CanvasContextProps = Pick<CanvasRenderingContext2D, typeof CONTEXT_PROPERTIES[number]>;
export interface Context extends CanvasContextProps {};
CONTEXT_PROPERTIES.forEach(function (prop) { CONTEXT_PROPERTIES.forEach(function (prop) {
Object.defineProperty(Context.prototype, prop, { Object.defineProperty(Context.prototype, prop, {
get() { get() {

View File

@ -164,6 +164,17 @@ export const Konva = {
isDragReady() { isDragReady() {
return !!Konva['DD'].node; return !!Konva['DD'].node;
}, },
/**
* Should Konva release canvas elements on destroy. Default is true.
* Useful to avoid memory leak issues in Safari on macOS/iOS.
* @property releaseCanvasOnDestroy
* @default true
* @name releaseCanvasOnDestroy
* @memberof Konva
* @example
* Konva.releaseCanvasOnDestroy = true;
*/
releaseCanvasOnDestroy: true,
// user agent // user agent
document: glob.document, document: glob.document,
// insert Konva into global namespace (window) // insert Konva into global namespace (window)

View File

@ -468,6 +468,11 @@ export class Layer extends Container<Group | Shape> {
} }
} }
destroy(): this {
Util.releaseCanvas(this.getNativeCanvasElement(), this.getHitCanvas()._canvas);
return super.destroy();
}
hitGraphEnabled: GetSet<boolean, this>; hitGraphEnabled: GetSet<boolean, this>;
clearBeforeDraw: GetSet<boolean, this>; clearBeforeDraw: GetSet<boolean, this>;

View File

@ -243,7 +243,12 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
* node.clearCache(); * node.clearCache();
*/ */
clearCache() { clearCache() {
this._cache.delete(CANVAS); if (this._cache.has(CANVAS)) {
const { scene, filter, hit } = this._cache.get(CANVAS);
Util.releaseCanvas(scene, filter, hit);
this._cache.delete(CANVAS);
}
this._clearSelfAndDescendantCache(); this._clearSelfAndDescendantCache();
this._requestDraw(); this._requestDraw();
return this; return this;
@ -853,6 +858,7 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
*/ */
destroy() { destroy() {
this.remove(); this.remove();
this.clearCache();
return this; return this;
} }
/** /**

View File

@ -278,6 +278,9 @@ export class Stage extends Container<Layer> {
if (index > -1) { if (index > -1) {
stages.splice(index, 1); stages.splice(index, 1);
} }
Util.releaseCanvas(this.bufferCanvas._canvas, this.bufferHitCanvas._canvas)
return this; return this;
} }
/** /**

View File

@ -957,4 +957,12 @@ export const Util = {
return evt.changedTouches[0].identifier; return evt.changedTouches[0].identifier;
} }
}, },
releaseCanvas(...canvases: HTMLCanvasElement[]) {
if (!Konva.releaseCanvasOnDestroy) return;
canvases.forEach(c => {
c.width = 0;
c.height = 0;
})
}
}; };

View File

@ -197,7 +197,7 @@ export const Kaleidoscope: Filter = function (imageData) {
var scratchData = tempCanvas var scratchData = tempCanvas
.getContext('2d') .getContext('2d')
.getImageData(0, 0, xSize, ySize); .getImageData(0, 0, xSize, ySize);
Util.releaseCanvas(tempCanvas);
// Convert thhe original to polar coordinates // Convert thhe original to polar coordinates
ToPolar(imageData, scratchData, { ToPolar(imageData, scratchData, {
polarCenterX: xSize / 2, polarCenterX: xSize / 2,

View File

@ -533,6 +533,10 @@ export class TextPath extends Shape<TextPathConfig> {
height: maxY - minY + fontSize, height: maxY - minY + fontSize,
}; };
} }
destroy(): this {
Util.releaseCanvas(this.dummyCanvas);
return super.destroy();
}
fontFamily: GetSet<string, this>; fontFamily: GetSet<string, this>;
fontSize: GetSet<number, this>; fontSize: GetSet<number, this>;

View File

@ -57,7 +57,7 @@ describe('Context', function () {
'textBaseline', 'textBaseline',
'globalAlpha', 'globalAlpha',
'globalCompositeOperation', 'globalCompositeOperation',
]; ] as const;
it('context wrapper should work like native context', function () { it('context wrapper should work like native context', function () {
var stage = addStage(); var stage = addStage();
@ -108,10 +108,10 @@ describe('Context', function () {
// test get // test get
nativeContext.fillStyle = '#ff0000'; nativeContext.fillStyle = '#ff0000';
assert.equal(context['fillStyle'], '#ff0000'); assert.equal(context.fillStyle, '#ff0000');
// test set // test set
context['globalAlpha'] = 0.5; context.globalAlpha = 0.5;
assert.equal(context['globalAlpha'], 0.5); assert.equal(context.globalAlpha, 0.5);
}); });
}); });