diff --git a/dist/kinetic-core.js b/dist/kinetic-core.js index a887fabd..09263c43 100644 --- a/dist/kinetic-core.js +++ b/dist/kinetic-core.js @@ -3,7 +3,7 @@ * http://www.kineticjs.com/ * Copyright 2012, Eric Rowell * Licensed under the MIT or GPL Version 2 licenses. - * Date: Mar 22 2012 + * Date: Mar 23 2012 * * Copyright (C) 2011 - 2012 by Eric Rowell * @@ -52,6 +52,10 @@ Kinetic.GlobalObject = { offset: { x: 0, y: 0 + }, + start: { + x: 0, + y: 0 } }, extend: function(obj1, obj2) { @@ -754,6 +758,35 @@ Kinetic.Node.prototype = { getDragBounds: function() { return this.dragBounds; }, + /** + * get matrix transform of the node while taking into + * account the matrix transforms of its parents + */ + getAbsoluteMatrix: function() { + // absolute matrix + var am = new Kinetic.Matrix(); + + var family = []; + var parent = this.parent; + + family.unshift(this); + while(parent) { + family.unshift(parent); + parent = parent.parent; + } + + for(var n = 0; n < family.length; n++) { + var node = family[n]; + var m = node.getMatrix(); + am.multiply(m); + } + + return am; + }, + /** + * get matrix transform of the node while not taking + * into account the matrix transforms of its parents + */ getMatrix: function() { var m = new Kinetic.Matrix(); @@ -783,9 +816,13 @@ Kinetic.Node.prototype = { var pos = stage.getUserPosition(); if(pos) { + var m = that.getMatrix().getTranslation(); + var am = that.getAbsoluteMatrix().getTranslation(); go.drag.node = that; go.drag.offset.x = pos.x - that.x; go.drag.offset.y = pos.y - that.y; + go.drag.start.x = m.x - am.x; + go.drag.start.y = m.y - am.y; } }); }, @@ -965,7 +1002,7 @@ Kinetic.Stage = function(config) { * if container is a string, assume it's an id for * a DOM element */ - if(typeof config.container === 'string') { + if( typeof config.container === 'string') { config.container = document.getElementById(config.container); } @@ -1536,18 +1573,21 @@ Kinetic.Stage.prototype = { var node = go.drag.node; if(node) { var pos = that.getUserPosition(); - var ds = node.dragConstraint; + var dc = node.dragConstraint; var db = node.dragBounds; - if(ds === 'none' || ds === 'horizontal') { + var m = node.getMatrix().getTranslation(); + var am = node.getAbsoluteMatrix().getTranslation(); + + if(dc === 'none' || dc === 'horizontal') { var newX = pos.x - go.drag.offset.x; if((db.left === undefined || db.left < newX) && (db.right === undefined || db.right > newX)) { - node.x = newX; + node.x = newX + m.x - (am.x + go.drag.start.x); } } - if(ds === 'none' || ds === 'vertical') { + if(dc === 'none' || dc === 'vertical') { var newY = pos.y - go.drag.offset.y; if((db.top === undefined || db.top < newY) && (db.bottom === undefined || db.bottom > newY)) { - node.y = newY; + node.y = newY + m.y - (am.y + go.drag.start.y); } } go.drag.node.getLayer().draw(); @@ -1634,13 +1674,17 @@ Kinetic.Layer = function(config) { */ Kinetic.Layer.prototype = { /** - * public draw children + * draw children nodes. this includes any groups + * or shapes */ draw: function() { this._draw(); }, /** - * clear layer + * clears the canvas context tied to the layer. Clearing + * a layer does not remove its children. The nodes within + * the layer will be redrawn whenever the .draw() method + * is used again. */ clear: function() { var context = this.getContext(); @@ -1660,7 +1704,8 @@ Kinetic.Layer.prototype = { return this.context; }, /** - * add node to layer + * add a node to the layer. New nodes are always + * placed at the top. * @param {Node} node */ add: function(child) { @@ -1859,12 +1904,13 @@ Kinetic.Shape.prototype = { family.unshift(parent); parent = parent.parent; } - + context.save(); for(var n = 0; n < family.length; n++) { var node = family[n]; - var m = node.getMatrix(); - m.transformContext(context); + var m = node.getMatrix().toArray(); + context.transform(m[0], m[1], m[2], m[3], m[4], m[5]); + if(node.getAbsoluteAlpha() !== 1) { context.globalAlpha = node.getAbsoluteAlpha(); } @@ -2494,8 +2540,7 @@ Kinetic.GlobalObject.extend(Kinetic.Text, Kinetic.Shape); /* * The usage of this class was inspired by some of the work done by a forked * project, KineticJS-Ext by Wappworks, which is based on Simon's Transform -* class. KineticJS has slightly modified the original class and added new methods -* specific for canvas. +* class. */ /** @@ -2543,11 +2588,41 @@ Kinetic.Matrix.prototype = { this.m[3] = m22; }, /** - * transform canvas context + * Returns the translation + * @returns {Object} 2D point(x, y) */ - transformContext: function(context) { - var m = this.m; - context.transform(m[0], m[1], m[2], m[3], m[4], m[5]); + getTranslation: function() { + return { + x: this.m[4], + y: this.m[5] + }; + }, + /** + * Transform multiplication + * @param {Kinetic.Matrix} matrix + */ + multiply: function(matrix) { + var m11 = this.m[0] * matrix.m[0] + this.m[2] * matrix.m[1]; + var m12 = this.m[1] * matrix.m[0] + this.m[3] * matrix.m[1]; + + var m21 = this.m[0] * matrix.m[2] + this.m[2] * matrix.m[3]; + var m22 = this.m[1] * matrix.m[2] + this.m[3] * matrix.m[3]; + + var dx = this.m[0] * matrix.m[4] + this.m[2] * matrix.m[5] + this.m[4]; + var dy = this.m[1] * matrix.m[4] + this.m[3] * matrix.m[5] + this.m[5]; + + this.m[0] = m11; + this.m[1] = m12; + this.m[2] = m21; + this.m[3] = m22; + this.m[4] = dx; + this.m[5] = dy; + }, + /** + * return matrix as array + */ + toArray: function() { + return this.m; } }; diff --git a/src/GlobalObject.js b/src/GlobalObject.js index 9d3a15d2..dfe17083 100644 --- a/src/GlobalObject.js +++ b/src/GlobalObject.js @@ -24,6 +24,10 @@ Kinetic.GlobalObject = { offset: { x: 0, y: 0 + }, + start: { + x: 0, + y: 0 } }, extend: function(obj1, obj2) { diff --git a/src/Layer.js b/src/Layer.js index 063a1662..12b0277a 100644 --- a/src/Layer.js +++ b/src/Layer.js @@ -26,13 +26,17 @@ Kinetic.Layer = function(config) { */ Kinetic.Layer.prototype = { /** - * public draw children + * draw children nodes. this includes any groups + * or shapes */ draw: function() { this._draw(); }, /** - * clear layer + * clears the canvas context tied to the layer. Clearing + * a layer does not remove its children. The nodes within + * the layer will be redrawn whenever the .draw() method + * is used again. */ clear: function() { var context = this.getContext(); @@ -52,7 +56,8 @@ Kinetic.Layer.prototype = { return this.context; }, /** - * add node to layer + * add a node to the layer. New nodes are always + * placed at the top. * @param {Node} node */ add: function(child) { diff --git a/src/Matrix.js b/src/Matrix.js index 1fb50e9a..facbd38a 100644 --- a/src/Matrix.js +++ b/src/Matrix.js @@ -11,8 +11,7 @@ /* * The usage of this class was inspired by some of the work done by a forked * project, KineticJS-Ext by Wappworks, which is based on Simon's Transform -* class. KineticJS has slightly modified the original class and added new methods -* specific for canvas. +* class. */ /** @@ -60,10 +59,40 @@ Kinetic.Matrix.prototype = { this.m[3] = m22; }, /** - * transform canvas context + * Returns the translation + * @returns {Object} 2D point(x, y) */ - transformContext: function(context) { - var m = this.m; - context.transform(m[0], m[1], m[2], m[3], m[4], m[5]); + getTranslation: function() { + return { + x: this.m[4], + y: this.m[5] + }; + }, + /** + * Transform multiplication + * @param {Kinetic.Matrix} matrix + */ + multiply: function(matrix) { + var m11 = this.m[0] * matrix.m[0] + this.m[2] * matrix.m[1]; + var m12 = this.m[1] * matrix.m[0] + this.m[3] * matrix.m[1]; + + var m21 = this.m[0] * matrix.m[2] + this.m[2] * matrix.m[3]; + var m22 = this.m[1] * matrix.m[2] + this.m[3] * matrix.m[3]; + + var dx = this.m[0] * matrix.m[4] + this.m[2] * matrix.m[5] + this.m[4]; + var dy = this.m[1] * matrix.m[4] + this.m[3] * matrix.m[5] + this.m[5]; + + this.m[0] = m11; + this.m[1] = m12; + this.m[2] = m21; + this.m[3] = m22; + this.m[4] = dx; + this.m[5] = dy; + }, + /** + * return matrix as array + */ + toArray: function() { + return this.m; } }; diff --git a/src/Node.js b/src/Node.js index d326a193..88e9fb5b 100644 --- a/src/Node.js +++ b/src/Node.js @@ -507,6 +507,35 @@ Kinetic.Node.prototype = { getDragBounds: function() { return this.dragBounds; }, + /** + * get matrix transform of the node while taking into + * account the matrix transforms of its parents + */ + getAbsoluteMatrix: function() { + // absolute matrix + var am = new Kinetic.Matrix(); + + var family = []; + var parent = this.parent; + + family.unshift(this); + while(parent) { + family.unshift(parent); + parent = parent.parent; + } + + for(var n = 0; n < family.length; n++) { + var node = family[n]; + var m = node.getMatrix(); + am.multiply(m); + } + + return am; + }, + /** + * get matrix transform of the node while not taking + * into account the matrix transforms of its parents + */ getMatrix: function() { var m = new Kinetic.Matrix(); @@ -536,9 +565,13 @@ Kinetic.Node.prototype = { var pos = stage.getUserPosition(); if(pos) { + var m = that.getMatrix().getTranslation(); + var am = that.getAbsoluteMatrix().getTranslation(); go.drag.node = that; go.drag.offset.x = pos.x - that.x; go.drag.offset.y = pos.y - that.y; + go.drag.start.x = m.x - am.x; + go.drag.start.y = m.y - am.y; } }); }, diff --git a/src/Shape.js b/src/Shape.js index 7504a163..0b550cdd 100644 --- a/src/Shape.js +++ b/src/Shape.js @@ -120,12 +120,13 @@ Kinetic.Shape.prototype = { family.unshift(parent); parent = parent.parent; } - + context.save(); for(var n = 0; n < family.length; n++) { var node = family[n]; - var m = node.getMatrix(); - m.transformContext(context); + var m = node.getMatrix().toArray(); + context.transform(m[0], m[1], m[2], m[3], m[4], m[5]); + if(node.getAbsoluteAlpha() !== 1) { context.globalAlpha = node.getAbsoluteAlpha(); } diff --git a/src/Stage.js b/src/Stage.js index 055d2f8e..1b8ae494 100644 --- a/src/Stage.js +++ b/src/Stage.js @@ -16,7 +16,7 @@ Kinetic.Stage = function(config) { * if container is a string, assume it's an id for * a DOM element */ - if(typeof config.container === 'string') { + if( typeof config.container === 'string') { config.container = document.getElementById(config.container); } @@ -587,18 +587,21 @@ Kinetic.Stage.prototype = { var node = go.drag.node; if(node) { var pos = that.getUserPosition(); - var ds = node.dragConstraint; + var dc = node.dragConstraint; var db = node.dragBounds; - if(ds === 'none' || ds === 'horizontal') { + var m = node.getMatrix().getTranslation(); + var am = node.getAbsoluteMatrix().getTranslation(); + + if(dc === 'none' || dc === 'horizontal') { var newX = pos.x - go.drag.offset.x; if((db.left === undefined || db.left < newX) && (db.right === undefined || db.right > newX)) { - node.x = newX; + node.x = newX + m.x - (am.x + go.drag.start.x); } } - if(ds === 'none' || ds === 'vertical') { + if(dc === 'none' || dc === 'vertical') { var newY = pos.y - go.drag.offset.y; if((db.top === undefined || db.top < newY) && (db.bottom === undefined || db.bottom > newY)) { - node.y = newY; + node.y = newY + m.y - (am.y + go.drag.start.y); } } go.drag.node.getLayer().draw(); diff --git a/tests/js/functionalTests.js b/tests/js/functionalTests.js index 5573581e..a2623643 100644 --- a/tests/js/functionalTests.js +++ b/tests/js/functionalTests.js @@ -979,27 +979,28 @@ Test.prototype.tests = { circle.draggable(false); }, - 'DRAG AND DROP - scale stage after add layer then drag and drop shape': function(containerId) { + 'DRAG AND DROP - scale and rotate stage after add layer then drag and drop shape': function(containerId) { var stage = new Kinetic.Stage({ container: containerId, width: 578, height: 200 }); var layer = new Kinetic.Layer(); - var circle = new Kinetic.Circle({ - x: stage.width / 2, - y: stage.height / 2, - radius: 70, + var rect = new Kinetic.Rect({ + x: 200, + y: 80, + width: 100, + height: 50, fill: 'red', stroke: 'black', - strokeWidth: 4 + strokeWidth: 4, + draggable: true }); - circle.draggable(true); - - layer.add(circle); + layer.add(rect); stage.add(layer); + stage.rotateDeg(20); stage.setScale(0.5); stage.draw();