fix some transformer bugs

This commit is contained in:
Anton Lavrenov
2020-04-10 16:00:25 -05:00
parent 2423ec261b
commit 8937741c31
5 changed files with 91 additions and 128 deletions

View File

@@ -8,7 +8,7 @@
* Konva JavaScript Framework v4.2.2 * Konva JavaScript Framework v4.2.2
* http://konvajs.org/ * http://konvajs.org/
* Licensed under the MIT * Licensed under the MIT
* Date: Wed Apr 08 2020 * Date: Fri Apr 10 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)
@@ -2682,10 +2682,12 @@
* when the logic for a cached result depends on ancestor propagation, use this * when the logic for a cached result depends on ancestor propagation, use this
* method to clear self and children cache * method to clear self and children cache
*/ */
Node.prototype._clearSelfAndDescendantCache = function (attr) { Node.prototype._clearSelfAndDescendantCache = function (attr, forceEvent) {
this._clearCache(attr); this._clearCache(attr);
// trigger clear cache, so transformer can use it // trigger clear cache, so transformer can use it
this.fire('clearCache'); if (forceEvent) {
this.fire('clearCache');
}
// skip clearing if node is cached with canvas // skip clearing if node is cached with canvas
// for performance reasons !!! // for performance reasons !!!
if (this.isCached()) { if (this.isCached()) {
@@ -2693,7 +2695,7 @@
} }
if (this.children) { if (this.children) {
this.children.each(function (node) { this.children.each(function (node) {
node._clearSelfAndDescendantCache(attr); node._clearSelfAndDescendantCache(attr, true);
}); });
} }
}; };
@@ -3474,8 +3476,8 @@
x: this.attrs.x + it.getTranslation().x, x: this.attrs.x + it.getTranslation().x,
y: this.attrs.y + it.getTranslation().y y: this.attrs.y + it.getTranslation().y
}; };
this.setPosition({ x: pos.x, y: pos.y });
this._setTransform(origTrans); this._setTransform(origTrans);
this.setPosition({ x: pos.x, y: pos.y });
return this; return this;
}; };
Node.prototype._setTransform = function (trans) { Node.prototype._setTransform = function (trans) {
@@ -14683,9 +14685,7 @@
'offsetXChange', 'offsetXChange',
'offsetYChange', 'offsetYChange',
'transformsEnabledChange', 'transformsEnabledChange',
'strokeWidthChange', 'strokeWidthChange'
// listen to cache changes
'clearCache'
] ]
.map(function (e) { return e + ("." + EVENTS_NAME); }) .map(function (e) { return e + ("." + EVENTS_NAME); })
.join(' '); .join(' ');
@@ -14966,9 +14966,6 @@
return this._nodes && this._nodes[0]; return this._nodes && this._nodes[0];
}; };
Transformer.prototype.drawScene = function (can, top, caching) { Transformer.prototype.drawScene = function (can, top, caching) {
if (!this._cache.get(NODES_RECT)) {
this.update();
}
return _super.prototype.drawScene.call(this, can, top, caching); return _super.prototype.drawScene.call(this, can, top, caching);
}; };
// _attachTo(node) => { // _attachTo(node) => {
@@ -14998,9 +14995,10 @@
}; };
node.on(additionalEvents, onChange); node.on(additionalEvents, onChange);
node.on(TRANSFORM_CHANGE_STR$1, onChange); node.on(TRANSFORM_CHANGE_STR$1, onChange);
node.on("xChange." + EVENTS_NAME + " yChange." + EVENTS_NAME, function () { node.on("clearCache." + EVENTS_NAME, function () {
_this._resetTransformCache(); _this._resetTransformCache();
}); });
node.on("xChange." + EVENTS_NAME + " yChange." + EVENTS_NAME, onChange);
}); });
this._resetTransformCache(); this._resetTransformCache();
// we may need it if we set node in initial props // we may need it if we set node in initial props
@@ -15340,7 +15338,7 @@
var reverseX = this.findOne('.top-right').x() < this.findOne('.bottom-left').x() var reverseX = this.findOne('.top-right').x() < this.findOne('.bottom-left').x()
? -1 ? -1
: 1; : 1;
var reverseY = this.findOne('.bottom-right').y() < this.findOne('.top-left').y() var reverseY = this.findOne('.bottom-left').y() < this.findOne('.top-right').y()
? -1 ? -1
: 1; : 1;
x = newHypotenuse * this.cos * reverseX; x = newHypotenuse * this.cos * reverseX;
@@ -15600,15 +15598,6 @@
var _this = this; var _this = this;
var attrs = this._getNodeRect(); var attrs = this._getNodeRect();
this.rotation(Util._getRotation(attrs.rotation)); this.rotation(Util._getRotation(attrs.rotation));
var node = this.getNode();
var scale = { x: 1, y: 1 };
// if (node && node.getParent()) {
// scale = node.getParent().getAbsoluteScale();
// }
var invertedScale = {
x: 1 / scale.x,
y: 1 / scale.y
};
var width = attrs.width; var width = attrs.width;
var height = attrs.height; var height = attrs.height;
var enabledAnchors = this.enabledAnchors(); var enabledAnchors = this.enabledAnchors();
@@ -15632,14 +15621,12 @@
y: 0, y: 0,
offsetX: anchorSize / 2 + padding, offsetX: anchorSize / 2 + padding,
offsetY: anchorSize / 2 + padding, offsetY: anchorSize / 2 + padding,
scale: invertedScale,
visible: resizeEnabled && enabledAnchors.indexOf('top-left') >= 0 visible: resizeEnabled && enabledAnchors.indexOf('top-left') >= 0
}); });
this.findOne('.top-center').setAttrs({ this.findOne('.top-center').setAttrs({
x: width / 2, x: width / 2,
y: 0, y: 0,
offsetY: anchorSize / 2 + padding, offsetY: anchorSize / 2 + padding,
scale: invertedScale,
visible: resizeEnabled && enabledAnchors.indexOf('top-center') >= 0 visible: resizeEnabled && enabledAnchors.indexOf('top-center') >= 0
}); });
this.findOne('.top-right').setAttrs({ this.findOne('.top-right').setAttrs({
@@ -15647,21 +15634,18 @@
y: 0, y: 0,
offsetX: anchorSize / 2 - padding, offsetX: anchorSize / 2 - padding,
offsetY: anchorSize / 2 + padding, offsetY: anchorSize / 2 + padding,
scale: invertedScale,
visible: resizeEnabled && enabledAnchors.indexOf('top-right') >= 0 visible: resizeEnabled && enabledAnchors.indexOf('top-right') >= 0
}); });
this.findOne('.middle-left').setAttrs({ this.findOne('.middle-left').setAttrs({
x: 0, x: 0,
y: height / 2, y: height / 2,
offsetX: anchorSize / 2 + padding, offsetX: anchorSize / 2 + padding,
scale: invertedScale,
visible: resizeEnabled && enabledAnchors.indexOf('middle-left') >= 0 visible: resizeEnabled && enabledAnchors.indexOf('middle-left') >= 0
}); });
this.findOne('.middle-right').setAttrs({ this.findOne('.middle-right').setAttrs({
x: width, x: width,
y: height / 2, y: height / 2,
offsetX: anchorSize / 2 - padding, offsetX: anchorSize / 2 - padding,
scale: invertedScale,
visible: resizeEnabled && enabledAnchors.indexOf('middle-right') >= 0 visible: resizeEnabled && enabledAnchors.indexOf('middle-right') >= 0
}); });
this.findOne('.bottom-left').setAttrs({ this.findOne('.bottom-left').setAttrs({
@@ -15669,14 +15653,12 @@
y: height, y: height,
offsetX: anchorSize / 2 + padding, offsetX: anchorSize / 2 + padding,
offsetY: anchorSize / 2 - padding, offsetY: anchorSize / 2 - padding,
scale: invertedScale,
visible: resizeEnabled && enabledAnchors.indexOf('bottom-left') >= 0 visible: resizeEnabled && enabledAnchors.indexOf('bottom-left') >= 0
}); });
this.findOne('.bottom-center').setAttrs({ this.findOne('.bottom-center').setAttrs({
x: width / 2, x: width / 2,
y: height, y: height,
offsetY: anchorSize / 2 - padding, offsetY: anchorSize / 2 - padding,
scale: invertedScale,
visible: resizeEnabled && enabledAnchors.indexOf('bottom-center') >= 0 visible: resizeEnabled && enabledAnchors.indexOf('bottom-center') >= 0
}); });
this.findOne('.bottom-right').setAttrs({ this.findOne('.bottom-right').setAttrs({
@@ -15684,20 +15666,16 @@
y: height, y: height,
offsetX: anchorSize / 2 - padding, offsetX: anchorSize / 2 - padding,
offsetY: anchorSize / 2 - padding, offsetY: anchorSize / 2 - padding,
scale: invertedScale,
visible: resizeEnabled && enabledAnchors.indexOf('bottom-right') >= 0 visible: resizeEnabled && enabledAnchors.indexOf('bottom-right') >= 0
}); });
var scaledRotateAnchorOffset = -this.rotateAnchorOffset() * Math.abs(invertedScale.y);
this.findOne('.rotater').setAttrs({ this.findOne('.rotater').setAttrs({
x: width / 2, x: width / 2,
y: scaledRotateAnchorOffset * Util._sign(height) - padding, y: -this.rotateAnchorOffset() * Util._sign(height) - padding,
scale: invertedScale,
visible: this.rotateEnabled() visible: this.rotateEnabled()
}); });
this.findOne('.back').setAttrs({ this.findOne('.back').setAttrs({
width: width * scale.x, width: width,
height: height * scale.y, height: height,
scale: invertedScale,
visible: this.borderEnabled(), visible: this.borderEnabled(),
stroke: this.borderStroke(), stroke: this.borderStroke(),
strokeWidth: this.borderStrokeWidth(), strokeWidth: this.borderStrokeWidth(),

4
konva.min.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -265,10 +265,12 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
* when the logic for a cached result depends on ancestor propagation, use this * when the logic for a cached result depends on ancestor propagation, use this
* method to clear self and children cache * method to clear self and children cache
*/ */
_clearSelfAndDescendantCache(attr?: string) { _clearSelfAndDescendantCache(attr?: string, forceEvent?: boolean) {
this._clearCache(attr); this._clearCache(attr);
// trigger clear cache, so transformer can use it // trigger clear cache, so transformer can use it
this.fire('clearCache'); if (forceEvent) {
this.fire('clearCache');
}
// skip clearing if node is cached with canvas // skip clearing if node is cached with canvas
// for performance reasons !!! // for performance reasons !!!
@@ -277,7 +279,7 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
} }
if (this.children) { if (this.children) {
this.children.each(function(node) { this.children.each(function(node) {
node._clearSelfAndDescendantCache(attr); node._clearSelfAndDescendantCache(attr, true);
}); });
} }
} }
@@ -1208,8 +1210,8 @@ export abstract class Node<Config extends NodeConfig = NodeConfig> {
y: this.attrs.y + it.getTranslation().y y: this.attrs.y + it.getTranslation().y
}; };
this.setPosition({ x: pos.x, y: pos.y });
this._setTransform(origTrans); this._setTransform(origTrans);
this.setPosition({ x: pos.x, y: pos.y });
return this; return this;
} }

View File

@@ -70,9 +70,7 @@ var TRANSFORM_CHANGE_STR = [
'offsetXChange', 'offsetXChange',
'offsetYChange', 'offsetYChange',
'transformsEnabledChange', 'transformsEnabledChange',
'strokeWidthChange', 'strokeWidthChange'
// listen to cache changes
'clearCache'
] ]
.map(e => e + `.${EVENTS_NAME}`) .map(e => e + `.${EVENTS_NAME}`)
.join(' '); .join(' ');
@@ -401,9 +399,6 @@ export class Transformer extends Group {
} }
drawScene(can?, top?, caching?) { drawScene(can?, top?, caching?) {
if (!this._cache.get(NODES_RECT)) {
this.update();
}
return super.drawScene(can, top, caching); return super.drawScene(can, top, caching);
} }
// _attachTo(node) => { // _attachTo(node) => {
@@ -432,9 +427,10 @@ export class Transformer extends Group {
}; };
node.on(additionalEvents, onChange); node.on(additionalEvents, onChange);
node.on(TRANSFORM_CHANGE_STR, onChange); node.on(TRANSFORM_CHANGE_STR, onChange);
node.on(`xChange.${EVENTS_NAME} yChange.${EVENTS_NAME}`, () => { node.on(`clearCache.${EVENTS_NAME}`, () => {
this._resetTransformCache(); this._resetTransformCache();
}); });
node.on(`xChange.${EVENTS_NAME} yChange.${EVENTS_NAME}`, onChange);
}); });
this._resetTransformCache(); this._resetTransformCache();
// we may need it if we set node in initial props // we may need it if we set node in initial props
@@ -869,7 +865,7 @@ export class Transformer extends Group {
: 1; : 1;
var reverseY = var reverseY =
this.findOne('.bottom-right').y() < this.findOne('.top-left').y() this.findOne('.bottom-left').y() < this.findOne('.top-right').y()
? -1 ? -1
: 1; : 1;
@@ -1198,15 +1194,6 @@ export class Transformer extends Group {
update() { update() {
var attrs = this._getNodeRect(); var attrs = this._getNodeRect();
this.rotation(Util._getRotation(attrs.rotation)); this.rotation(Util._getRotation(attrs.rotation));
var node = this.getNode();
var scale = { x: 1, y: 1 };
// if (node && node.getParent()) {
// scale = node.getParent().getAbsoluteScale();
// }
var invertedScale = {
x: 1 / scale.x,
y: 1 / scale.y
};
var width = attrs.width; var width = attrs.width;
var height = attrs.height; var height = attrs.height;
@@ -1233,14 +1220,12 @@ export class Transformer extends Group {
y: 0, y: 0,
offsetX: anchorSize / 2 + padding, offsetX: anchorSize / 2 + padding,
offsetY: anchorSize / 2 + padding, offsetY: anchorSize / 2 + padding,
scale: invertedScale,
visible: resizeEnabled && enabledAnchors.indexOf('top-left') >= 0 visible: resizeEnabled && enabledAnchors.indexOf('top-left') >= 0
}); });
this.findOne('.top-center').setAttrs({ this.findOne('.top-center').setAttrs({
x: width / 2, x: width / 2,
y: 0, y: 0,
offsetY: anchorSize / 2 + padding, offsetY: anchorSize / 2 + padding,
scale: invertedScale,
visible: resizeEnabled && enabledAnchors.indexOf('top-center') >= 0 visible: resizeEnabled && enabledAnchors.indexOf('top-center') >= 0
}); });
this.findOne('.top-right').setAttrs({ this.findOne('.top-right').setAttrs({
@@ -1248,21 +1233,18 @@ export class Transformer extends Group {
y: 0, y: 0,
offsetX: anchorSize / 2 - padding, offsetX: anchorSize / 2 - padding,
offsetY: anchorSize / 2 + padding, offsetY: anchorSize / 2 + padding,
scale: invertedScale,
visible: resizeEnabled && enabledAnchors.indexOf('top-right') >= 0 visible: resizeEnabled && enabledAnchors.indexOf('top-right') >= 0
}); });
this.findOne('.middle-left').setAttrs({ this.findOne('.middle-left').setAttrs({
x: 0, x: 0,
y: height / 2, y: height / 2,
offsetX: anchorSize / 2 + padding, offsetX: anchorSize / 2 + padding,
scale: invertedScale,
visible: resizeEnabled && enabledAnchors.indexOf('middle-left') >= 0 visible: resizeEnabled && enabledAnchors.indexOf('middle-left') >= 0
}); });
this.findOne('.middle-right').setAttrs({ this.findOne('.middle-right').setAttrs({
x: width, x: width,
y: height / 2, y: height / 2,
offsetX: anchorSize / 2 - padding, offsetX: anchorSize / 2 - padding,
scale: invertedScale,
visible: resizeEnabled && enabledAnchors.indexOf('middle-right') >= 0 visible: resizeEnabled && enabledAnchors.indexOf('middle-right') >= 0
}); });
this.findOne('.bottom-left').setAttrs({ this.findOne('.bottom-left').setAttrs({
@@ -1270,14 +1252,12 @@ export class Transformer extends Group {
y: height, y: height,
offsetX: anchorSize / 2 + padding, offsetX: anchorSize / 2 + padding,
offsetY: anchorSize / 2 - padding, offsetY: anchorSize / 2 - padding,
scale: invertedScale,
visible: resizeEnabled && enabledAnchors.indexOf('bottom-left') >= 0 visible: resizeEnabled && enabledAnchors.indexOf('bottom-left') >= 0
}); });
this.findOne('.bottom-center').setAttrs({ this.findOne('.bottom-center').setAttrs({
x: width / 2, x: width / 2,
y: height, y: height,
offsetY: anchorSize / 2 - padding, offsetY: anchorSize / 2 - padding,
scale: invertedScale,
visible: resizeEnabled && enabledAnchors.indexOf('bottom-center') >= 0 visible: resizeEnabled && enabledAnchors.indexOf('bottom-center') >= 0
}); });
this.findOne('.bottom-right').setAttrs({ this.findOne('.bottom-right').setAttrs({
@@ -1285,23 +1265,18 @@ export class Transformer extends Group {
y: height, y: height,
offsetX: anchorSize / 2 - padding, offsetX: anchorSize / 2 - padding,
offsetY: anchorSize / 2 - padding, offsetY: anchorSize / 2 - padding,
scale: invertedScale,
visible: resizeEnabled && enabledAnchors.indexOf('bottom-right') >= 0 visible: resizeEnabled && enabledAnchors.indexOf('bottom-right') >= 0
}); });
var scaledRotateAnchorOffset =
-this.rotateAnchorOffset() * Math.abs(invertedScale.y);
this.findOne('.rotater').setAttrs({ this.findOne('.rotater').setAttrs({
x: width / 2, x: width / 2,
y: scaledRotateAnchorOffset * Util._sign(height) - padding, y: -this.rotateAnchorOffset() * Util._sign(height) - padding,
scale: invertedScale,
visible: this.rotateEnabled() visible: this.rotateEnabled()
}); });
this.findOne('.back').setAttrs({ this.findOne('.back').setAttrs({
width: width * scale.x, width: width,
height: height * scale.y, height: height,
scale: invertedScale,
visible: this.borderEnabled(), visible: this.borderEnabled(),
stroke: this.borderStroke(), stroke: this.borderStroke(),
strokeWidth: this.borderStrokeWidth(), strokeWidth: this.borderStrokeWidth(),

View File

@@ -1319,8 +1319,7 @@ suite('Transformer', function() {
tr.simulateMouseUp(); tr.simulateMouseUp();
}); });
// TODO: doesn't work!!! test('switch vertical scaling with (top-left anchor)', function() {
test.skip('switch vertical scaling with (top-left anchor)', function() {
var stage = addStage(); var stage = addStage();
var layer = new Konva.Layer(); var layer = new Konva.Layer();
stage.add(layer); stage.add(layer);
@@ -1368,12 +1367,12 @@ suite('Transformer', function() {
tr.simulateMouseUp(); tr.simulateMouseUp();
assert.equal(rect.x(), 0); assert.equal(rect.x(), 0);
assert.equal(rect.y(), 100); assert.equal(rect.y(), 0);
assert.equal(rect.width(), 100); assert.equal(rect.width(), 100);
assert.equal(rect.scaleX(), 1); assert.equal(rect.scaleX(), 1);
assert.equal(rect.height(), 100); assert.equal(rect.height(), 100);
assert.equal(rect.rotation(), 0); assert.equal(rect.rotation(), 0);
assert.equal(rect.scaleY(), -1); assert.equal(rect.scaleY(), 1);
}); });
test('switch scaling with padding for rotated - x', function() { test('switch scaling with padding for rotated - x', function() {
@@ -1705,38 +1704,7 @@ suite('Transformer', function() {
}); });
}); });
test.skip('on negative scaleY should move rotater', function() { test('try rotate scaled rect', function() {
var stage = addStage();
var layer = new Konva.Layer();
stage.add(layer);
var rect = new Konva.Rect({
x: 50,
y: 160,
draggable: true,
width: 100,
height: 100,
fill: 'yellow',
scaleY: -1
});
layer.add(rect);
var tr = new Konva.Transformer({
node: rect
});
layer.add(tr);
layer.draw();
var rotater = tr.findOne('.rotater');
var pos = rotater.getAbsolutePosition();
assert.equal(pos.x, 100);
assert.equal(pos.y, 210);
});
// TODO: why it doesn't work?
test.skip('try rotated scaled rect', function() {
var stage = addStage(); var stage = addStage();
var layer = new Konva.Layer(); var layer = new Konva.Layer();
stage.add(layer); stage.add(layer);
@@ -1761,27 +1729,20 @@ suite('Transformer', function() {
var rotater = tr.findOne('.rotater'); var rotater = tr.findOne('.rotater');
var pos = rotater.getAbsolutePosition(); var pos = rotater.getAbsolutePosition();
stage.simulateMouseDown({ tr.simulateMouseDown({
x: pos.x, x: pos.x,
y: pos.y y: pos.y
}); });
var top = stage.content.getBoundingClientRect().top; tr.simulateMouseMove({
tr._handleMouseMove({ x: pos.x + 100,
clientX: pos.x + 100, y: pos.y + 100
clientY: pos.y - 100 + top });
tr.simulateMouseUp({
x: pos.x + 100,
y: pos.y + 100
}); });
// here is duplicate, because transformer is listening window events assert.equal(rect.rotation(), 90);
tr._handleMouseUp({
clientX: pos.x + 100,
clientY: pos.y - 100 + top
});
stage.simulateMouseUp({
x: 100,
y: 100
});
assert.equal(rect.rotation(), -90);
}); });
test('check correct cursor on scaled shape', function() { test('check correct cursor on scaled shape', function() {
@@ -1926,12 +1887,13 @@ suite('Transformer', function() {
layer.draw(); layer.draw();
layer.scaleX(2); layer.scaleX(2);
layer.draw();
assert.equal(tr.width(), 200); assert.equal(tr.width(), 200);
layer.draw();
}); });
test.skip('check fit and correct cursor on rotated parent', function() { test('check fit and correct cursor on rotated parent', function() {
var stage = addStage(); var stage = addStage();
var layer = new Konva.Layer({ var layer = new Konva.Layer({
x: 100, x: 100,
@@ -1977,6 +1939,51 @@ suite('Transformer', function() {
assert.equal(stage.content.style.cursor, 'ew-resize'); assert.equal(stage.content.style.cursor, 'ew-resize');
}); });
test('check drag with transformer', function() {
var stage = addStage();
stage.draggable(true);
var layer = new Konva.Layer();
stage.add(layer);
var rect = new Konva.Rect({
x: 0,
y: 0,
draggable: true,
width: 100,
height: 100,
fill: 'yellow'
});
layer.add(rect);
var tr = new Konva.Transformer({
node: rect
});
layer.add(tr);
layer.draw();
stage.simulateMouseDown({
x: 50,
y: 50
});
stage.simulateMouseMove({
x: 55,
y: 50
});
stage.simulateMouseMove({
x: 60,
y: 50
});
stage.simulateMouseUp({
x: 60,
y: 50
});
assert.equal(rect.x(), 10);
assert.equal(rect.y(), 0);
});
test('stopTransform method', function() { test('stopTransform method', function() {
var stage = addStage(); var stage = addStage();
var layer = new Konva.Layer(); var layer = new Konva.Layer();
@@ -2535,6 +2542,7 @@ suite('Transformer', function() {
}); });
}); });
// TODO: fix it!!!
test.skip('centered scaling on flip + keep ratio', function() { test.skip('centered scaling on flip + keep ratio', function() {
var stage = addStage(); var stage = addStage();
var layer = new Konva.Layer(); var layer = new Konva.Layer();