performance updates

This commit is contained in:
Anton Lavrenov
2020-07-30 10:44:15 -05:00
parent 2dbde0fd6f
commit dd84716715
7 changed files with 277 additions and 91 deletions

View File

@@ -6,6 +6,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
## Unreleased ## Unreleased
* Add `onUpdate` callbacks to `Konva.Tween` configuration and `node.to()` method. * Add `onUpdate` callbacks to `Konva.Tween` configuration and `node.to()` method.
* Up to 6x faster initializations of objects, like `const shape = new Konva.Shape()`.
## 7.0.3 - 2020-07-09 ## 7.0.3 - 2020-07-09

118
konva.js
View File

@@ -8,7 +8,7 @@
* Konva JavaScript Framework v7.0.3 * Konva JavaScript Framework v7.0.3
* http://konvajs.org/ * http://konvajs.org/
* Licensed under the MIT * Licensed under the MIT
* Date: Thu Jul 09 2020 * Date: Thu Jul 30 2020
* *
* 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)
@@ -2641,7 +2641,6 @@
*/ */
var Node = /** @class */ (function () { var Node = /** @class */ (function () {
function Node(config) { function Node(config) {
var _this = this;
this._id = idCounter++; this._id = idCounter++;
this.eventListeners = {}; this.eventListeners = {};
this.attrs = {}; this.attrs = {};
@@ -2656,25 +2655,12 @@
this._isUnderCache = false; this._isUnderCache = false;
this.children = emptyChildren; this.children = emptyChildren;
this._dragEventId = null; this._dragEventId = null;
this._shouldFireChangeEvents = false;
// on initial set attrs wi don't need to fire change events
// because nobody is listening to them yet
this.setAttrs(config); this.setAttrs(config);
// event bindings for cache handling this._shouldFireChangeEvents = true;
this.on(TRANSFORM_CHANGE_STR, function () { // all change event listeners are attached to the prototype
if (_this._batchingTransformChange) {
_this._needClearTransformCache = true;
return;
}
_this._clearCache(TRANSFORM);
_this._clearSelfAndDescendantCache(ABSOLUTE_TRANSFORM);
});
this.on('visibleChange.konva', function () {
_this._clearSelfAndDescendantCache(VISIBLE);
});
this.on('listeningChange.konva', function () {
_this._clearSelfAndDescendantCache(LISTENING);
});
this.on('opacityChange.konva', function () {
_this._clearSelfAndDescendantCache(ABSOLUTE_OPACITY);
});
} }
Node.prototype.hasChildren = function () { Node.prototype.hasChildren = function () {
return false; return false;
@@ -4425,7 +4411,7 @@
} }
return this; return this;
}; };
Node.prototype._setAttr = function (key, val) { Node.prototype._setAttr = function (key, val, skipFire) {
var oldVal = this.attrs[key]; var oldVal = this.attrs[key];
if (oldVal === val && !Util.isObject(val)) { if (oldVal === val && !Util.isObject(val)) {
return; return;
@@ -4436,7 +4422,9 @@
else { else {
this.attrs[key] = val; this.attrs[key] = val;
} }
this._fireChangeEvent(key, oldVal, val); if (this._shouldFireChangeEvents) {
this._fireChangeEvent(key, oldVal, val);
}
}; };
Node.prototype._setComponentAttr = function (key, component, val) { Node.prototype._setComponentAttr = function (key, component, val) {
var oldVal; var oldVal;
@@ -4480,9 +4468,29 @@
} }
} }
}; };
Node.prototype._getListeners = function (eventType) {
var totalEvents = [];
var obj;
while (true) {
obj = obj ? Object.getPrototypeOf(obj) : this;
if (!obj) {
break;
}
if (!obj.eventListeners) {
continue;
}
var events = obj.eventListeners[eventType];
if (!events) {
continue;
}
totalEvents = events.concat(totalEvents);
obj = Object.getPrototypeOf(obj);
}
return totalEvents;
};
Node.prototype._fire = function (eventType, evt) { Node.prototype._fire = function (eventType, evt) {
var events = this.eventListeners[eventType], i; var events = this._getListeners(eventType), i;
if (events) { if (events.length) {
evt = evt || {}; evt = evt || {};
evt.currentTarget = this; evt.currentTarget = this;
evt.type = eventType; evt.type = eventType;
@@ -4699,6 +4707,26 @@
}()); }());
Node.prototype.nodeType = 'Node'; Node.prototype.nodeType = 'Node';
Node.prototype._attrsAffectingSize = []; Node.prototype._attrsAffectingSize = [];
// attache events listeners once into prototype
// that way we don't spend too much time on making an new instance
Node.prototype.eventListeners = {};
Node.prototype.on.call(Node.prototype, TRANSFORM_CHANGE_STR, function () {
if (this._batchingTransformChange) {
this._needClearTransformCache = true;
return;
}
this._clearCache(TRANSFORM);
this._clearSelfAndDescendantCache(ABSOLUTE_TRANSFORM);
});
Node.prototype.on.call(Node.prototype, 'visibleChange.konva', function () {
this._clearSelfAndDescendantCache(VISIBLE);
});
Node.prototype.on.call(Node.prototype, 'listeningChange.konva', function () {
this._clearSelfAndDescendantCache(LISTENING);
});
Node.prototype.on.call(Node.prototype, 'opacityChange.konva', function () {
this._clearSelfAndDescendantCache(ABSOLUTE_OPACITY);
});
var addGetterSetter = Factory.addGetterSetter; var addGetterSetter = Factory.addGetterSetter;
/** /**
* get/set zIndex relative to the node's siblings who share the same parent. * get/set zIndex relative to the node's siblings who share the same parent.
@@ -6881,11 +6909,6 @@
} }
_this.colorKey = key; _this.colorKey = key;
shapes[key] = _this; shapes[key] = _this;
_this.on('shadowColorChange.konva shadowBlurChange.konva shadowOffsetChange.konva shadowOpacityChange.konva shadowEnabledChange.konva', _clearHasShadowCache);
_this.on('shadowColorChange.konva shadowOpacityChange.konva shadowEnabledChange.konva', _clearGetShadowRGBACache);
_this.on('fillPriorityChange.konva fillPatternImageChange.konva fillPatternRepeatChange.konva fillPatternScaleXChange.konva fillPatternScaleYChange.konva', _clearFillPatternCache);
_this.on('fillPriorityChange.konva fillLinearGradientColorStopsChange.konva fillLinearGradientStartPointXChange.konva fillLinearGradientStartPointYChange.konva fillLinearGradientEndPointXChange.konva fillLinearGradientEndPointYChange.konva', _clearLinearGradientCache);
_this.on('fillPriorityChange.konva fillRadialGradientColorStopsChange.konva fillRadialGradientStartPointXChange.konva fillRadialGradientStartPointYChange.konva fillRadialGradientEndPointXChange.konva fillRadialGradientEndPointYChange.konva fillRadialGradientStartRadiusChange.konva fillRadialGradientEndRadiusChange.konva', _clearRadialGradientCache);
return _this; return _this;
} }
/** /**
@@ -7343,6 +7366,12 @@
Shape.prototype._centroid = false; Shape.prototype._centroid = false;
Shape.prototype.nodeType = 'Shape'; Shape.prototype.nodeType = 'Shape';
_registerNode(Shape); _registerNode(Shape);
Shape.prototype.eventListeners = {};
Shape.prototype.on.call(Shape.prototype, 'shadowColorChange.konva shadowBlurChange.konva shadowOffsetChange.konva shadowOpacityChange.konva shadowEnabledChange.konva', _clearHasShadowCache);
Shape.prototype.on.call(Shape.prototype, 'shadowColorChange.konva shadowOpacityChange.konva shadowEnabledChange.konva', _clearGetShadowRGBACache);
Shape.prototype.on.call(Shape.prototype, 'fillPriorityChange.konva fillPatternImageChange.konva fillPatternRepeatChange.konva fillPatternScaleXChange.konva fillPatternScaleYChange.konva', _clearFillPatternCache);
Shape.prototype.on.call(Shape.prototype, 'fillPriorityChange.konva fillLinearGradientColorStopsChange.konva fillLinearGradientStartPointXChange.konva fillLinearGradientStartPointYChange.konva fillLinearGradientEndPointXChange.konva fillLinearGradientEndPointYChange.konva', _clearLinearGradientCache);
Shape.prototype.on.call(Shape.prototype, 'fillPriorityChange.konva fillRadialGradientColorStopsChange.konva fillRadialGradientStartPointXChange.konva fillRadialGradientStartPointYChange.konva fillRadialGradientEndPointXChange.konva fillRadialGradientEndPointYChange.konva fillRadialGradientStartRadiusChange.konva fillRadialGradientEndRadiusChange.konva', _clearRadialGradientCache);
// add getters and setters // add getters and setters
Factory.addGetterSetter(Shape, 'stroke', undefined, getStringValidator()); Factory.addGetterSetter(Shape, 'stroke', undefined, getStringValidator());
/** /**
@@ -9137,7 +9166,7 @@
duration: 1, duration: 1,
easing: 1, easing: 1,
onFinish: 1, onFinish: 1,
yoyo: 1 yoyo: 1,
}, PAUSED = 1, PLAYING = 2, REVERSING = 3, idCounter$1 = 0, colorAttrs = ['fill', 'stroke', 'shadowColor']; }, PAUSED = 1, PLAYING = 2, REVERSING = 3, idCounter$1 = 0, colorAttrs = ['fill', 'stroke', 'shadowColor'];
var TweenEngine = /** @class */ (function () { var TweenEngine = /** @class */ (function () {
function TweenEngine(prop, propFunc, func, begin, finish, duration, yoyo) { function TweenEngine(prop, propFunc, func, begin, finish, duration, yoyo) {
@@ -9234,6 +9263,7 @@
}; };
TweenEngine.prototype.update = function () { TweenEngine.prototype.update = function () {
this.setPosition(this.getPosition(this._time)); this.setPosition(this.getPosition(this._time));
this.fire('onUpdate');
}; };
TweenEngine.prototype.onEnterFrame = function () { TweenEngine.prototype.onEnterFrame = function () {
var t = this.getTimer() - this._startTime; var t = this.getTimer() - this._startTime;
@@ -9262,10 +9292,15 @@
* @example * @example
* // instantiate new tween which fully rotates a node in 1 second * // instantiate new tween which fully rotates a node in 1 second
* var tween = new Konva.Tween({ * var tween = new Konva.Tween({
* // list of tween specific properties
* node: node, * node: node,
* rotationDeg: 360,
* duration: 1, * duration: 1,
* easing: Konva.Easings.EaseInOut * easing: Konva.Easings.EaseInOut,
* onUpdate: () => console.log('node attrs updated')
* onFinish: () => console.log('finished'),
* // set new values for any attributes of a passed node
* rotation: 360,
* fill: 'red'
* }); * });
* *
* // play tween * // play tween
@@ -9321,6 +9356,7 @@
// callbacks // callbacks
this.onFinish = config.onFinish; this.onFinish = config.onFinish;
this.onReset = config.onReset; this.onReset = config.onReset;
this.onUpdate = config.onUpdate;
} }
Tween.prototype._addAttr = function (key, end) { Tween.prototype._addAttr = function (key, end) {
var node = this.node, nodeId = node._id, start, diff, tweenId, n, len, trueEnd, trueStart, endRGBA; var node = this.node, nodeId = node._id, start, diff, tweenId, n, len, trueEnd, trueStart, endRGBA;
@@ -9361,7 +9397,7 @@
r: endRGBA.r - startRGBA.r, r: endRGBA.r - startRGBA.r,
g: endRGBA.g - startRGBA.g, g: endRGBA.g - startRGBA.g,
b: endRGBA.b - startRGBA.b, b: endRGBA.b - startRGBA.b,
a: endRGBA.a - startRGBA.a a: endRGBA.a - startRGBA.a,
}); });
} }
} }
@@ -9379,7 +9415,7 @@
r: endRGBA.r - start.r, r: endRGBA.r - start.r,
g: endRGBA.g - start.g, g: endRGBA.g - start.g,
b: endRGBA.b - start.b, b: endRGBA.b - start.b,
a: endRGBA.a - start.a a: endRGBA.a - start.a,
}; };
} }
else { else {
@@ -9390,7 +9426,7 @@
diff: diff, diff: diff,
end: end, end: end,
trueEnd: trueEnd, trueEnd: trueEnd,
trueStart: trueStart trueStart: trueStart,
}; };
Tween.tweens[nodeId][key] = this._id; Tween.tweens[nodeId][key] = this._id;
}; };
@@ -9481,6 +9517,11 @@
_this.onReset(); _this.onReset();
} }
}; };
this.tween.onUpdate = function () {
if (_this.onUpdate) {
_this.onUpdate.call(_this);
}
};
}; };
/** /**
* play * play
@@ -9570,9 +9611,8 @@
* circle.to({ * circle.to({
* x : 50, * x : 50,
* duration : 0.5, * duration : 0.5,
* onFinish: () => { * onUpdate: () => console.log('props updated'),
* console.log('finished'); * onFinish: () => console.log('finished'),
* }
* }); * });
*/ */
Node.prototype.to = function (params) { Node.prototype.to = function (params) {
@@ -9821,7 +9861,7 @@
*/ */
Linear: function (t, b, c, d) { Linear: function (t, b, c, d) {
return (c * t) / d + b; return (c * t) / d + b;
} },
}; };
// what is core parts of Konva? // what is core parts of Konva?

4
konva.min.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -208,30 +208,17 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
children = emptyChildren; children = emptyChildren;
nodeType!: string; nodeType!: string;
className!: string; className!: string;
_dragEventId: number | null = null; _dragEventId: number | null = null;
_shouldFireChangeEvents = false;
constructor(config?: Config) { constructor(config?: Config) {
// on initial set attrs wi don't need to fire change events
// because nobody is listening to them yet
this.setAttrs(config); this.setAttrs(config);
this._shouldFireChangeEvents = true;
// event bindings for cache handling // all change event listeners are attached to the prototype
this.on(TRANSFORM_CHANGE_STR, () => {
if (this._batchingTransformChange) {
this._needClearTransformCache = true;
return;
}
this._clearCache(TRANSFORM);
this._clearSelfAndDescendantCache(ABSOLUTE_TRANSFORM);
});
this.on('visibleChange.konva', () => {
this._clearSelfAndDescendantCache(VISIBLE);
});
this.on('listeningChange.konva', () => {
this._clearSelfAndDescendantCache(LISTENING);
});
this.on('opacityChange.konva', () => {
this._clearSelfAndDescendantCache(ABSOLUTE_OPACITY);
});
} }
hasChildren() { hasChildren() {
@@ -2218,7 +2205,7 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
} }
return this; return this;
} }
_setAttr(key, val) { _setAttr(key, val, skipFire = false) {
var oldVal = this.attrs[key]; var oldVal = this.attrs[key];
if (oldVal === val && !Util.isObject(val)) { if (oldVal === val && !Util.isObject(val)) {
return; return;
@@ -2228,7 +2215,9 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
} else { } else {
this.attrs[key] = val; this.attrs[key] = val;
} }
this._fireChangeEvent(key, oldVal, val); if (this._shouldFireChangeEvents) {
this._fireChangeEvent(key, oldVal, val);
}
} }
_setComponentAttr(key, component, val) { _setComponentAttr(key, component, val) {
var oldVal; var oldVal;
@@ -2280,11 +2269,32 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
} }
} }
} }
_getListeners(eventType) {
let totalEvents = [];
let obj;
while (true) {
obj = obj ? Object.getPrototypeOf(obj) : this;
if (!obj) {
break;
}
if (!obj.eventListeners) {
continue;
}
const events = obj.eventListeners[eventType];
if (!events) {
continue;
}
totalEvents = events.concat(totalEvents);
obj = Object.getPrototypeOf(obj);
}
return totalEvents;
}
_fire(eventType, evt) { _fire(eventType, evt) {
var events = this.eventListeners[eventType], var events = this._getListeners(eventType),
i; i;
if (events) { if (events.length) {
evt = evt || {}; evt = evt || {};
evt.currentTarget = this; evt.currentTarget = this;
evt.type = eventType; evt.type = eventType;
@@ -2604,6 +2614,28 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
Node.prototype.nodeType = 'Node'; Node.prototype.nodeType = 'Node';
Node.prototype._attrsAffectingSize = []; Node.prototype._attrsAffectingSize = [];
// attache events listeners once into prototype
// that way we don't spend too much time on making an new instance
Node.prototype.eventListeners = {};
Node.prototype.on.call(Node.prototype, TRANSFORM_CHANGE_STR, function () {
if (this._batchingTransformChange) {
this._needClearTransformCache = true;
return;
}
this._clearCache(TRANSFORM);
this._clearSelfAndDescendantCache(ABSOLUTE_TRANSFORM);
});
Node.prototype.on.call(Node.prototype, 'visibleChange.konva', function () {
this._clearSelfAndDescendantCache(VISIBLE);
});
Node.prototype.on.call(Node.prototype, 'listeningChange.konva', function () {
this._clearSelfAndDescendantCache(LISTENING);
});
Node.prototype.on.call(Node.prototype, 'opacityChange.konva', function () {
this._clearSelfAndDescendantCache(ABSOLUTE_OPACITY);
});
const addGetterSetter = Factory.addGetterSetter; const addGetterSetter = Factory.addGetterSetter;
/** /**

View File

@@ -182,31 +182,6 @@ export class Shape<Config extends ShapeConfig = ShapeConfig> extends Node<
this.colorKey = key; this.colorKey = key;
shapes[key] = this; shapes[key] = this;
this.on(
'shadowColorChange.konva shadowBlurChange.konva shadowOffsetChange.konva shadowOpacityChange.konva shadowEnabledChange.konva',
_clearHasShadowCache
);
this.on(
'shadowColorChange.konva shadowOpacityChange.konva shadowEnabledChange.konva',
_clearGetShadowRGBACache
);
this.on(
'fillPriorityChange.konva fillPatternImageChange.konva fillPatternRepeatChange.konva fillPatternScaleXChange.konva fillPatternScaleYChange.konva',
_clearFillPatternCache
);
this.on(
'fillPriorityChange.konva fillLinearGradientColorStopsChange.konva fillLinearGradientStartPointXChange.konva fillLinearGradientStartPointYChange.konva fillLinearGradientEndPointXChange.konva fillLinearGradientEndPointYChange.konva',
_clearLinearGradientCache
);
this.on(
'fillPriorityChange.konva fillRadialGradientColorStopsChange.konva fillRadialGradientStartPointXChange.konva fillRadialGradientStartPointYChange.konva fillRadialGradientEndPointXChange.konva fillRadialGradientEndPointYChange.konva fillRadialGradientStartRadiusChange.konva fillRadialGradientEndRadiusChange.konva',
_clearRadialGradientCache
);
} }
/** /**
@@ -842,6 +817,37 @@ Shape.prototype._centroid = false;
Shape.prototype.nodeType = 'Shape'; Shape.prototype.nodeType = 'Shape';
_registerNode(Shape); _registerNode(Shape);
Shape.prototype.eventListeners = {};
Shape.prototype.on.call(
Shape.prototype,
'shadowColorChange.konva shadowBlurChange.konva shadowOffsetChange.konva shadowOpacityChange.konva shadowEnabledChange.konva',
_clearHasShadowCache
);
Shape.prototype.on.call(
Shape.prototype,
'shadowColorChange.konva shadowOpacityChange.konva shadowEnabledChange.konva',
_clearGetShadowRGBACache
);
Shape.prototype.on.call(
Shape.prototype,
'fillPriorityChange.konva fillPatternImageChange.konva fillPatternRepeatChange.konva fillPatternScaleXChange.konva fillPatternScaleYChange.konva',
_clearFillPatternCache
);
Shape.prototype.on.call(
Shape.prototype,
'fillPriorityChange.konva fillLinearGradientColorStopsChange.konva fillLinearGradientStartPointXChange.konva fillLinearGradientStartPointYChange.konva fillLinearGradientEndPointXChange.konva fillLinearGradientEndPointYChange.konva',
_clearLinearGradientCache
);
Shape.prototype.on.call(
Shape.prototype,
'fillPriorityChange.konva fillRadialGradientColorStopsChange.konva fillRadialGradientStartPointXChange.konva fillRadialGradientStartPointYChange.konva fillRadialGradientEndPointXChange.konva fillRadialGradientEndPointYChange.konva fillRadialGradientStartRadiusChange.konva fillRadialGradientEndRadiusChange.konva',
_clearRadialGradientCache
);
// add getters and setters // add getters and setters
Factory.addGetterSetter(Shape, 'stroke', undefined, getStringValidator()); Factory.addGetterSetter(Shape, 'stroke', undefined, getStringValidator());

View File

@@ -10,8 +10,8 @@
</head> </head>
<body> <body>
<div id="container"></div> <div id="container"></div>
<script src="../../konva.js"></script> <!-- <script src="../../konva.js"></script> -->
<!-- <script src="https://unpkg.com/konva@6.0.0/konva.min.js"></script> --> <script src="https://unpkg.com/konva@7.0.3/konva.min.js"></script>
<script src="http://www.html5canvastutorials.com/lib/stats/stats.js"></script> <script src="http://www.html5canvastutorials.com/lib/stats/stats.js"></script>
<script defer="defer"> <script defer="defer">
var lastTime = 0; var lastTime = 0;

View File

@@ -0,0 +1,107 @@
<!DOCTYPE html>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
</style>
</head>
<body>
<div id="container"></div>
<script src="../../konva.js"></script>
<!-- <script src="https://unpkg.com/konva@7.0.3/konva.min.js"></script> -->
<script src="http://www.html5canvastutorials.com/lib/stats/stats.js"></script>
<script defer="defer">
var lastTime = 0;
var width = window.innerWidth;
var height = window.innerHeight;
var maxX = width - 10;
var minX = 0;
var maxY = height - 10;
var minY = 0;
var startObjectsCount = 5000;
var isAdding = false;
var count = 0;
var container;
var layer;
var stats;
var amount = 10;
var counter;
var stage = new Konva.Stage({
container: 'container',
width: width - 10,
height: height - 10,
});
layer = new Konva.Layer();
stage.add(layer);
stats = new Stats();
document.body.appendChild(stats.domElement);
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0px';
window.requestAnimationFrame(update);
counter = document.createElement('div');
counter.className = 'counter';
counter.style.position = 'absolute';
counter.style.top = '50px';
document.body.appendChild(counter);
count = startObjectsCount;
counter.innerHTML = startObjectsCount + ' BUNNIES';
container = stage;
// stage.addChild(container);
stage.on('mousedown', function () {
isAdding = true;
});
stage.on('mouseup', function () {
isAdding = false;
});
document.addEventListener('touchstart', onTouchStart, true);
document.addEventListener('touchend', onTouchEnd, true);
function onTouchStart(event) {
isAdding = true;
}
function onTouchEnd(event) {
isAdding = false;
}
function update() {
stats.begin();
layer.destroyChildren();
if (isAdding) {
// add 10 at a time :)
count += 10;
counter.innerHTML = count + ' BUNNIES';
}
for (var i = 0; i < count; i++) {
var bunny = new Konva.Rect({
x: Math.random() * width,
y: Math.random() * height,
width: 50,
height: 50,
fill: Konva.Util.getRandomColor(),
});
layer.add(bunny);
}
requestAnimationFrame(update);
stats.end();
}
</script>
</body>
</html>