mirror of
https://github.com/konvajs/konva.git
synced 2025-10-15 12:34:52 +08:00
Merge branch 'matrix'
This commit is contained in:
@@ -24,6 +24,10 @@ Kinetic.GlobalObject = {
|
||||
offset: {
|
||||
x: 0,
|
||||
y: 0
|
||||
},
|
||||
start: {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
},
|
||||
extend: function(obj1, obj2) {
|
||||
|
11
src/Layer.js
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) {
|
||||
|
98
src/Matrix.js
Normal file
98
src/Matrix.js
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Last updated November 2011
|
||||
* By Simon Sarris
|
||||
* www.simonsarris.com
|
||||
* sarris@acm.org
|
||||
*
|
||||
* Free to use and distribute at will
|
||||
* So long as you are nice to people, etc
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Matrix object
|
||||
*/
|
||||
Kinetic.Matrix = function() {
|
||||
this.m = [1, 0, 0, 1, 0, 0];
|
||||
}
|
||||
|
||||
Kinetic.Matrix.prototype = {
|
||||
/**
|
||||
* Apply translation
|
||||
* @param {Number} x
|
||||
* @param {Number} y
|
||||
*/
|
||||
translate: function(x, y) {
|
||||
this.m[4] += this.m[0] * x + this.m[2] * y;
|
||||
this.m[5] += this.m[1] * x + this.m[3] * y;
|
||||
},
|
||||
/**
|
||||
* Apply scale
|
||||
* @param {Number} sx
|
||||
* @param {Number} sy
|
||||
*/
|
||||
scale: function(sx, sy) {
|
||||
this.m[0] *= sx;
|
||||
this.m[1] *= sx;
|
||||
this.m[2] *= sy;
|
||||
this.m[3] *= sy;
|
||||
},
|
||||
/**
|
||||
* Apply rotation
|
||||
* @param {Number} rad Angle in radians
|
||||
*/
|
||||
rotate: function(rad) {
|
||||
var c = Math.cos(rad);
|
||||
var s = Math.sin(rad);
|
||||
var m11 = this.m[0] * c + this.m[2] * s;
|
||||
var m12 = this.m[1] * c + this.m[3] * s;
|
||||
var m21 = this.m[0] * -s + this.m[2] * c;
|
||||
var m22 = this.m[1] * -s + this.m[3] * c;
|
||||
this.m[0] = m11;
|
||||
this.m[1] = m12;
|
||||
this.m[2] = m21;
|
||||
this.m[3] = m22;
|
||||
},
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
toArray: function() {
|
||||
return this.m;
|
||||
}
|
||||
};
|
59
src/Node.js
59
src/Node.js
@@ -49,14 +49,6 @@ Kinetic.Node = function(config) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// overrides
|
||||
if(this.centerOffset.x === undefined) {
|
||||
this.centerOffset.x = 0;
|
||||
}
|
||||
if(this.centerOffset.y === undefined) {
|
||||
this.centerOffset.y = 0;
|
||||
}
|
||||
};
|
||||
/*
|
||||
* Node methods
|
||||
@@ -515,6 +507,53 @@ 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();
|
||||
|
||||
if(this.x !== 0 || this.y !== 0) {
|
||||
m.translate(this.x, this.y);
|
||||
}
|
||||
if(this.rotation !== 0) {
|
||||
m.rotate(this.rotation);
|
||||
}
|
||||
if(this.scale.x !== 1 || this.scale.y !== 1) {
|
||||
m.scale(this.scale.x, this.scale.y);
|
||||
}
|
||||
if(this.centerOffset.x !== 0 || this.centerOffset.y !== 0) {
|
||||
m.translate(-1 * this.centerOffset.x, -1 * this.centerOffset.y);
|
||||
}
|
||||
|
||||
return m;
|
||||
},
|
||||
/**
|
||||
* initialize drag and drop
|
||||
*/
|
||||
@@ -526,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;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
49
src/Shape.js
49
src/Shape.js
@@ -112,56 +112,27 @@ Kinetic.Shape.prototype = {
|
||||
if(this.visible) {
|
||||
var stage = layer.getStage();
|
||||
var context = layer.getContext();
|
||||
|
||||
var family = [];
|
||||
var parent = this.parent;
|
||||
|
||||
family.unshift(this);
|
||||
var parent = this.parent;
|
||||
while(parent.className !== 'Stage') {
|
||||
while(parent) {
|
||||
family.unshift(parent);
|
||||
parent = parent.parent;
|
||||
}
|
||||
|
||||
// children transforms
|
||||
for(var n = 0; n < family.length; n++) {
|
||||
var obj = family[n];
|
||||
|
||||
context.save();
|
||||
if(obj.x !== 0 || obj.y !== 0) {
|
||||
context.translate(obj.x, obj.y);
|
||||
}
|
||||
if(obj.centerOffset.x !== 0 || obj.centerOffset.y !== 0) {
|
||||
context.translate(obj.centerOffset.x, obj.centerOffset.y);
|
||||
}
|
||||
if(obj.rotation !== 0) {
|
||||
context.rotate(obj.rotation);
|
||||
}
|
||||
if(obj.scale.x !== 1 || obj.scale.y !== 1) {
|
||||
context.scale(obj.scale.x, obj.scale.y);
|
||||
}
|
||||
if(obj.centerOffset.x !== 0 || obj.centerOffset.y !== 0) {
|
||||
context.translate(-1 * obj.centerOffset.x, -1 * obj.centerOffset.y);
|
||||
}
|
||||
if(obj.getAbsoluteAlpha() !== 1) {
|
||||
context.globalAlpha = obj.getAbsoluteAlpha();
|
||||
}
|
||||
}
|
||||
|
||||
// stage transform
|
||||
context.save();
|
||||
if(stage && (stage.scale.x !== 1 || stage.scale.y !== 1)) {
|
||||
context.scale(stage.scale.x, stage.scale.y);
|
||||
}
|
||||
for(var n = 0; n < family.length; n++) {
|
||||
var node = family[n];
|
||||
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();
|
||||
}
|
||||
}
|
||||
this.tempLayer = layer;
|
||||
this.drawFunc.call(this);
|
||||
|
||||
// children restore
|
||||
for(var i = 0; i < family.length; i++) {
|
||||
context.restore();
|
||||
}
|
||||
|
||||
// stage restore
|
||||
context.restore();
|
||||
}
|
||||
}
|
||||
|
104
src/Stage.js
104
src/Stage.js
@@ -6,17 +6,26 @@
|
||||
* animations
|
||||
* @constructor
|
||||
* @augments Kinetic.Container
|
||||
* @augments Kinetic.Node
|
||||
* @param {String|DomElement} cont Container id or DOM element
|
||||
* @param {int} width
|
||||
* @param {int} height
|
||||
*/
|
||||
Kinetic.Stage = function(cont, width, height) {
|
||||
Kinetic.Stage = function(config) {
|
||||
/*
|
||||
* if container is a string, assume it's an id for
|
||||
* a DOM element
|
||||
*/
|
||||
if( typeof config.container === 'string') {
|
||||
config.container = document.getElementById(config.container);
|
||||
}
|
||||
|
||||
this.className = 'Stage';
|
||||
this.container = typeof cont === 'string' ? document.getElementById(cont) : cont;
|
||||
this.container = config.container;
|
||||
this.content = document.createElement('div');
|
||||
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.width = config.width;
|
||||
this.height = config.height;
|
||||
this.scale = {
|
||||
x: 1,
|
||||
y: 1
|
||||
@@ -52,8 +61,9 @@ Kinetic.Stage = function(cont, width, height) {
|
||||
// add stage to global object
|
||||
Kinetic.GlobalObject.stages.push(this);
|
||||
|
||||
// call super constructor
|
||||
// call super constructors
|
||||
Kinetic.Container.apply(this, []);
|
||||
Kinetic.Node.apply(this, [config]);
|
||||
};
|
||||
/*
|
||||
* Stage methods
|
||||
@@ -110,48 +120,6 @@ Kinetic.Stage.prototype = {
|
||||
this.backstageLayer.getCanvas().width = width;
|
||||
this.backstageLayer.getCanvas().height = height;
|
||||
},
|
||||
/**
|
||||
* set stage scale. If only one parameter is passed in, then
|
||||
* both scaleX and scaleY are set to the parameter
|
||||
* @param {int} scaleX
|
||||
* @param {int} scaleY
|
||||
*/
|
||||
setScale: function(scaleX, scaleY) {
|
||||
var oldScaleX = this.scale.x;
|
||||
var oldScaleY = this.scale.y;
|
||||
|
||||
if(scaleY) {
|
||||
this.scale.x = scaleX;
|
||||
this.scale.y = scaleY;
|
||||
}
|
||||
else {
|
||||
this.scale.x = scaleX;
|
||||
this.scale.y = scaleX;
|
||||
}
|
||||
|
||||
/*
|
||||
* scale all shape positions
|
||||
*/
|
||||
var layers = this.children;
|
||||
var that = this;
|
||||
function scaleChildren(children) {
|
||||
for(var i = 0; i < children.length; i++) {
|
||||
var child = children[i];
|
||||
child.x *= that.scale.x / oldScaleX;
|
||||
child.y *= that.scale.y / oldScaleY;
|
||||
if(child.children) {
|
||||
scaleChildren(child.children);
|
||||
}
|
||||
}
|
||||
}
|
||||
scaleChildren(layers);
|
||||
},
|
||||
/**
|
||||
* get scale
|
||||
*/
|
||||
getScale: function() {
|
||||
return this.scale;
|
||||
},
|
||||
/**
|
||||
* clear all layers
|
||||
*/
|
||||
@@ -200,16 +168,14 @@ Kinetic.Stage.prototype = {
|
||||
remove: function(layer) {
|
||||
// remove layer canvas from dom
|
||||
this.content.removeChild(layer.canvas);
|
||||
|
||||
this._remove(layer);
|
||||
},
|
||||
/**
|
||||
* bind event listener to stage (which is essentially
|
||||
* the container DOM)
|
||||
* bind event listener to container DOM element
|
||||
* @param {String} typesStr
|
||||
* @param {function} handler
|
||||
*/
|
||||
on: function(typesStr, handler) {
|
||||
onContainer: function(typesStr, handler) {
|
||||
var types = typesStr.split(' ');
|
||||
for(var n = 0; n < types.length; n++) {
|
||||
var baseEvent = types[n];
|
||||
@@ -443,7 +409,7 @@ Kinetic.Stage.prototype = {
|
||||
* handle incoming event
|
||||
* @param {Event} evt
|
||||
*/
|
||||
_handleEvent: function(evt) {
|
||||
_handleStageEvent: function(evt) {
|
||||
var go = Kinetic.GlobalObject;
|
||||
if(!evt) {
|
||||
evt = window.event;
|
||||
@@ -492,25 +458,25 @@ Kinetic.Stage.prototype = {
|
||||
// desktop events
|
||||
this.container.addEventListener('mousedown', function(evt) {
|
||||
that.mouseDown = true;
|
||||
that._handleEvent(evt);
|
||||
that._handleStageEvent(evt);
|
||||
}, false);
|
||||
|
||||
this.container.addEventListener('mousemove', function(evt) {
|
||||
that.mouseUp = false;
|
||||
that.mouseDown = false;
|
||||
that._handleEvent(evt);
|
||||
that._handleStageEvent(evt);
|
||||
}, false);
|
||||
|
||||
this.container.addEventListener('mouseup', function(evt) {
|
||||
that.mouseUp = true;
|
||||
that.mouseDown = false;
|
||||
that._handleEvent(evt);
|
||||
that._handleStageEvent(evt);
|
||||
|
||||
that.clickStart = false;
|
||||
}, false);
|
||||
|
||||
this.container.addEventListener('mouseover', function(evt) {
|
||||
that._handleEvent(evt);
|
||||
that._handleStageEvent(evt);
|
||||
}, false);
|
||||
|
||||
this.container.addEventListener('mouseout', function(evt) {
|
||||
@@ -520,18 +486,18 @@ Kinetic.Stage.prototype = {
|
||||
this.container.addEventListener('touchstart', function(evt) {
|
||||
evt.preventDefault();
|
||||
that.touchStart = true;
|
||||
that._handleEvent(evt);
|
||||
that._handleStageEvent(evt);
|
||||
}, false);
|
||||
|
||||
this.container.addEventListener('touchmove', function(evt) {
|
||||
evt.preventDefault();
|
||||
that._handleEvent(evt);
|
||||
that._handleStageEvent(evt);
|
||||
}, false);
|
||||
|
||||
this.container.addEventListener('touchend', function(evt) {
|
||||
evt.preventDefault();
|
||||
that.touchEnd = true;
|
||||
that._handleEvent(evt);
|
||||
that._handleStageEvent(evt);
|
||||
}, false);
|
||||
},
|
||||
/**
|
||||
@@ -622,23 +588,26 @@ Kinetic.Stage.prototype = {
|
||||
_prepareDrag: function() {
|
||||
var that = this;
|
||||
|
||||
this.on('mousemove touchmove', function(evt) {
|
||||
this.onContainer('mousemove touchmove', function(evt) {
|
||||
var go = Kinetic.GlobalObject;
|
||||
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();
|
||||
@@ -653,7 +622,7 @@ Kinetic.Stage.prototype = {
|
||||
}
|
||||
}, false);
|
||||
|
||||
this.on('mouseup touchend mouseout', function(evt) {
|
||||
this.onContainer('mouseup touchend mouseout', function(evt) {
|
||||
that._endDrag(evt);
|
||||
});
|
||||
},
|
||||
@@ -693,5 +662,6 @@ Kinetic.Stage.prototype = {
|
||||
this.content.appendChild(this.backstageLayer.canvas);
|
||||
}
|
||||
};
|
||||
// extend Container
|
||||
// Extend Container and Node
|
||||
Kinetic.GlobalObject.extend(Kinetic.Stage, Kinetic.Container);
|
||||
Kinetic.GlobalObject.extend(Kinetic.Stage, Kinetic.Node);
|
||||
|
Reference in New Issue
Block a user