diff --git a/CHANGELOG.md b/CHANGELOG.md index 0edca03c..6e25748c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## Not released: +## 5.0.3 - 2020-05-01 + +* Fixes for `boundBoxFunc` of `Konva.Transformer`. + ## 5.0.2 - 2020-04-23 * Deatach fixes for `Konva.Transformer` diff --git a/README.md b/README.md index d285cd60..9143cf0e 100644 --- a/README.md +++ b/README.md @@ -155,6 +155,7 @@ See file `konva-node/demo.js` file in this repo as a sample. # Backers [myposter GmbH](https://www.myposter.de/) +[queue.gg]()https://queue.gg/ # Change log diff --git a/konva.js b/konva.js index f36dfe53..57325685 100644 --- a/konva.js +++ b/konva.js @@ -499,7 +499,7 @@ result.rotation = b > 0 ? Math.acos(a / r) : -Math.acos(a / r); result.scaleX = r; result.scaleY = delta / r; - result.skewX = Math.atan((a * c + b * d) / (r * r)); + result.skewX = (a * c + b * d) / (r * r); result.skewY = 0; } else if (c != 0 || d != 0) { @@ -509,11 +509,24 @@ result.scaleX = delta / s; result.scaleY = s; result.skewX = 0; - result.skewY = Math.atan((a * c + b * d) / (s * s)); + result.skewY = (a * c + b * d) / (s * s); } result.rotation = Util._getRotation(result.rotation); return result; }; + Transform.prototype.qrDecompose = function () { + var angle = Math.atan2(this.m[1], this.m[0]), denom = Math.pow(this.m[0], 2) + Math.pow(this.m[1], 2), scaleX = Math.sqrt(denom), scaleY = (this.m[0] * this.m[3] - this.m[2] * this.m[1]) / scaleX, skewX = Math.atan2(this.m[0] * this.m[2] + this.m[1] * this.m[3], denom); + var rotation = Util._getRotation(angle); + return { + rotation: rotation, + scaleX: scaleX, + scaleY: scaleY, + skewX: skewX / (Math.PI / 180), + skewY: 0, + x: this.m[4], + y: this.m[5] + }; + }; return Transform; }()); // CONSTANTS @@ -13379,7 +13392,7 @@ */ Factory.addGetterSetter(TextPath, 'letterSpacing', 0, getNumberValidator()); /** - * get/set text baselineg. The default is 'middle'. Can be 'top', 'bottom', 'middle', 'alphabetic', 'hanging' + * get/set text baseline. The default is 'middle'. Can be 'top', 'bottom', 'middle', 'alphabetic', 'hanging' * @name Konva.TextPath#textBaseline * @method * @param {String} textBaseline @@ -13577,51 +13590,6 @@ var center = getCenter(shape); return rotateAroundPoint(shape, deltaRad, center); } - function getShapeRect(shape) { - var angleRad = shape.rotation; - var x1 = shape.x; - var y1 = shape.y; - var x2 = x1 + shape.width * Math.cos(angleRad); - var y2 = y1 + shape.width * Math.sin(angleRad); - var x3 = shape.x + - shape.width * Math.cos(angleRad) + - shape.height * Math.sin(-angleRad); - var y3 = shape.y + - shape.height * Math.cos(angleRad) + - shape.width * Math.sin(angleRad); - var x4 = shape.x + shape.height * Math.sin(-angleRad); - var y4 = shape.y + shape.height * Math.cos(angleRad); - var leftX = Math.min(x1, x2, x3, x4); - var rightX = Math.max(x1, x2, x3, x4); - var topY = Math.min(y1, y2, y3, y4); - var bottomY = Math.max(y1, y2, y3, y4); - return { - x: leftX, - y: topY, - width: rightX - leftX, - height: bottomY - topY - }; - } - function getShapesRect(shapes) { - var x1 = 9999999999; - var y1 = 9999999999; - var x2 = -999999999; - var y2 = -999999999; - shapes.forEach(function (shape) { - var rect = getShapeRect(shape); - x1 = Math.min(x1, rect.x); - y1 = Math.min(y1, rect.y); - x2 = Math.max(x2, rect.x + rect.width); - y2 = Math.max(y2, rect.y + rect.height); - }); - return { - x: x1, - y: y1, - width: x2 - x1, - height: y2 - y1, - rotation: 0 - }; - } function getSnap(snaps, newRotationRad, tol) { var snapped = newRotationRad; for (var i = 0; i < snaps.length; i++) { @@ -13816,6 +13784,7 @@ Transformer.prototype._getNodeRect = function () { return this._getCache(NODES_RECT, this.__getNodeRect); }; + // return absolute rotated bounding rectangle Transformer.prototype.__getNodeShape = function (node, rot, relative) { if (rot === void 0) { rot = this.rotation(); } var rect = node.getClientRect({ @@ -13854,14 +13823,56 @@ rotation: 0 }; } - var shapes = this.nodes().map(function (node) { - return _this.__getNodeShape(node); + var totalPoints = []; + this.nodes().map(function (node) { + var box = node.getClientRect({ + skipTransform: true, + skipShadow: true, + skipStroke: _this.ignoreStroke() + }); + var points = [ + { x: box.x, y: box.y }, + { x: box.x + box.width, y: box.y }, + { x: box.x + box.width, y: box.y + box.height }, + { x: box.x, y: box.y + box.height } + ]; + var trans = node.getAbsoluteTransform(); + points.forEach(function (point) { + var transformed = trans.point(point); + totalPoints.push(transformed); + }); }); - var box = getShapesRect(shapes); - return rotateAroundPoint(box, Konva.getAngle(this.rotation()), { - x: 0, - y: 0 + var tr = new Transform(); + tr.rotate(-Konva.getAngle(this.rotation())); + var minX, minY, maxX, maxY; + totalPoints.forEach(function (point) { + var transformed = tr.point(point); + if (minX === undefined) { + minX = maxX = transformed.x; + minY = maxY = transformed.y; + } + minX = Math.min(minX, transformed.x); + minY = Math.min(minY, transformed.y); + maxX = Math.max(maxX, transformed.x); + maxY = Math.max(maxY, transformed.y); }); + tr.invert(); + var p = tr.point({ x: minX, y: minY }); + return { + x: p.x, + y: p.y, + width: maxX - minX, + height: maxY - minY, + rotation: Konva.getAngle(this.rotation()) + }; + // const shapes = this.nodes().map(node => { + // return this.__getNodeShape(node); + // }); + // const box = getShapesRect(shapes); + // return rotateAroundPoint(box, Konva.getAngle(this.rotation()), { + // x: 0, + // y: 0 + // }); }; Transformer.prototype.getX = function () { return this._getNodeRect().x; @@ -14171,7 +14182,6 @@ } }; Transformer.prototype._fitNodesInto = function (newAttrs, evt) { - var _this = this; var oldAttrs = this._getNodeRect(); var minSize = 1; if (Util._inRange(newAttrs.width, -this.padding() * 2 - minSize, minSize)) { @@ -14236,17 +14246,30 @@ this._anchorDragOffset.y -= offset.y; newAttrs.height += this.padding() * 2; } + // const pure = this.findOne('.back').getClientRect({ + // skipStroke: true + // }); + // var scaleX = pure.width ? newAttrs.width / pure.width : 1; + // var scaleY = pure.height ? newAttrs.height / pure.height : 1; + // var dx = pure.x * scaleX; + // var dy = pure.y * scaleY; // let's find delta transform - var dx = newAttrs.x - oldAttrs.x, dy = newAttrs.y - oldAttrs.y, angle = newAttrs.rotation - oldAttrs.rotation, scaleX = newAttrs.width / oldAttrs.width, scaleY = newAttrs.height / oldAttrs.height; - // dt1.invert(); - // const dt2 = new Transform(); - // dt2.translate(newAttrs.x, newAttrs.y); - // dt2.rotate(newAttrs.rotation); + // var dx = newAttrs.x - oldAttrs.x, + // dy = newAttrs.y - oldAttrs.y, + // var angle = newAttrs.rotation - oldAttrs.rotation; + // scaleX = newAttrs.width / oldAttrs.width, + // scaleY = newAttrs.height / oldAttrs.height; + // const x = newAttrs.x - (dx * Math.cos(angle) + dy * Math.sin(-angle)); + // const y = newAttrs.y - (dy * Math.cos(angle) + dx * Math.sin(angle)); + // // dt1.invert(); + // const tr = new Transform(); + // tr.translate(x, y); + // tr.rotate(angle); // // console.log(dt.point(newAttrs)); // // dt.translate(newAttrs.x, newAttrs.y); // // console.log(dt.decompose()); // // dt.rotate(newAttrs.rotation); - // dt2.scale(scaleX, scaleY); + // tr.scale(scaleX, scaleY); // dt1.multiply(dt2); // dt.translate(dx, dy); // dt.rotate(angle); @@ -14257,103 +14280,37 @@ // ) { // debugger; // } + var base = 10000000; + var oldTr = new Transform(); + oldTr.translate(oldAttrs.x, oldAttrs.y); + oldTr.rotate(oldAttrs.rotation); + oldTr.scale(oldAttrs.width / base, oldAttrs.height / base); + var newTr = new Transform(); + newTr.translate(newAttrs.x, newAttrs.y); + newTr.rotate(newAttrs.rotation); + newTr.scale(newAttrs.width / base, newAttrs.height / base); + var delta = newTr.multiply(oldTr.invert()); this._nodes.forEach(function (node) { - // var oldRect = this.__getNodeShape(node, 0); - // var newRect = transformAndRotateShape(oldRect, oldAttrs, newAttrs); - // this._fitNodeInto(node, newRect, evt); - // var m = new Transform(), - // x = node.x(), - // y = node.y(), - // rotation = Konva.getAngle(node.rotation()), - // scaleX = node.scaleX(), - // scaleY = node.scaleY(), - // skewX = node.skewX(), - // skewY = node.skewY(), - // offsetX = node.offsetX(), - // offsetY = node.offsetY(); - // // if (x !== 0 || y !== 0) { - // // m.translate(x, y); - // // } - // if (rotation !== 0) { - // m.rotate(rotation); + var pt = node.getParent().getAbsoluteTransform(); + var selfTr = node.getTransform().copy(); + selfTr.translate(node.offsetX(), node.offsetY()); + var newLocal = new Transform(); + newLocal + .multiply(delta) + .multiply(pt) + .multiply(pt.copy().invert()) + .multiply(selfTr); + // node._cache.set('transform', newLocal); + // node._cache.set('absoluteTransform', newLocal); + // console.log(); + var attrs = newLocal.decompose(); + // if (Math.abs(attrs.skewX - node.skewX()) < 0.00000001) { + // attrs.skewX = node.skewX(); // } - // if (skewX !== 0 || skewY !== 0) { - // m.skew(skewX, skewY); + // if (Math.abs(attrs.skewY - node.skewY()) < 0.00000001) { + // attrs.skewY = node.skewY(); // } - // if (scaleX !== 1 || scaleY !== 1) { - // m.scale(scaleX, scaleY); - // } - // if (offsetX !== 0 || offsetY !== 0) { - // m.translate(-1 * offsetX, -1 * offsetY); - // } - // return m; - // t.translate(dx, dy); - // t.rotate(Konva.getAngle(node.rotation()) + angle); - // t.scale(node.scaleX() * scaleX, node.scaleY() * scaleY); - // const t = node.getTransform(); - // const dt1 = new Transform(); - // dt1.translate(dx, dy); - // dt1.rotate(angle); - // dt1.scale(scaleX, scaleY); - // // const oldScale = { - // // x: dt.decompose().scaleX, - // // y: dt.decompose().scaleY - // // }; - // // dt.scale(1, 1); - // t.multiply(dt1); - // dt.scale(oldScale.x, oldScale.y); - // t.translate( - // dt.getTranslation().x * t.decompose().scaleX, - // dt.getTranslation().y * t.decompose().scaleY - // ); - // dt.set - // dt.setAbsolutePosition(0, 0); - // t.multiply(dt); - // console.log( - // node.rotation(), - // dt.decompose().rotation, - // t.decompose().rotation, - // Util._getRotation(newAttrs.rotation) - // ); - // m.multiply(dt); - // m.translate(x + dx, y + dy); - // node.setAttrs(t.decompose()); - // if (offsetX !== 0 || offsetY !== 0) { - // t.translate( - // node.offsetX() * (1 - node.scaleX() / scaleX), - // node.offsetY() * (1 - node.scaleY() / scaleY) - // ); - var parentRot = Konva.getAngle(node.getParent().getAbsoluteRotation()); - var rotation = Util._getRotation(newAttrs.rotation - parentRot); - var pure = node.getClientRect({ - skipShadow: true, - skipStroke: _this.ignoreStroke() - }); - var parentTransform = node - .getParent() - .getAbsoluteTransform() - .copy(); - parentTransform.invert(); - var invertedPoint = parentTransform.point({ - x: newAttrs.x, - y: newAttrs.y - }); - newAttrs.x = invertedPoint.x; - newAttrs.y = invertedPoint.y; - var absScale = node.getParent().getAbsoluteScale(); - pure.width *= absScale.x; - pure.height *= absScale.y; - var scaleX = pure.width ? newAttrs.width / pure.width : 1; - var scaleY = pure.height ? newAttrs.height / pure.height : 1; - var dx = pure.x * scaleX - node.offsetX() * scaleX; - var dy = pure.y * scaleY - node.offsetY() * scaleY; - var x = newAttrs.x - (dx * Math.cos(rotation) + dy * Math.sin(-rotation)); - var y = newAttrs.y - (dy * Math.cos(rotation) + dx * Math.sin(rotation)); - var tr = new Transform(); - tr.translate(x, y); - tr.rotate(rotation); - tr.scale(scaleX, scaleY); - node.setAttrs(tr.multiply(node.getTransform()).decompose()); + node.setAttrs(attrs); }); this.rotation(Util._getRotation(newAttrs.rotation)); this._resetTransformCache(); @@ -14361,18 +14318,6 @@ this.getLayer().batchDraw(); }; Transformer.prototype._fitNodeInto = function (node, newAttrs, evt) { - if (this.boundBoxFunc()) { - var oldAttrs = this.__getNodeShape(node, node.rotation(), node.getParent()); - var bounded = this.boundBoxFunc()(oldAttrs, newAttrs, node); - if (bounded) { - newAttrs = bounded; - } - else { - Util.warn('boundBoxFunc returned falsy. You should return new bound rect from it!'); - } - } - var parentRot = Konva.getAngle(node.getParent().getAbsoluteRotation()); - node.rotation(Util._getRotation(newAttrs.rotation - parentRot)); var pure = node.getClientRect({ skipTransform: true, skipShadow: true, @@ -14387,11 +14332,24 @@ x: newAttrs.x, y: newAttrs.y }); + var absScale = node.getParent().getAbsoluteScale(); newAttrs.x = invertedPoint.x; newAttrs.y = invertedPoint.y; + newAttrs.width /= absScale.x; + newAttrs.height /= absScale.y; + if (this.boundBoxFunc()) { + var oldAttrs = this.__getNodeShape(node, node.rotation(), node.getParent()); + var bounded = this.boundBoxFunc()(oldAttrs, newAttrs, node); + if (bounded) { + newAttrs = bounded; + } + else { + Util.warn('boundBoxFunc returned falsy. You should return new bound rect from it!'); + } + } + var parentRot = Konva.getAngle(node.getParent().getAbsoluteRotation()); + node.rotation(Util._getRotation(newAttrs.rotation - parentRot)); var absScale = node.getParent().getAbsoluteScale(); - pure.width *= absScale.x; - pure.height *= absScale.y; var scaleX = pure.width ? newAttrs.width / pure.width : 1; var scaleY = pure.height ? newAttrs.height / pure.height : 1; var rotation = Konva.getAngle(node.rotation()); diff --git a/konva.min.js b/konva.min.js index 19b4e1a8..837bbbae 100644 --- a/konva.min.js +++ b/konva.min.js @@ -1,12 +1,12 @@ !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t=t||self).Konva=e()}(this,function(){"use strict"; /* - * Konva JavaScript Framework v5.0.2 + * Konva JavaScript Framework v5.0.3 * http://konvajs.org/ * Licensed under the MIT - * Date: Thu Apr 23 2020 + * Date: Fri May 01 2020 * * Original work Copyright (C) 2011 - 2013 by Eric Rowell (KineticJS) * Modified work Copyright (C) 2014 - present by Anton Lavrenov (Konva) * * @license - */var e=Math.PI/180;function t(t){var e=t.toLowerCase(),i=/(chrome)[ /]([\w.]+)/.exec(e)||/(webkit)[ /]([\w.]+)/.exec(e)||/(opera)(?:.*version|)[ /]([\w.]+)/.exec(e)||/(msie) ([\w.]+)/.exec(e)||e.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(e)||[],n=!!t.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i),r=!!t.match(/IEMobile/i);return{browser:i[1]||"",version:i[2]||"0",isIE:function(t){var e=t.indexOf("msie ");if(0>16&255,g:e>>8&255,b:255&e}},getRandomColor:function(){for(var t=(16777215*Math.random()<<0).toString(16);t.length<6;)t="0"+t;return"#"+t},get:function(t,e){return void 0===t?e:t},getRGB:function(t){var e;return t in p?{r:(e=p[t])[0],g:e[1],b:e[2]}:"#"===t[0]?this._hexToRgb(t.substring(1)):"rgb("===t.substr(0,4)?(e=u.exec(t.replace(/ /g,"")),{r:parseInt(e[1],10),g:parseInt(e[2],10),b:parseInt(e[3],10)}):{r:0,g:0,b:0}},colorToRGBA:function(t){return t=t||"black",A._namedColorToRBA(t)||A._hex3ColorToRGBA(t)||A._hex6ColorToRGBA(t)||A._rgbColorToRGBA(t)||A._rgbaColorToRGBA(t)||A._hslColorToRGBA(t)},_namedColorToRBA:function(t){var e=p[t.toLowerCase()];return e?{r:e[0],g:e[1],b:e[2],a:1}:null},_rgbColorToRGBA:function(t){if(0===t.indexOf("rgb(")){var e=(t=t.match(/rgb\(([^)]+)\)/)[1]).split(/ *, */).map(Number);return{r:e[0],g:e[1],b:e[2],a:1}}},_rgbaColorToRGBA:function(t){if(0===t.indexOf("rgba(")){var e=(t=t.match(/rgba\(([^)]+)\)/)[1]).split(/ *, */).map(Number);return{r:e[0],g:e[1],b:e[2],a:e[3]}}},_hex6ColorToRGBA:function(t){if("#"===t[0]&&7===t.length)return{r:parseInt(t.slice(1,3),16),g:parseInt(t.slice(3,5),16),b:parseInt(t.slice(5,7),16),a:1}},_hex3ColorToRGBA:function(t){if("#"===t[0]&&4===t.length)return{r:parseInt(t[1]+t[1],16),g:parseInt(t[2]+t[2],16),b:parseInt(t[3]+t[3],16),a:1}},_hslColorToRGBA:function(t){if(/hsl\((\d+),\s*([\d.]+)%,\s*([\d.]+)%\)/g.test(t)){var e=/hsl\((\d+),\s*([\d.]+)%,\s*([\d.]+)%\)/g.exec(t),i=(e[0],e.slice(1)),n=Number(i[0])/360,r=Number(i[1])/100,o=Number(i[2])/100,a=void 0,s=void 0,h=void 0;if(0==r)return h=255*o,{r:Math.round(h),g:Math.round(h),b:Math.round(h),a:1};for(var d=2*o-(a=o<.5?o*(1+r):o+r-o*r),l=[0,0,0],c=0;c<3;c++)(s=n+1/3*-(c-1))<0&&s++,1t.x+t.width||e.x+e.widtht.y+t.height||e.y+e.heighte.length){var a=e;e=t,t=a}for(n=0;n=this.parent.children.length)&&A.warn("Unexpected value "+t+" for zIndex property. zIndex is just index of a node in children of its parent. Expected value is from 0 to "+(this.parent.children.length-1)+".");var e=this.index;return this.parent.children.splice(e,1),this.parent.children.splice(t,0,this),this.parent._setChildrenIndices(),this},ct.prototype.getAbsoluteOpacity=function(){return this._getCache(Z,this._getAbsoluteOpacity)},ct.prototype._getAbsoluteOpacity=function(){var t=this.opacity(),e=this.getParent();return e&&!e._isUnderCache&&(t*=e.getAbsoluteOpacity()),t},ct.prototype.moveTo=function(t){return this.getParent()!==t&&(this._remove(),t.add(this)),this},ct.prototype.toObject=function(){var t,e,i,n={},r=this.getAttrs();for(t in n.attrs={},r)e=r[t],A.isObject(e)&&!A._isPlainObject(e)&&!A._isArray(e)||(i="function"==typeof this[t]&&this[t],delete r[t],(i?i.call(this):null)!==(r[t]=e)&&(n.attrs[t]=e));return n.className=this.getClassName(),A._prepareToStringify(n)},ct.prototype.toJSON=function(){return JSON.stringify(this.toObject())},ct.prototype.getParent=function(){return this.parent},ct.prototype.findAncestors=function(t,e,i){var n=[];e&&this._isMatch(t)&&n.push(this);for(var r=this.parent;r;){if(r===i)return n;r._isMatch(t)&&n.push(r),r=r.parent}return n},ct.prototype.isAncestorOf=function(t){return!1},ct.prototype.findAncestor=function(t,e,i){return this.findAncestors(t,e,i)[0]},ct.prototype._isMatch=function(t){if(!t)return!1;if("function"==typeof t)return t(this);var e,i,n=t.replace(/ /g,"").split(","),r=n.length;for(e=0;ethis.duration?this.yoyo?(this._time=this.duration,this.reverse()):this.finish():t<0?this.yoyo?(this._time=0,this.play()):this.reset():(this._time=t,this.update())},Ge.prototype.getTime=function(){return this._time},Ge.prototype.setPosition=function(t){this.prevPos=this._pos,this.propFunc(t),this._pos=t},Ge.prototype.getPosition=function(t){return void 0===t&&(t=this._time),this.func(t,this.begin,this._change,this.duration)},Ge.prototype.play=function(){this.state=2,this._startTime=this.getTimer()-this._time,this.onEnterFrame(),this.fire("onPlay")},Ge.prototype.reverse=function(){this.state=3,this._time=this.duration-this._time,this._startTime=this.getTimer()-this._time,this.onEnterFrame(),this.fire("onReverse")},Ge.prototype.seek=function(t){this.pause(),this._time=t,this.update(),this.fire("onSeek")},Ge.prototype.reset=function(){this.pause(),this._time=0,this.update(),this.fire("onReset")},Ge.prototype.finish=function(){this.pause(),this._time=this.duration,this.update(),this.fire("onFinish")},Ge.prototype.update=function(){this.setPosition(this.getPosition(this._time))},Ge.prototype.onEnterFrame=function(){var t=this.getTimer()-this._startTime;2===this.state?this.setTime(t):3===this.state&&this.setTime(this.duration-t)},Ge.prototype.pause=function(){this.state=1,this.fire("onPause")},Ge.prototype.getTimer=function(){return(new Date).getTime()},Ge);function Ge(t,e,i,n,r,o,a){this.prop=t,this.propFunc=e,this.begin=n,this._pos=n,this.duration=o,this._change=0,this.prevPos=0,this.yoyo=a,this._time=0,this._position=0,this._startTime=0,this._finish=0,this.func=i,this._change=r-this.begin,this.pause()}var Re=(Oe.prototype._addAttr=function(t,e){var i,n,r,o,a,s,h,d,l=this.node,c=l._id;if((r=Oe.tweens[c][t])&&delete Oe.attrs[c][r][t],i=l.getAttr(t),A._isArray(e))if(n=[],a=Math.max(e.length,i.length),"points"===t&&e.length!==i.length&&(e.length>i.length?(h=i,i=A._prepareArrayForTween(i,e,l.closed())):(s=e,e=A._prepareArrayForTween(e,i,l.closed()))),0===t.indexOf("fill"))for(o=0;othis.dataArray[i].pathLength;)t-=this.dataArray[i].pathLength,++i;if(i===n)return{x:(e=this.dataArray[i-1].points.slice(-2))[0],y:e[1]};if(t<.01)return{x:(e=this.dataArray[i].points.slice(0,2))[0],y:e[1]};var r=this.dataArray[i],o=r.points;switch(r.command){case"L":return pi.getPointOnLine(t,r.start.x,r.start.y,o[0],o[1]);case"C":return pi.getPointOnCubicBezier(t/r.pathLength,r.start.x,r.start.y,o[0],o[1],o[2],o[3],o[4],o[5]);case"Q":return pi.getPointOnQuadraticBezier(t/r.pathLength,r.start.x,r.start.y,o[0],o[1],o[2],o[3]);case"A":var a=o[0],s=o[1],h=o[2],d=o[3],l=o[4],c=o[5],p=o[6];return l+=c*t/r.pathLength,pi.getPointOnEllipticalArc(a,s,h,d,l,p)}return null},pi.getLineLength=function(t,e,i,n){return Math.sqrt((i-t)*(i-t)+(n-e)*(n-e))},pi.getPointOnLine=function(t,e,i,n,r,o,a){void 0===o&&(o=e),void 0===a&&(a=i);var s=(r-i)/(n-e+1e-8),h=Math.sqrt(t*t/(1+s*s));n>>1,k=_.slice(0,1+P),T=this._getTextWidth(k)+v;T<=d?(b=1+P,w=k+(g?"…":""),C=T):S=P}if(!w)break;if(f){var A,M=_[w.length];0<(A=(" "===M||"-"===M)&&C<=d?w.length:Math.max(w.lastIndexOf(" "),w.lastIndexOf("-"))+1)&&(b=A,w=w.slice(0,b),C=this._getTextWidth(w))}if(w=w.trimRight(),this._addTextLine(w),i=Math.max(i,C),c+=n,!u||s&&le?g=ci.getPointOnLine(e,f.x,f.y,v.points[0],v.points[1],f.x,f.y):v=void 0;break;case"A":var a=v.points[4],s=v.points[5],h=v.points[4]+s;0===m?m=a+1e-8:iv.pathLength?1e-8:e/v.pathLength:ip.x?-1:1,f=this.findOne(".top-left").y()>p.y?-1:1;e=n*this.cos*u,i=n*this.sin*f,this.findOne(".top-left").x(p.x-e),this.findOne(".top-left").y(p.y-i)}}else if("top-center"===this._movingAnchorName)this.findOne(".top-left").y(r.y());else if("top-right"===this._movingAnchorName){l&&(p=c?{x:this.width()/2,y:this.height()/2}:{x:this.findOne(".bottom-left").x(),y:this.findOne(".bottom-left").y()},n=Math.sqrt(Math.pow(r.x()-p.x,2)+Math.pow(p.y-r.y(),2)),u=this.findOne(".top-right").x()p.y?-1:1,e=n*this.cos*u,i=n*this.sin*f,this.findOne(".top-right").x(p.x+e),this.findOne(".top-right").y(p.y-i));var g=r.position();this.findOne(".top-left").y(g.y),this.findOne(".bottom-right").x(g.x)}else"middle-left"===this._movingAnchorName?this.findOne(".top-left").x(r.x()):"middle-right"===this._movingAnchorName?this.findOne(".bottom-right").x(r.x()):"bottom-left"===this._movingAnchorName?(l&&(p=c?{x:this.width()/2,y:this.height()/2}:{x:this.findOne(".top-right").x(),y:this.findOne(".top-right").y()},n=Math.sqrt(Math.pow(p.x-r.x(),2)+Math.pow(r.y()-p.y,2)),u=p.x>z,0!==C?(C=255/C,k[s]=(d*N>>z)*C,k[s+1]=(l*N>>z)*C,k[s+2]=(c*N>>z)*C):k[s]=k[s+1]=k[s+2]=0,d-=u,l-=f,c-=g,p-=v,u-=F.r,f-=F.g,g-=F.b,v-=F.a,o=h+((o=i+e+1)>z,0>z)*C,k[o+1]=(l*N>>z)*C,k[o+2]=(c*N>>z)*C):k[o]=k[o+1]=k[o+2]=0,d-=u,l-=f,c-=g,p-=v,u-=F.r,f-=F.g,g-=F.b,v-=F.a,o=i+((o=n+O)>16&255,g:e>>8&255,b:255&e}},getRandomColor:function(){for(var t=(16777215*Math.random()<<0).toString(16);t.length<6;)t="0"+t;return"#"+t},get:function(t,e){return void 0===t?e:t},getRGB:function(t){var e;return t in p?{r:(e=p[t])[0],g:e[1],b:e[2]}:"#"===t[0]?this._hexToRgb(t.substring(1)):"rgb("===t.substr(0,4)?(e=u.exec(t.replace(/ /g,"")),{r:parseInt(e[1],10),g:parseInt(e[2],10),b:parseInt(e[3],10)}):{r:0,g:0,b:0}},colorToRGBA:function(t){return t=t||"black",A._namedColorToRBA(t)||A._hex3ColorToRGBA(t)||A._hex6ColorToRGBA(t)||A._rgbColorToRGBA(t)||A._rgbaColorToRGBA(t)||A._hslColorToRGBA(t)},_namedColorToRBA:function(t){var e=p[t.toLowerCase()];return e?{r:e[0],g:e[1],b:e[2],a:1}:null},_rgbColorToRGBA:function(t){if(0===t.indexOf("rgb(")){var e=(t=t.match(/rgb\(([^)]+)\)/)[1]).split(/ *, */).map(Number);return{r:e[0],g:e[1],b:e[2],a:1}}},_rgbaColorToRGBA:function(t){if(0===t.indexOf("rgba(")){var e=(t=t.match(/rgba\(([^)]+)\)/)[1]).split(/ *, */).map(Number);return{r:e[0],g:e[1],b:e[2],a:e[3]}}},_hex6ColorToRGBA:function(t){if("#"===t[0]&&7===t.length)return{r:parseInt(t.slice(1,3),16),g:parseInt(t.slice(3,5),16),b:parseInt(t.slice(5,7),16),a:1}},_hex3ColorToRGBA:function(t){if("#"===t[0]&&4===t.length)return{r:parseInt(t[1]+t[1],16),g:parseInt(t[2]+t[2],16),b:parseInt(t[3]+t[3],16),a:1}},_hslColorToRGBA:function(t){if(/hsl\((\d+),\s*([\d.]+)%,\s*([\d.]+)%\)/g.test(t)){var e=/hsl\((\d+),\s*([\d.]+)%,\s*([\d.]+)%\)/g.exec(t),i=(e[0],e.slice(1)),n=Number(i[0])/360,r=Number(i[1])/100,o=Number(i[2])/100,a=void 0,s=void 0,h=void 0;if(0==r)return h=255*o,{r:Math.round(h),g:Math.round(h),b:Math.round(h),a:1};for(var d=2*o-(a=o<.5?o*(1+r):o+r-o*r),l=[0,0,0],c=0;c<3;c++)(s=n+1/3*-(c-1))<0&&s++,1t.x+t.width||e.x+e.widtht.y+t.height||e.y+e.heighte.length){var a=e;e=t,t=a}for(n=0;n=this.parent.children.length)&&A.warn("Unexpected value "+t+" for zIndex property. zIndex is just index of a node in children of its parent. Expected value is from 0 to "+(this.parent.children.length-1)+".");var e=this.index;return this.parent.children.splice(e,1),this.parent.children.splice(t,0,this),this.parent._setChildrenIndices(),this},ct.prototype.getAbsoluteOpacity=function(){return this._getCache(Z,this._getAbsoluteOpacity)},ct.prototype._getAbsoluteOpacity=function(){var t=this.opacity(),e=this.getParent();return e&&!e._isUnderCache&&(t*=e.getAbsoluteOpacity()),t},ct.prototype.moveTo=function(t){return this.getParent()!==t&&(this._remove(),t.add(this)),this},ct.prototype.toObject=function(){var t,e,i,n={},r=this.getAttrs();for(t in n.attrs={},r)e=r[t],A.isObject(e)&&!A._isPlainObject(e)&&!A._isArray(e)||(i="function"==typeof this[t]&&this[t],delete r[t],(i?i.call(this):null)!==(r[t]=e)&&(n.attrs[t]=e));return n.className=this.getClassName(),A._prepareToStringify(n)},ct.prototype.toJSON=function(){return JSON.stringify(this.toObject())},ct.prototype.getParent=function(){return this.parent},ct.prototype.findAncestors=function(t,e,i){var n=[];e&&this._isMatch(t)&&n.push(this);for(var r=this.parent;r;){if(r===i)return n;r._isMatch(t)&&n.push(r),r=r.parent}return n},ct.prototype.isAncestorOf=function(t){return!1},ct.prototype.findAncestor=function(t,e,i){return this.findAncestors(t,e,i)[0]},ct.prototype._isMatch=function(t){if(!t)return!1;if("function"==typeof t)return t(this);var e,i,n=t.replace(/ /g,"").split(","),r=n.length;for(e=0;ethis.duration?this.yoyo?(this._time=this.duration,this.reverse()):this.finish():t<0?this.yoyo?(this._time=0,this.play()):this.reset():(this._time=t,this.update())},Ge.prototype.getTime=function(){return this._time},Ge.prototype.setPosition=function(t){this.prevPos=this._pos,this.propFunc(t),this._pos=t},Ge.prototype.getPosition=function(t){return void 0===t&&(t=this._time),this.func(t,this.begin,this._change,this.duration)},Ge.prototype.play=function(){this.state=2,this._startTime=this.getTimer()-this._time,this.onEnterFrame(),this.fire("onPlay")},Ge.prototype.reverse=function(){this.state=3,this._time=this.duration-this._time,this._startTime=this.getTimer()-this._time,this.onEnterFrame(),this.fire("onReverse")},Ge.prototype.seek=function(t){this.pause(),this._time=t,this.update(),this.fire("onSeek")},Ge.prototype.reset=function(){this.pause(),this._time=0,this.update(),this.fire("onReset")},Ge.prototype.finish=function(){this.pause(),this._time=this.duration,this.update(),this.fire("onFinish")},Ge.prototype.update=function(){this.setPosition(this.getPosition(this._time))},Ge.prototype.onEnterFrame=function(){var t=this.getTimer()-this._startTime;2===this.state?this.setTime(t):3===this.state&&this.setTime(this.duration-t)},Ge.prototype.pause=function(){this.state=1,this.fire("onPause")},Ge.prototype.getTimer=function(){return(new Date).getTime()},Ge);function Ge(t,e,i,n,r,o,a){this.prop=t,this.propFunc=e,this.begin=n,this._pos=n,this.duration=o,this._change=0,this.prevPos=0,this.yoyo=a,this._time=0,this._position=0,this._startTime=0,this._finish=0,this.func=i,this._change=r-this.begin,this.pause()}var Re=(Oe.prototype._addAttr=function(t,e){var i,n,r,o,a,s,h,d,l=this.node,c=l._id;if((r=Oe.tweens[c][t])&&delete Oe.attrs[c][r][t],i=l.getAttr(t),A._isArray(e))if(n=[],a=Math.max(e.length,i.length),"points"===t&&e.length!==i.length&&(e.length>i.length?(h=i,i=A._prepareArrayForTween(i,e,l.closed())):(s=e,e=A._prepareArrayForTween(e,i,l.closed()))),0===t.indexOf("fill"))for(o=0;othis.dataArray[i].pathLength;)t-=this.dataArray[i].pathLength,++i;if(i===n)return{x:(e=this.dataArray[i-1].points.slice(-2))[0],y:e[1]};if(t<.01)return{x:(e=this.dataArray[i].points.slice(0,2))[0],y:e[1]};var r=this.dataArray[i],o=r.points;switch(r.command){case"L":return pi.getPointOnLine(t,r.start.x,r.start.y,o[0],o[1]);case"C":return pi.getPointOnCubicBezier(t/r.pathLength,r.start.x,r.start.y,o[0],o[1],o[2],o[3],o[4],o[5]);case"Q":return pi.getPointOnQuadraticBezier(t/r.pathLength,r.start.x,r.start.y,o[0],o[1],o[2],o[3]);case"A":var a=o[0],s=o[1],h=o[2],d=o[3],l=o[4],c=o[5],p=o[6];return l+=c*t/r.pathLength,pi.getPointOnEllipticalArc(a,s,h,d,l,p)}return null},pi.getLineLength=function(t,e,i,n){return Math.sqrt((i-t)*(i-t)+(n-e)*(n-e))},pi.getPointOnLine=function(t,e,i,n,r,o,a){void 0===o&&(o=e),void 0===a&&(a=i);var s=(r-i)/(n-e+1e-8),h=Math.sqrt(t*t/(1+s*s));n>>1,k=_.slice(0,1+P),T=this._getTextWidth(k)+v;T<=d?(b=1+P,w=k+(g?"…":""),C=T):S=P}if(!w)break;if(f){var A,M=_[w.length];0<(A=(" "===M||"-"===M)&&C<=d?w.length:Math.max(w.lastIndexOf(" "),w.lastIndexOf("-"))+1)&&(b=A,w=w.slice(0,b),C=this._getTextWidth(w))}if(w=w.trimRight(),this._addTextLine(w),i=Math.max(i,C),c+=n,!u||s&&le?g=ci.getPointOnLine(e,f.x,f.y,v.points[0],v.points[1],f.x,f.y):v=void 0;break;case"A":var a=v.points[4],s=v.points[5],h=v.points[4]+s;0===m?m=a+1e-8:iv.pathLength?1e-8:e/v.pathLength:ip.x?-1:1,f=this.findOne(".top-left").y()>p.y?-1:1;e=n*this.cos*u,i=n*this.sin*f,this.findOne(".top-left").x(p.x-e),this.findOne(".top-left").y(p.y-i)}}else if("top-center"===this._movingAnchorName)this.findOne(".top-left").y(r.y());else if("top-right"===this._movingAnchorName){l&&(p=c?{x:this.width()/2,y:this.height()/2}:{x:this.findOne(".bottom-left").x(),y:this.findOne(".bottom-left").y()},n=Math.sqrt(Math.pow(r.x()-p.x,2)+Math.pow(p.y-r.y(),2)),u=this.findOne(".top-right").x()p.y?-1:1,e=n*this.cos*u,i=n*this.sin*f,this.findOne(".top-right").x(p.x+e),this.findOne(".top-right").y(p.y-i));var g=r.position();this.findOne(".top-left").y(g.y),this.findOne(".bottom-right").x(g.x)}else"middle-left"===this._movingAnchorName?this.findOne(".top-left").x(r.x()):"middle-right"===this._movingAnchorName?this.findOne(".bottom-right").x(r.x()):"bottom-left"===this._movingAnchorName?(l&&(p=c?{x:this.width()/2,y:this.height()/2}:{x:this.findOne(".top-right").x(),y:this.findOne(".top-right").y()},n=Math.sqrt(Math.pow(p.x-r.x(),2)+Math.pow(r.y()-p.y,2)),u=p.x>z,0!==C?(C=255/C,k[s]=(d*N>>z)*C,k[s+1]=(l*N>>z)*C,k[s+2]=(c*N>>z)*C):k[s]=k[s+1]=k[s+2]=0,d-=u,l-=f,c-=g,p-=v,u-=F.r,f-=F.g,g-=F.b,v-=F.a,o=h+((o=i+e+1)>z,0>z)*C,k[o+1]=(l*N>>z)*C,k[o+2]=(c*N>>z)*C):k[o]=k[o+1]=k[o+2]=0,d-=u,l-=f,c-=g,p-=v,u-=F.r,f-=F.g,g-=F.b,v-=F.a,o=i+((o=n+O) 0 ? Math.acos(a / r) : -Math.acos(a / r); result.scaleX = r; result.scaleY = delta / r; - result.skewX = Math.atan((a * c + b * d) / (r * r)); + result.skewX = (a * c + b * d) / (r * r); result.skewY = 0; } else if (c != 0 || d != 0) { var s = Math.sqrt(c * c + d * d); @@ -353,7 +352,7 @@ export class Transform { result.scaleX = delta / s; result.scaleY = s; result.skewX = 0; - result.skewY = Math.atan((a * c + b * d) / (s * s)); + result.skewY = (a * c + b * d) / (s * s); } else { // a = b = c = d = 0 } @@ -362,6 +361,25 @@ export class Transform { return result; } + qrDecompose() { + var angle = Math.atan2(this.m[1], this.m[0]), + denom = Math.pow(this.m[0], 2) + Math.pow(this.m[1], 2), + scaleX = Math.sqrt(denom), + scaleY = (this.m[0] * this.m[3] - this.m[2] * this.m[1]) / scaleX, + skewX = Math.atan2(this.m[0] * this.m[2] + this.m[1] * this.m[3], denom); + + const rotation = Util._getRotation(angle); + + return { + rotation, + scaleX: scaleX, + scaleY: scaleY, + skewX: skewX / (Math.PI / 180), + skewY: 0, + x: this.m[4], + y: this.m[5] + }; + } } // CONSTANTS diff --git a/src/shapes/TextPath.ts b/src/shapes/TextPath.ts index da845fa4..87b824cc 100644 --- a/src/shapes/TextPath.ts +++ b/src/shapes/TextPath.ts @@ -660,7 +660,7 @@ Factory.addGetterSetter(TextPath, 'align', 'left'); Factory.addGetterSetter(TextPath, 'letterSpacing', 0, getNumberValidator()); /** - * get/set text baselineg. The default is 'middle'. Can be 'top', 'bottom', 'middle', 'alphabetic', 'hanging' + * get/set text baseline. The default is 'middle'. Can be 'top', 'bottom', 'middle', 'alphabetic', 'hanging' * @name Konva.TextPath#textBaseline * @method * @param {String} textBaseline diff --git a/src/shapes/Transformer.ts b/src/shapes/Transformer.ts index 9bcaae9c..b1f0ba00 100644 --- a/src/shapes/Transformer.ts +++ b/src/shapes/Transformer.ts @@ -480,7 +480,8 @@ export class Transformer extends Group { return this._getCache(NODES_RECT, this.__getNodeRect); } - __getNodeShape(node, rot = this.rotation(), relative?: Node) { + // return absolute rotated bounding rectangle + __getNodeShape(node: Node, rot = this.rotation(), relative?: Node) { var rect = node.getClientRect({ skipTransform: true, skipShadow: true, @@ -522,15 +523,60 @@ export class Transformer extends Group { }; } - const shapes = this.nodes().map(node => { - return this.__getNodeShape(node); + const totalPoints = []; + this.nodes().map(node => { + const box = node.getClientRect({ + skipTransform: true, + skipShadow: true, + skipStroke: this.ignoreStroke() + }); + var points = [ + { x: box.x, y: box.y }, + { x: box.x + box.width, y: box.y }, + { x: box.x + box.width, y: box.y + box.height }, + { x: box.x, y: box.y + box.height } + ]; + var trans = node.getAbsoluteTransform(); + points.forEach(function(point) { + var transformed = trans.point(point); + totalPoints.push(transformed); + }); }); - const box = getShapesRect(shapes); - return rotateAroundPoint(box, Konva.getAngle(this.rotation()), { - x: 0, - y: 0 + const tr = new Transform(); + tr.rotate(-Konva.getAngle(this.rotation())); + + var minX: number, minY: number, maxX: number, maxY: number; + totalPoints.forEach(function(point) { + var transformed = tr.point(point); + if (minX === undefined) { + minX = maxX = transformed.x; + minY = maxY = transformed.y; + } + minX = Math.min(minX, transformed.x); + minY = Math.min(minY, transformed.y); + maxX = Math.max(maxX, transformed.x); + maxY = Math.max(maxY, transformed.y); }); + + tr.invert(); + const p = tr.point({ x: minX, y: minY }); + return { + x: p.x, + y: p.y, + width: maxX - minX, + height: maxY - minY, + rotation: Konva.getAngle(this.rotation()) + }; + // const shapes = this.nodes().map(node => { + // return this.__getNodeShape(node); + // }); + + // const box = getShapesRect(shapes); + // return rotateAroundPoint(box, Konva.getAngle(this.rotation()), { + // x: 0, + // y: 0 + // }); } getX() { return this._getNodeRect().x; @@ -1006,23 +1052,36 @@ export class Transformer extends Group { return; } } + + // const pure = this.findOne('.back').getClientRect({ + // skipStroke: true + // }); + + // var scaleX = pure.width ? newAttrs.width / pure.width : 1; + // var scaleY = pure.height ? newAttrs.height / pure.height : 1; + // var dx = pure.x * scaleX; + // var dy = pure.y * scaleY; + // let's find delta transform - var dx = newAttrs.x - oldAttrs.x, - dy = newAttrs.y - oldAttrs.y, - angle = newAttrs.rotation - oldAttrs.rotation, - scaleX = newAttrs.width / oldAttrs.width, - scaleY = newAttrs.height / oldAttrs.height; + // var dx = newAttrs.x - oldAttrs.x, + // dy = newAttrs.y - oldAttrs.y, + // var angle = newAttrs.rotation - oldAttrs.rotation; + // scaleX = newAttrs.width / oldAttrs.width, + // scaleY = newAttrs.height / oldAttrs.height; - // dt1.invert(); + // const x = newAttrs.x - (dx * Math.cos(angle) + dy * Math.sin(-angle)); + // const y = newAttrs.y - (dy * Math.cos(angle) + dx * Math.sin(angle)); - // const dt2 = new Transform(); - // dt2.translate(newAttrs.x, newAttrs.y); - // dt2.rotate(newAttrs.rotation); + // // dt1.invert(); + + // const tr = new Transform(); + // tr.translate(x, y); + // tr.rotate(angle); // // console.log(dt.point(newAttrs)); // // dt.translate(newAttrs.x, newAttrs.y); // // console.log(dt.decompose()); // // dt.rotate(newAttrs.rotation); - // dt2.scale(scaleX, scaleY); + // tr.scale(scaleX, scaleY); // dt1.multiply(dt2); @@ -1037,116 +1096,44 @@ export class Transformer extends Group { // debugger; // } + const base = 10000000; + const oldTr = new Transform(); + oldTr.translate(oldAttrs.x, oldAttrs.y); + oldTr.rotate(oldAttrs.rotation); + oldTr.scale(oldAttrs.width / base, oldAttrs.height / base); + + const newTr = new Transform(); + newTr.translate(newAttrs.x, newAttrs.y); + newTr.rotate(newAttrs.rotation); + newTr.scale(newAttrs.width / base, newAttrs.height / base); + + const delta = newTr.multiply(oldTr.invert()); + this._nodes.forEach(node => { - // var oldRect = this.__getNodeShape(node, 0); - // var newRect = transformAndRotateShape(oldRect, oldAttrs, newAttrs); - // this._fitNodeInto(node, newRect, evt); + const pt = node.getParent().getAbsoluteTransform(); + const selfTr = node.getTransform().copy(); + selfTr.translate(node.offsetX(), node.offsetY()); - // var m = new Transform(), - // x = node.x(), - // y = node.y(), - // rotation = Konva.getAngle(node.rotation()), - // scaleX = node.scaleX(), - // scaleY = node.scaleY(), - // skewX = node.skewX(), - // skewY = node.skewY(), - // offsetX = node.offsetX(), - // offsetY = node.offsetY(); + const newLocal = new Transform(); + newLocal + .multiply(delta) + .multiply(pt) + .multiply(pt.copy().invert()) + .multiply(selfTr); - // // if (x !== 0 || y !== 0) { - // // m.translate(x, y); - // // } - // if (rotation !== 0) { - // m.rotate(rotation); + // node._cache.set('transform', newLocal); + // node._cache.set('absoluteTransform', newLocal); + + // console.log(); + + const attrs = newLocal.decompose(); + // if (Math.abs(attrs.skewX - node.skewX()) < 0.00000001) { + // attrs.skewX = node.skewX(); // } - // if (skewX !== 0 || skewY !== 0) { - // m.skew(skewX, skewY); + // if (Math.abs(attrs.skewY - node.skewY()) < 0.00000001) { + // attrs.skewY = node.skewY(); // } - // if (scaleX !== 1 || scaleY !== 1) { - // m.scale(scaleX, scaleY); - // } - // if (offsetX !== 0 || offsetY !== 0) { - // m.translate(-1 * offsetX, -1 * offsetY); - // } - - // return m; - - // t.translate(dx, dy); - // t.rotate(Konva.getAngle(node.rotation()) + angle); - // t.scale(node.scaleX() * scaleX, node.scaleY() * scaleY); - // const t = node.getTransform(); - - // const dt1 = new Transform(); - // dt1.translate(dx, dy); - // dt1.rotate(angle); - // dt1.scale(scaleX, scaleY); - - // // const oldScale = { - // // x: dt.decompose().scaleX, - // // y: dt.decompose().scaleY - // // }; - // // dt.scale(1, 1); - // t.multiply(dt1); - // dt.scale(oldScale.x, oldScale.y); - // t.translate( - // dt.getTranslation().x * t.decompose().scaleX, - // dt.getTranslation().y * t.decompose().scaleY - // ); - // dt.set - // dt.setAbsolutePosition(0, 0); - // t.multiply(dt); - // console.log( - // node.rotation(), - // dt.decompose().rotation, - // t.decompose().rotation, - // Util._getRotation(newAttrs.rotation) - // ); - // m.multiply(dt); - // m.translate(x + dx, y + dy); - // node.setAttrs(t.decompose()); - // if (offsetX !== 0 || offsetY !== 0) { - // t.translate( - // node.offsetX() * (1 - node.scaleX() / scaleX), - // node.offsetY() * (1 - node.scaleY() / scaleY) - // ); - const parentRot = Konva.getAngle(node.getParent().getAbsoluteRotation()); - const rotation = Util._getRotation(newAttrs.rotation - parentRot); - - var pure = node.getClientRect({ - skipShadow: true, - skipStroke: this.ignoreStroke() - }); - - const parentTransform = node - .getParent() - .getAbsoluteTransform() - .copy(); - parentTransform.invert(); - const invertedPoint = parentTransform.point({ - x: newAttrs.x, - y: newAttrs.y - }); - newAttrs.x = invertedPoint.x; - newAttrs.y = invertedPoint.y; - var absScale = node.getParent().getAbsoluteScale(); - - pure.width *= absScale.x; - pure.height *= absScale.y; - - var scaleX = pure.width ? newAttrs.width / pure.width : 1; - var scaleY = pure.height ? newAttrs.height / pure.height : 1; - - var dx = pure.x * scaleX - node.offsetX() * scaleX; - var dy = pure.y * scaleY - node.offsetY() * scaleY; - - var x = newAttrs.x - (dx * Math.cos(rotation) + dy * Math.sin(-rotation)); - var y = newAttrs.y - (dy * Math.cos(rotation) + dx * Math.sin(rotation)); - var tr = new Transform(); - tr.translate(x, y); - tr.rotate(rotation); - tr.scale(scaleX, scaleY); - - node.setAttrs(tr.multiply(node.getTransform()).decompose()); + node.setAttrs(attrs); }); this.rotation(Util._getRotation(newAttrs.rotation)); this._resetTransformCache(); @@ -1154,6 +1141,29 @@ export class Transformer extends Group { this.getLayer().batchDraw(); } _fitNodeInto(node: Node, newAttrs, evt) { + var pure = node.getClientRect({ + skipTransform: true, + skipShadow: true, + skipStroke: this.ignoreStroke() + }); + + const parentTransform = node + .getParent() + .getAbsoluteTransform() + .copy(); + parentTransform.invert(); + const invertedPoint = parentTransform.point({ + x: newAttrs.x, + y: newAttrs.y + }); + + var absScale = node.getParent().getAbsoluteScale(); + + newAttrs.x = invertedPoint.x; + newAttrs.y = invertedPoint.y; + newAttrs.width /= absScale.x; + newAttrs.height /= absScale.y; + if (this.boundBoxFunc()) { const oldAttrs = this.__getNodeShape( node, @@ -1173,28 +1183,8 @@ export class Transformer extends Group { const parentRot = Konva.getAngle(node.getParent().getAbsoluteRotation()); node.rotation(Util._getRotation(newAttrs.rotation - parentRot)); - var pure = node.getClientRect({ - skipTransform: true, - skipShadow: true, - skipStroke: this.ignoreStroke() - }); - - const parentTransform = node - .getParent() - .getAbsoluteTransform() - .copy(); - parentTransform.invert(); - const invertedPoint = parentTransform.point({ - x: newAttrs.x, - y: newAttrs.y - }); - newAttrs.x = invertedPoint.x; - newAttrs.y = invertedPoint.y; var absScale = node.getParent().getAbsoluteScale(); - pure.width *= absScale.x; - pure.height *= absScale.y; - var scaleX = pure.width ? newAttrs.width / pure.width : 1; var scaleY = pure.height ? newAttrs.height / pure.height : 1; diff --git a/test/unit/Shape-test.js b/test/unit/Shape-test.js index 48f694a6..1298f9fe 100644 --- a/test/unit/Shape-test.js +++ b/test/unit/Shape-test.js @@ -1492,6 +1492,37 @@ suite('Shape', function() { assert.equal(absRect.height, 100); }); + test.only('getClientRect with skew', function() { + var stage = addStage(); + var layer = new Konva.Layer(); + stage.add(layer); + + var shape = new Konva.Rect({ + x: 150, + y: 50, + width: 100, + height: 100, + scaleX: 0.63, + skewX: -1.6, + fill: 'green' + }); + layer.add(shape); + + var back = new Konva.Rect({ + stroke: 'red' + }); + back.setAttrs(shape.getClientRect()); + layer.add(back); + layer.draw(); + + var absRect = shape.getClientRect(); + + assert.equal(absRect.x, 10); + assert.equal(absRect.y, 0); + assert.equal(absRect.width, 100); + assert.equal(absRect.height, 100); + }); + test('shadow should respect pixel ratio', function() { var stage = addStage(); var layer = new Konva.Layer(); diff --git a/test/unit/shapes/TextPath-test.js b/test/unit/shapes/TextPath-test.js index c1ed72e9..63fafefa 100644 --- a/test/unit/shapes/TextPath-test.js +++ b/test/unit/shapes/TextPath-test.js @@ -315,6 +315,32 @@ suite('TextPath', function() { assert.equal(layer.getContext().getTrace(true), trace); }); + test.skip('Text path with center align - arc', function() { + var stage = addStage(); + var layer = new Konva.Layer(); + + var textpath = new Konva.TextPath({ + fill: '#333', + fontSize: 20, + text: 'Hello World', + align: 'right', + data: 'M 50 200 a 100 100 0 0 1 200 0' + }); + layer.add(textpath); + + var path = new Konva.Path({ + stroke: '#000', + data: 'M 50 200 a 100 100 0 0 1 200 0' + }); + layer.add(path); + stage.add(layer); + + var trace = + 'restore();save();translate();rotate();fillStyle;fillText();restore();save();translate();rotate();fillStyle;fillText();restore();save();translate();rotate();fillStyle;fillText();restore();save();translate();rotate();fillStyle;fillText();restore();save();translate();rotate();fillStyle;fillText();restore();save();translate();rotate();fillStyle;fillText();restore();save();translate();rotate();fillStyle;fillText();restore();save();translate();rotate();fillStyle;fillText();restore();save();translate();rotate();fillStyle;fillText();restore();save();translate();rotate();fillStyle;fillText();restore();save();translate();rotate();fillStyle;fillText();restore();save();translate();rotate();fillStyle;fillText();restore();save();translate();rotate();fillStyle;fillText();restore();save();translate();rotate();fillStyle;fillText();restore();save();translate();rotate();fillStyle;fillText();restore();save();translate();rotate();fillStyle;fillText();restore();restore();restore();'; + + assert.equal(layer.getContext().getTrace(true), trace); + }); + test('Text path with align right', function() { var stage = addStage(); var layer = new Konva.Layer(); diff --git a/test/unit/shapes/Transformer-test.js b/test/unit/shapes/Transformer-test.js index 54df6401..3f34e2cf 100644 --- a/test/unit/shapes/Transformer-test.js +++ b/test/unit/shapes/Transformer-test.js @@ -1,4 +1,4 @@ -suite.only('Transformer', function() { +suite('Transformer', function() { function isClose(a, b) { return Math.abs(a - b) < 0.000001; } @@ -138,7 +138,7 @@ suite.only('Transformer', function() { layer.draw(); }); - test.only('try to fit simple rectangle', function() { + test('try to fit simple rectangle', function() { var stage = addStage(); var layer = new Konva.Layer(); stage.add(layer); @@ -159,21 +159,23 @@ suite.only('Transformer', function() { layer.draw(); - // tr._fitNodesInto({ - // x: 120, - // y: 60, - // width: 50, - // height: 50, - // rotation: Konva.getAngle(45) - // }); + tr._fitNodesInto({ + x: 120, + y: 60, + width: 50, + height: 50, + rotation: Konva.getAngle(45) + }); - // assert.equal(tr.x(), rect.x()); - // assert.equal(Math.round(tr.y()), rect.y()); - // assert.equal(tr.width(), 50); - // assert.equal(tr.height(), 50); - // assert.equal(tr.rotation(), rect.rotation()); + assert.equal(tr.x(), rect.x()); + assert.equal(Math.round(tr.y()), rect.y()); + assert.equal(tr.width(), 50); + assert.equal(tr.height(), 50); + assert.equal(tr.rotation(), rect.rotation()); }); + + test('try to fit simple rotated rectangle', function() { var stage = addStage(); var layer = new Konva.Layer(); @@ -351,6 +353,43 @@ suite.only('Transformer', function() { layer.draw(); }); + test.only('try to fit rectangle with skew', function() { + var stage = addStage(); + var layer = new Konva.Layer(); + stage.add(layer); + + var rect = new Konva.Rect({ + x: 100, + y: 60, + draggable: true, + width: 100, + height: 100, + fill: 'yellow', + skewX: 1, + }); + layer.add(rect); + + var tr = new Konva.Transformer(); + layer.add(tr); + tr.nodes([rect]); + + layer.draw(); + + // tr._fitNodesInto({ + // x: 120, + // y: 60, + // width: 50, + // height: 50, + // rotation: Konva.getAngle(45) + // }); + + // assert.equal(tr.x(), rect.x()); + // assert.equal(Math.round(tr.y()), rect.y()); + // assert.equal(tr.width(), 50); + // assert.equal(tr.height(), 50); + // assert.equal(tr.rotation(), rect.rotation()); + }); + test('try to resize in draggable stage', function() { var stage = addStage(); stage.draggable(true); @@ -452,13 +491,13 @@ suite.only('Transformer', function() { tr._fitNodesInto(box); - assert.equal(rect.x(), 100); - assert.equal(rect.y(), 0); - assert.equal(rect.width(), 100); - assert.equal(rect.scaleX(), -1); - assert.equal(rect.height(), 100); - assert.equal(rect.scaleY(), 1); - assert.equal(rect.rotation(), 0); + assert.almostEqual(rect.x(), 100); + assert.almostEqual(rect.y(), 0); + assert.almostEqual(rect.width(), 100); + assert.almostEqual(rect.scaleX(), 1); + assert.almostEqual(rect.height(), 100); + assert.almostEqual(rect.scaleY(), -1); + assert.almostEqual(rect.rotation(), -180); layer.draw(); }); @@ -497,11 +536,11 @@ suite.only('Transformer', function() { rotation: 0 }); - assert.equal(rect.x(), 20); - assert.equal(rect.y(), 20); - assert.equal(rect.width(), 100); - assert.equal(rect.height(), 100); - assert.equal(rect.scaleX(), 2); + assert.almostEqual(rect.x(), 20); + assert.almostEqual(rect.y(), 20); + assert.almostEqual(rect.width(), 100); + assert.almostEqual(rect.height(), 100); + assert.almostEqual(rect.scaleX(), 2); }); test('listen shape changes', function() { @@ -569,7 +608,7 @@ suite.only('Transformer', function() { assert.equal(tr.rotation(), rect.rotation()); }); - test.only('try to fit a transformed rect', function() { + test('try to fit a transformed rect', function() { var stage = addStage(); var layer = new Konva.Layer(); stage.add(layer); @@ -600,11 +639,11 @@ suite.only('Transformer', function() { rotation: 0 }); - assert.equal(rect.x(), 100); - assert.equal(rect.y(), 70); - assert.equal(rect.width() * rect.scaleX(), 100); - assert.equal(rect.height() * rect.scaleY(), 100); - assert.equal(rect.rotation(), rect.rotation()); + assert.almostEqual(rect.x(), 100); + assert.almostEqual(rect.y(), 70); + assert.almostEqual(rect.width() * rect.scaleX(), 100); + assert.almostEqual(rect.height() * rect.scaleY(), 100); + assert.almostEqual(rect.rotation(), rect.rotation()); }); test('add transformer for transformed rect with offset', function() { @@ -670,17 +709,17 @@ suite.only('Transformer', function() { }); layer.draw(); - assert.equal(rect.x(), 100); - assert.equal(rect.y(), 50); - assert.equal(rect.width() * rect.scaleX(), 200); - assert.equal(rect.height() * rect.scaleY(), 100); - assert.equal(rect.rotation(), rect.rotation()); + assert.almostEqual(rect.x(), 100); + assert.almostEqual(rect.y(), 50); + assert.almostEqual(rect.width() * rect.scaleX(), 200); + assert.almostEqual(rect.height() * rect.scaleY(), 100); + assert.almostEqual(rect.rotation(), rect.rotation()); - assert.equal(tr.x(), 0); - assert.equal(tr.y(), 0); - assert.equal(tr.width(), 200); - assert.equal(tr.height(), 100); - assert.equal(rect.rotation(), rect.rotation()); + assert.almostEqual(tr.x(), 0); + assert.almostEqual(tr.y(), 0); + assert.almostEqual(tr.width(), 200); + assert.almostEqual(tr.height(), 100); + assert.almostEqual(rect.rotation(), rect.rotation()); }); test('add transformer for circle', function() { @@ -938,16 +977,16 @@ suite.only('Transformer', function() { var rect = group.getClientRect(); - assert.equal(group.x(), 50); - assert.equal(group.y(), 50); - assert.equal(rect.width, 100); - assert.equal(rect.height, 100); - assert.equal(group.rotation(), 90); + assert.almostEqual(group.x(), 50); + assert.almostEqual(group.y(), 50); + assert.almostEqual(rect.width, 100); + assert.almostEqual(rect.height, 100); + assert.almostEqual(group.rotation(), 90); - assert.equal(tr.x(), 100); - assert.equal(tr.y(), 0); - assert.equal(tr.width(), 100); - assert.equal(tr.height(), 100); + assert.almostEqual(tr.x(), 100); + assert.almostEqual(tr.y(), 0); + assert.almostEqual(tr.width(), 100); + assert.almostEqual(tr.height(), 100); }); test('add transformer to another group', function() { @@ -1040,15 +1079,15 @@ suite.only('Transformer', function() { var rect = group.getClientRect(); - assert.equal(group.x(), 100); - assert.equal(group.y(), 50); - assert.equal(rect.width, 200); - assert.equal(rect.height, 100); + assert.almostEqual(group.x(), 100); + assert.almostEqual(group.y(), 50); + assert.almostEqual(rect.width, 200); + assert.almostEqual(rect.height, 100); - assert.equal(tr.x(), 0); - assert.equal(tr.y(), 0); - assert.equal(tr.width(), 200); - assert.equal(tr.height(), 100); + assert.almostEqual(tr.x(), 0); + assert.almostEqual(tr.y(), 0); + assert.almostEqual(tr.width(), 200); + assert.almostEqual(tr.height(), 100); }); test('toJSON should not save attached node and children', function() { @@ -1203,12 +1242,12 @@ suite.only('Transformer', function() { y: 150 }); - assert.equal(rect.x(), 80); - assert.equal(rect.y(), 30); - assert.equal(rect.width(), 100); - assert.equal(rect.scaleX(), 0.5); - assert.equal(rect.height(), 100); - assert.equal(rect.scaleY(), 1); + assert.almostEqual(rect.x(), 80); + assert.almostEqual(rect.y(), 30); + assert.almostEqual(rect.width(), 100); + assert.almostEqual(rect.scaleX(), 0.5); + assert.almostEqual(rect.height(), 100); + assert.almostEqual(rect.scaleY(), 1); }); test('keep ratio should allow negative scaling', function() { @@ -1235,28 +1274,22 @@ suite.only('Transformer', function() { var anchor = tr.findOne('.top-right'); var pos = anchor.getAbsolutePosition(); - stage.simulateMouseDown({ + tr.simulateMouseDown({ x: pos.x, y: pos.y }); - var box = stage.content.getBoundingClientRect(); - tr._handleMouseMove({ - clientX: box.left + pos.x - 100, - clientY: box.top + pos.y + 100 + tr.simulateMouseMove({ + x: pos.x - 100, + y: pos.y + 100 }); - - // here is duplicate, because transformer is listening window events - tr._handleMouseUp({ - clientX: box.left + pos.x - 100, - clientY: box.top + pos.y + 100 - }); - stage.simulateMouseUp({ + tr.simulateMouseUp({ x: pos.x - 100, y: pos.y + 100 }); - assert.equal(rect.scaleX(), -1); - assert.equal(rect.scaleY(), -1); + assert.almostEqual(rect.scaleX(), 1); + assert.almostEqual(rect.scaleY(), 1); + assert.almostEqual(rect.rotation(), -180); }); test.skip('visual test', function(done) { @@ -1349,24 +1382,24 @@ suite.only('Transformer', function() { x: 0, y: 199 }); - assert.equal(rect.rotation(), 270); + assert.almostEqual(rect.rotation(), -90); tr.simulateMouseMove({ x: 0, y: 50 }); - assert.equal(rect.rotation(), 270); + assert.almostEqual(rect.rotation(), -90); tr.simulateMouseMove({ x: 0, y: 45 }); - assert.equal(rect.rotation(), 270); + assert.almostEqual(rect.rotation(), -90); tr.simulateMouseMove({ x: 0, y: 1 }); - assert.equal(rect.rotation(), 270); + assert.almostEqual(rect.rotation(), -90); tr.simulateMouseMove({ x: 1, @@ -1410,12 +1443,12 @@ suite.only('Transformer', function() { y: 60 }); - assert.equal(rect.x(), 115); - assert.equal(rect.y(), 10); - assert.equal(rect.width(), 100); - assert.equal(rect.scaleX(), -0.05); - assert.equal(rect.height(), 100); - assert.equal(rect.rotation(), 0); + assert.almostEqual(rect.x(), 115); + assert.almostEqual(rect.y(), 10); + assert.almostEqual(rect.width(), 100); + assert.almostEqual(rect.scaleX(), -0.05); + assert.almostEqual(rect.height(), 100); + assert.almostEqual(rect.rotation(), 0); tr.simulateMouseMove({ x: 125, @@ -1478,24 +1511,24 @@ suite.only('Transformer', function() { y: 125 }); - assert.equal(rect.x(), 10); - assert.equal(rect.y(), 115); - assert.equal(rect.width(), 100); - assert.equal(rect.scaleY(), -0.05); - assert.equal(rect.height(), 100); - assert.equal(rect.rotation(), 0); + assert.almostEqual(rect.x(), 10); + assert.almostEqual(rect.y(), 115); + assert.almostEqual(rect.width(), 100); + assert.almostEqual(rect.scaleY(), -0.05); + assert.almostEqual(rect.height(), 100); + assert.almostEqual(rect.rotation(), 0); tr.simulateMouseMove({ x: 60, y: 125 }); - assert.equal(rect.x(), 10); - assert.equal(rect.y(), 115); - assert.equal(rect.width(), 100); - assert.equal(rect.scaleY(), -0.05); - assert.equal(rect.height(), 100); - assert.equal(rect.rotation(), 0); + assert.almostEqual(rect.x(), 10); + assert.almostEqual(rect.y(), 115); + assert.almostEqual(rect.width(), 100); + assert.almostEqual(rect.scaleY(), -0.05); + assert.almostEqual(rect.height(), 100); + assert.almostEqual(rect.rotation(), 0); // switch again tr.simulateMouseMove({ @@ -1503,13 +1536,13 @@ suite.only('Transformer', function() { y: 90 }); - assert.equal(rect.x(), 10); - assert.equal(rect.y(), 100); - assert.equal(rect.width(), 100); - assert.equal(rect.scaleX(), 1); - assert.equal(rect.scaleY(), 0.1); - assert.equal(rect.height(), 100); - assert.equal(rect.rotation(), 0); + assert.almostEqual(rect.x(), 10); + assert.almostEqual(rect.y(), 100); + assert.almostEqual(rect.width(), 100); + assert.almostEqual(rect.scaleX(), 1); + assert.almostEqual(rect.scaleY(), 0.1); + assert.almostEqual(rect.height(), 100); + assert.almostEqual(rect.rotation(), 0); tr.simulateMouseUp(); }); @@ -2884,8 +2917,6 @@ suite.only('Transformer', function() { }); layer.draw(); - console.log(start, end); - // move from start to end tr.simulateMouseDown(start); tr.simulateMouseMove(end); @@ -4186,7 +4217,12 @@ suite.only('Transformer', function() { test('boundBoxFox should work in local coordinates', function() { var stage = addStage(); - var layer = new Konva.Layer(); + var layer = new Konva.Layer({ + x: 10, + y: 10, + scaleX: 2, + scaleY: 2 + }); stage.add(layer); var rect1 = new Konva.Rect({ @@ -4221,6 +4257,13 @@ suite.only('Transformer', function() { height: 50, rotation: 0 }); + assert.deepEqual(newBox, { + x: 0, + y: 0, + width: 50, + height: 50, + rotation: 0 + }); } else { assert.deepEqual(oldBox, { x: 50, @@ -4229,6 +4272,13 @@ suite.only('Transformer', function() { height: 50, rotation: 0 }); + assert.deepEqual(newBox, { + x: 50, + y: 50, + width: 50, + height: 50, + rotation: 0 + }); } return newBox; } @@ -4236,10 +4286,10 @@ suite.only('Transformer', function() { layer.add(tr); tr._fitNodesInto({ - x: 0, - y: 0, - width: 100, - height: 100, + x: 10, + y: 10, + width: 200, + height: 200, rotation: 0 }); });