performance optimizations

This commit is contained in:
Anton Lavrenov
2020-06-10 11:57:48 -05:00
parent 43b23e9559
commit 1d8388eead
12 changed files with 184 additions and 112 deletions

View File

@@ -344,7 +344,8 @@ export abstract class Container<ChildType extends Node> extends Node<
if (cachedSceneCanvas) {
context.save();
layer._applyTransform(this, context, top);
var m = this.getAbsoluteTransform(top).getMatrix();
context.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
this._drawCachedSceneCanvas(context);
context.restore();
} else {
@@ -365,7 +366,8 @@ export abstract class Container<ChildType extends Node> extends Node<
if (cachedHitCanvas) {
context.save();
layer._applyTransform(this, context, top);
var m = this.getAbsoluteTransform(top).getMatrix();
context.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
this._drawCachedHitCanvas(context);
context.restore();
} else {
@@ -373,10 +375,8 @@ export abstract class Container<ChildType extends Node> extends Node<
}
return this;
}
// TODO: create ClipContainer
_drawChildren(drawMethod, canvas, top) {
var layer = this.getLayer(),
context = canvas && canvas.getContext(),
var context = canvas && canvas.getContext(),
clipWidth = this.clipWidth(),
clipHeight = this.clipHeight(),
clipFunc = this.clipFunc(),

View File

@@ -310,15 +310,6 @@ export abstract class Layer extends Container<Group | Shape> {
return this;
}
// the apply transform method is handled by the Layer and FastLayer class
// because it is up to the layer to decide if an absolute or relative transform
// should be used
// TODO: remove that method
_applyTransform(shape, context, top) {
var m = shape.getAbsoluteTransform(top).getMatrix();
context.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
}
/**
* get visible intersection shape. This is the preferred
* method for determining if a point intersects a shape or not

View File

@@ -241,9 +241,17 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
return emptyChildren;
}
/** @lends Konva.Node.prototype */
_clearCache(attr?: string) {
if (attr) {
// if we want to clear transform cache
// we don't really need to remove it from the cache
// but instead mark as "dirty"
// so we don't need to create a new instance next time
if (
(attr === TRANSFORM || attr === ABSOLUTE_TRANSFORM) &&
this._cache.get(attr)
) {
(this._cache.get(attr) as Transform).dirty = true;
} else if (attr) {
this._cache.delete(attr);
} else {
this._cache.clear();
@@ -252,8 +260,13 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
_getCache(attr: string, privateGetter: Function) {
var cache = this._cache.get(attr);
// for transform the cache can be NOT empty
// but we still need to recalculate it if it is dirty
var isTransform = attr === TRANSFORM || attr === ABSOLUTE_TRANSFORM;
var invalid = cache === undefined || (isTransform && cache.dirty === true);
// if not cached, we need to set it using the private getter method.
if (cache === undefined) {
if (invalid) {
cache = privateGetter.call(this);
this._cache.set(attr, cache);
}
@@ -1690,11 +1703,12 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
return at;
} else {
// try to use a cached value
at = this._cache.get(ABSOLUTE_TRANSFORM) || new Transform();
if (this.parent) {
// transform will be cached
at = this.parent.getAbsoluteTransform().copy();
this.parent.getAbsoluteTransform().copyInto(at);
} else {
at = new Transform();
at.reset();
}
var transformsEnabled = this.transformsEnabled();
if (transformsEnabled === 'all') {
@@ -1702,6 +1716,7 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
} else if (transformsEnabled === 'position') {
at.translate(this.x() - this.offsetX(), this.y() - this.offsetY());
}
at.dirty = false;
return at;
}
}
@@ -1766,8 +1781,10 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
return this._getCache(TRANSFORM, this._getTransform) as Transform;
}
_getTransform(): Transform {
var m = new Transform(),
x = this.x(),
var m: Transform = this._cache.get(TRANSFORM) || new Transform();
m.reset();
var x = this.x(),
y = this.y(),
rotation = Konva.getAngle(this.rotation()),
scaleX = this.scaleX(),
@@ -1793,6 +1810,8 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
m.translate(-1 * offsetX, -1 * offsetY);
}
m.dirty = false;
return m;
}
/**

View File

@@ -567,7 +567,9 @@ export class Shape<Config extends ShapeConfig = ShapeConfig> extends Node<
// if node is cached we just need to draw from cache
if (cachedCanvas) {
context.save();
layer._applyTransform(this, context, top);
var m = this.getAbsoluteTransform(top).getMatrix();
context.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
this._drawCachedSceneCanvas(context);
context.restore();
return this;
@@ -647,7 +649,10 @@ export class Shape<Config extends ShapeConfig = ShapeConfig> extends Node<
if (cachedHitCanvas) {
context.save();
layer._applyTransform(this, context, top);
var m = this.getAbsoluteTransform(top).getMatrix();
context.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
this._drawCachedHitCanvas(context);
context.restore();
return this;
@@ -874,7 +879,6 @@ Factory.addGetterSetter(
* shape.hitStrokeWidth('auto');
*/
// TODO: probably we should deprecate it
Factory.addGetterSetter(Shape, 'strokeHitEnabled', true, getBooleanValidator());
/**

View File

@@ -49,7 +49,7 @@ export class Collection<Child extends Node> {
}
static _mapMethod(methodName: any) {
Collection.prototype[methodName] = function() {
Collection.prototype[methodName] = function () {
var len = this.length,
i;
@@ -62,7 +62,7 @@ export class Collection<Child extends Node> {
};
}
static mapMethods = function(constructor: Function) {
static mapMethods = function (constructor: Function) {
var prot = constructor.prototype;
for (var methodName in prot) {
Collection._mapMethod(methodName);
@@ -83,7 +83,7 @@ Collection.prototype = [] as any;
* shape.setX(10);
* });
*/
Collection.prototype.each = function(func) {
Collection.prototype.each = function (func) {
for (var n = 0; n < this.length; n++) {
func(this[n], n);
}
@@ -93,7 +93,7 @@ Collection.prototype.each = function(func) {
* @method
* @name Konva.Collection#toArray
*/
Collection.prototype.toArray = function() {
Collection.prototype.toArray = function () {
var arr = [],
len = this.length,
n;
@@ -130,9 +130,18 @@ Collection.prototype.toArray = function() {
*/
export class Transform {
m: Array<number>;
dirty = false;
constructor(m = [1, 0, 0, 1, 0, 0]) {
this.m = (m && m.slice()) || [1, 0, 0, 1, 0, 0];
}
reset() {
this.m[0] = 1;
this.m[1] = 0;
this.m[2] = 0;
this.m[3] = 1;
this.m[4] = 0;
this.m[5] = 0;
}
/**
* Copy Konva.Transform object
* @method
@@ -144,6 +153,14 @@ export class Transform {
copy() {
return new Transform(this.m);
}
copyInto(tr: Transform) {
tr.m[0] = this.m[0];
tr.m[1] = this.m[1];
tr.m[2] = this.m[2];
tr.m[3] = this.m[3];
tr.m[4] = this.m[4];
tr.m[5] = this.m[5];
}
/**
* Transform point
* @method
@@ -155,7 +172,7 @@ export class Transform {
var m = this.m;
return {
x: m[0] * point.x + m[2] * point.y + m[4],
y: m[1] * point.x + m[3] * point.y + m[5]
y: m[1] * point.x + m[3] * point.y + m[5],
};
}
/**
@@ -215,7 +232,7 @@ export class Transform {
getTranslation() {
return {
x: this.m[4],
y: this.m[5]
y: this.m[5],
};
}
/**
@@ -334,7 +351,7 @@ export class Transform {
scaleX: 0,
scaleY: 0,
skewX: 0,
skewY: 0
skewY: 0,
};
// Apply the QR-like decomposition.
@@ -525,7 +542,7 @@ var OBJECT_ARRAY = '[object Array]',
white: [255, 255, 255],
whitesmoke: [245, 245, 245],
yellow: [255, 255, 0],
yellowgreen: [154, 205, 5]
yellowgreen: [154, 205, 5],
},
RGB_REGEX = /rgb\((\d{1,3}),(\d{1,3}),(\d{1,3})\)/,
animQueue: Array<Function> = [];
@@ -592,10 +609,10 @@ export const Util = {
requestAnimFrame(callback: Function) {
animQueue.push(callback);
if (animQueue.length === 1) {
requestAnimationFrame(function() {
requestAnimationFrame(function () {
const queue = animQueue;
animQueue = [];
queue.forEach(function(cb) {
queue.forEach(function (cb) {
cb();
});
});
@@ -646,7 +663,7 @@ export const Util = {
_urlToImage(url: string, callback: Function) {
// if arg is a string, then it's a data url
var imageObj = new glob.Image();
imageObj.onload = function() {
imageObj.onload = function () {
callback(imageObj);
};
imageObj.src = url;
@@ -660,7 +677,7 @@ export const Util = {
return {
r: (bigint >> 16) & 255,
g: (bigint >> 8) & 255,
b: bigint & 255
b: bigint & 255,
};
},
/**
@@ -704,7 +721,7 @@ export const Util = {
return {
r: rgb[0],
g: rgb[1],
b: rgb[2]
b: rgb[2],
};
} else if (color[0] === HASH) {
// hex
@@ -715,14 +732,14 @@ export const Util = {
return {
r: parseInt(rgb[1], 10),
g: parseInt(rgb[2], 10),
b: parseInt(rgb[3], 10)
b: parseInt(rgb[3], 10),
};
} else {
// default
return {
r: 0,
g: 0,
b: 0
b: 0,
};
}
},
@@ -749,7 +766,7 @@ export const Util = {
r: c[0],
g: c[1],
b: c[2],
a: 1
a: 1,
};
},
// Parse rgb(n, n, n)
@@ -761,7 +778,7 @@ export const Util = {
r: parts[0],
g: parts[1],
b: parts[2],
a: 1
a: 1,
};
}
},
@@ -774,7 +791,7 @@ export const Util = {
r: parts[0],
g: parts[1],
b: parts[2],
a: parts[3]
a: parts[3],
};
}
},
@@ -785,7 +802,7 @@ export const Util = {
r: parseInt(str.slice(1, 3), 16),
g: parseInt(str.slice(3, 5), 16),
b: parseInt(str.slice(5, 7), 16),
a: 1
a: 1,
};
}
},
@@ -796,7 +813,7 @@ export const Util = {
r: parseInt(str[1] + str[1], 16),
g: parseInt(str[2] + str[2], 16),
b: parseInt(str[3] + str[3], 16),
a: 1
a: 1,
};
}
},
@@ -821,7 +838,7 @@ export const Util = {
r: Math.round(val),
g: Math.round(val),
b: Math.round(val),
a: 1
a: 1,
};
}
@@ -861,7 +878,7 @@ export const Util = {
r: Math.round(rgb[0]),
g: Math.round(rgb[1]),
b: Math.round(rgb[2]),
a: 1
a: 1,
};
}
},
@@ -1016,7 +1033,7 @@ export const Util = {
_getProjectionToLine(pt: Vector2d, line, isClosed) {
var pc = Util.cloneObject(pt);
var dist = Number.MAX_VALUE;
line.forEach(function(p1, i) {
line.forEach(function (p1, i) {
if (!isClosed && i === line.length - 1) {
return;
}
@@ -1052,18 +1069,18 @@ export const Util = {
for (n = 0; n < startArray.length; n += 2) {
start.push({
x: startArray[n],
y: startArray[n + 1]
y: startArray[n + 1],
});
}
for (n = 0; n < endArray.length; n += 2) {
end.push({
x: endArray[n],
y: endArray[n + 1]
y: endArray[n + 1],
});
}
var newStart = [];
end.forEach(function(point) {
end.forEach(function (point) {
var pr = Util._getProjectionToLine(point, start, isClosed);
newStart.push(pr.x);
newStart.push(pr.y);
@@ -1118,5 +1135,5 @@ export const Util = {
} else {
return evt.changedTouches[0].identifier;
}
}
},
};

View File

@@ -1531,7 +1531,6 @@ Factory.addGetterSetter(Transformer, 'ignoreStroke', false);
*/
Factory.addGetterSetter(Transformer, 'padding', 0, getNumberValidator());
// TODO: that property is deprecated
Factory.addGetterSetter(Transformer, 'node');
/**