Permalink
Browse files

finished matrix integration. the stage, layers, groups, and nodes can…

… be transformed in anyway, and drag and drop on any of these nodes now works great regardless of parent node transforms
  • Loading branch information...
1 parent 7ced50f commit 68f9688575ce9bada598ca5816daf7f5ab634b7e @ericdrowell committed Mar 24, 2012
Showing with 197 additions and 46 deletions.
  1. +94 −19 dist/kinetic-core.js
  2. +4 −0 src/GlobalObject.js
  3. +8 −3 src/Layer.js
  4. +35 −6 src/Matrix.js
  5. +33 −0 src/Node.js
  6. +4 −3 src/Shape.js
  7. +9 −6 src/Stage.js
  8. +10 −9 tests/js/functionalTests.js
View
113 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)
+ */
+ 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
*/
- transformContext: function(context) {
- var m = this.m;
- context.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
+ toArray: function() {
+ return this.m;
}
};
View
4 src/GlobalObject.js
@@ -24,6 +24,10 @@ Kinetic.GlobalObject = {
offset: {
x: 0,
y: 0
+ },
+ start: {
+ x: 0,
+ y: 0
}
},
extend: function(obj1, obj2) {
View
11 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) {
View
41 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;
}
};
View
33 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;
}
});
},
View
7 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();
}
View
15 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();
View
19 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();

0 comments on commit 68f9688

Please sign in to comment.