mirror of
https://github.com/konvajs/konva.git
synced 2025-10-15 04:14:52 +08:00
performance optimizations
This commit is contained in:
@@ -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(),
|
||||
|
@@ -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
|
||||
|
33
src/Node.ts
33
src/Node.ts
@@ -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;
|
||||
}
|
||||
/**
|
||||
|
10
src/Shape.ts
10
src/Shape.ts
@@ -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());
|
||||
|
||||
/**
|
||||
|
71
src/Util.ts
71
src/Util.ts
@@ -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;
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
@@ -1531,7 +1531,6 @@ Factory.addGetterSetter(Transformer, 'ignoreStroke', false);
|
||||
*/
|
||||
Factory.addGetterSetter(Transformer, 'padding', 0, getNumberValidator());
|
||||
|
||||
// TODO: that property is deprecated
|
||||
Factory.addGetterSetter(Transformer, 'node');
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user