better animation and sprite workflow. fix #1002, #974, #700

This commit is contained in:
Лаврёнов Антон 2014-08-23 18:11:41 +08:00
parent 3dbd5d08ad
commit 1b925ed5f0
5 changed files with 330 additions and 205 deletions

View File

@ -4,7 +4,7 @@
* http://www.kineticjs.com/ * http://www.kineticjs.com/
* Copyright 2013, Eric Rowell * Copyright 2013, Eric Rowell
* Licensed under the MIT or GPL Version 2 licenses. * Licensed under the MIT or GPL Version 2 licenses.
* Date: 2014-06-10 * Date: 2014-08-23
* *
* Copyright (C) 2011 - 2013 by Eric Rowell * Copyright (C) 2011 - 2013 by Eric Rowell
* *
@ -283,7 +283,7 @@ var Kinetic = {};
* @memberof Kinetic * @memberof Kinetic
* @augments Kinetic.Container * @augments Kinetic.Container
* @param {Object} config * @param {Object} config
* @param {String|DomElement} config.container Container id or DOM element * @param {String|Element} config.container Container id or DOM element
* @param {Number} [config.x] * @param {Number} [config.x]
* @param {Number} [config.y] * @param {Number} [config.y]
* @param {Number} [config.width] * @param {Number} [config.width]
@ -475,12 +475,10 @@ var Kinetic = {};
// if DD is not included with the build, then // if DD is not included with the build, then
// drag and drop is not even possible // drag and drop is not even possible
if (!dd) { if (dd) {
return false;
}
// if DD is included with the build
else {
return dd.isDragging; return dd.isDragging;
} else {
return false;
} }
}, },
/** /**
@ -494,12 +492,10 @@ var Kinetic = {};
// if DD is not included with the build, then // if DD is not included with the build, then
// drag and drop is not even possible // drag and drop is not even possible
if (!dd) { if (dd) {
return false;
}
// if DD is included with the build
else {
return !!dd.node; return !!dd.node;
} else {
return false;
} }
}, },
_addId: function(node, id) { _addId: function(node, id) {
@ -600,9 +596,8 @@ var Kinetic = {};
// like Node. // like Node.
var Canvas = require('canvas'); var Canvas = require('canvas');
var jsdom = require('jsdom').jsdom; var jsdom = require('jsdom').jsdom;
var doc = jsdom('<!DOCTYPE html><html><head></head><body></body></html>');
Kinetic.document = doc; Kinetic.document = jsdom('<!DOCTYPE html><html><head></head><body></body></html>');
Kinetic.window = Kinetic.document.createWindow(); Kinetic.window = Kinetic.document.createWindow();
Kinetic.window.Image = Canvas.Image; Kinetic.window.Image = Canvas.Image;
Kinetic._nodeCanvas = Canvas; Kinetic._nodeCanvas = Canvas;
@ -734,7 +729,7 @@ var Kinetic = {};
/** /**
* Transform constructor * Transform constructor
* @constructor * @constructor
* @param {Array} Optional six-element matrix * @param {Array} [m] Optional six-element matrix
* @memberof Kinetic * @memberof Kinetic
*/ */
Kinetic.Transform = function(m) { Kinetic.Transform = function(m) {
@ -755,14 +750,14 @@ var Kinetic = {};
* Transform point * Transform point
* @method * @method
* @memberof Kinetic.Transform.prototype * @memberof Kinetic.Transform.prototype
* @param {Object} 2D point(x, y) * @param {Object} point 2D point(x, y)
* @returns {Object} 2D point(x, y) * @returns {Object} 2D point(x, y)
*/ */
point: function(p) { point: function(point) {
var m = this.m; var m = this.m;
return { return {
x: m[0] * p.x + m[2] * p.y + m[4], x: m[0] * point.x + m[2] * point.y + m[4],
y: m[1] * p.x + m[3] * p.y + m[5] y: m[1] * point.x + m[3] * point.y + m[5]
}; };
}, },
/** /**
@ -921,8 +916,7 @@ var Kinetic = {};
}; };
// CONSTANTS // CONSTANTS
var CANVAS = 'canvas', var CONTEXT_2D = '2d',
CONTEXT_2D = '2d',
OBJECT_ARRAY = '[object Array]', OBJECT_ARRAY = '[object Array]',
OBJECT_NUMBER = '[object Number]', OBJECT_NUMBER = '[object Number]',
OBJECT_STRING = '[object String]', OBJECT_STRING = '[object String]',
@ -990,11 +984,11 @@ var Kinetic = {};
// as much as it can, without ever going more than once per `wait` duration; // as much as it can, without ever going more than once per `wait` duration;
// but if you'd like to disable the execution on the leading edge, pass // but if you'd like to disable the execution on the leading edge, pass
// `{leading: false}`. To disable execution on the trailing edge, ditto. // `{leading: false}`. To disable execution on the trailing edge, ditto.
_throttle: function(func, wait, options) { _throttle: function(func, wait, opts) {
var context, args, result; var context, args, result;
var timeout = null; var timeout = null;
var previous = 0; var previous = 0;
options || (options = {}); var options = opts || {};
var later = function() { var later = function() {
previous = options.leading === false ? 0 : new Date().getTime(); previous = options.leading === false ? 0 : new Date().getTime();
timeout = null; timeout = null;
@ -1003,7 +997,9 @@ var Kinetic = {};
}; };
return function() { return function() {
var now = new Date().getTime(); var now = new Date().getTime();
if (!previous && options.leading === false) previous = now; if (!previous && options.leading === false) {
previous = now;
}
var remaining = wait - (now - previous); var remaining = wait - (now - previous);
context = this; context = this;
args = arguments; args = arguments;
@ -1035,7 +1031,11 @@ var Kinetic = {};
}, },
createCanvasElement: function() { createCanvasElement: function() {
var canvas = Kinetic.document.createElement('canvas'); var canvas = Kinetic.document.createElement('canvas');
// on some environments canvas.style is readonly
try {
canvas.style = canvas.style || {}; canvas.style = canvas.style || {};
} catch (e) {
}
return canvas; return canvas;
}, },
isBrowser: function() { isBrowser: function() {
@ -1337,14 +1337,15 @@ var Kinetic = {};
* @constructor * @constructor
* @abstract * @abstract
* @memberof Kinetic * @memberof Kinetic
* @param {Number} width * @param {Object} config
* @param {Number} height * @param {Number} config.width
* @param {Number} pixelRatio KineticJS automatically handles pixel ratio adustments in order to render crisp drawings * @param {Number} config.height
* @param {Number} config.pixelRatio KineticJS automatically handles pixel ratio adjustments in order to render crisp drawings
* on all devices. Most desktops, low end tablets, and low end phones, have device pixel ratios * on all devices. Most desktops, low end tablets, and low end phones, have device pixel ratios
* of 1. Some high end tablets and phones, like iPhones and iPads (not the mini) have a device pixel ratio * of 1. Some high end tablets and phones, like iPhones and iPads (not the mini) have a device pixel ratio
* of 2. Some Macbook Pros, and iMacs also have a device pixel ratio of 2. Some high end Android devices have pixel * of 2. Some Macbook Pros, and iMacs also have a device pixel ratio of 2. Some high end Android devices have pixel
* ratios of 2 or 3. Some browsers like Firefox allow you to configure the pixel ratio of the viewport. Unless otherwise * ratios of 2 or 3. Some browsers like Firefox allow you to configure the pixel ratio of the viewport. Unless otherwise
* specificed, the pixel ratio will be defaulted to the actual device pixel ratio. You can override the device pixel * specified, the pixel ratio will be defaulted to the actual device pixel ratio. You can override the device pixel
* ratio for special situations, or, if you don't want the pixel ratio to be taken into account, you can set it to 1. * ratio for special situations, or, if you don't want the pixel ratio to be taken into account, you can set it to 1.
*/ */
Kinetic.Canvas = function(config) { Kinetic.Canvas = function(config) {
@ -1353,9 +1354,9 @@ var Kinetic = {};
Kinetic.Canvas.prototype = { Kinetic.Canvas.prototype = {
init: function(config) { init: function(config) {
config = config || {}; var conf = config || {};
var pixelRatio = config.pixelRatio || Kinetic.pixelRatio || _pixelRatio; var pixelRatio = conf.pixelRatio || Kinetic.pixelRatio || _pixelRatio;
this.pixelRatio = pixelRatio; this.pixelRatio = pixelRatio;
this._canvas = Kinetic.Util.createCanvasElement(); this._canvas = Kinetic.Util.createCanvasElement();
@ -1481,11 +1482,11 @@ var Kinetic = {};
}; };
Kinetic.SceneCanvas = function(config) { Kinetic.SceneCanvas = function(config) {
config = config || {}; var conf = config || {};
var width = config.width || 0, var width = conf.width || 0,
height = config.height || 0; height = conf.height || 0;
Kinetic.Canvas.call(this, config); Kinetic.Canvas.call(this, conf);
this.context = new Kinetic.SceneContext(this); this.context = new Kinetic.SceneContext(this);
this.setSize(width, height); this.setSize(width, height);
}; };
@ -1509,11 +1510,11 @@ var Kinetic = {};
Kinetic.Util.extend(Kinetic.SceneCanvas, Kinetic.Canvas); Kinetic.Util.extend(Kinetic.SceneCanvas, Kinetic.Canvas);
Kinetic.HitCanvas = function(config) { Kinetic.HitCanvas = function(config) {
config = config || {}; var conf = config || {};
var width = config.width || 0, var width = conf.width || 0,
height = config.height || 0; height = conf.height || 0;
Kinetic.Canvas.call(this, config); Kinetic.Canvas.call(this, conf);
this.context = new Kinetic.HitContext(this); this.context = new Kinetic.HitContext(this);
this.setSize(width, height); this.setSize(width, height);
}; };
@ -2114,46 +2115,9 @@ var Kinetic = {};
;/*jshint unused:false */ ;/*jshint unused:false */
(function() { (function() {
// CONSTANTS // CONSTANTS
var ABSOLUTE_OPACITY = 'absoluteOpacity', var GET = 'get',
ABSOLUTE_TRANSFORM = 'absoluteTransform',
ADD = 'add',
B = 'b',
BEFORE = 'before',
BLACK = 'black',
CHANGE = 'Change',
CHILDREN = 'children',
DEG = 'Deg',
DOT = '.',
EMPTY_STRING = '',
G = 'g',
GET = 'get',
HASH = '#',
ID = 'id',
KINETIC = 'kinetic',
LISTENING = 'listening',
MOUSEENTER = 'mouseenter',
MOUSELEAVE = 'mouseleave',
NAME = 'name',
OFF = 'off',
ON = 'on',
PRIVATE_GET = '_get',
R = 'r',
RGB = 'RGB', RGB = 'RGB',
SET = 'set', SET = 'set';
SHAPE = 'Shape',
SPACE = ' ',
STAGE = 'Stage',
TRANSFORM = 'transform',
UPPER_B = 'B',
UPPER_G = 'G',
UPPER_HEIGHT = 'Height',
UPPER_R = 'R',
UPPER_WIDTH = 'Width',
UPPER_X = 'X',
UPPER_Y = 'Y',
VISIBLE = 'visible',
X = 'x',
Y = 'y';
Kinetic.Factory = { Kinetic.Factory = {
addGetterSetter: function(constructor, attr, def, validator, after) { addGetterSetter: function(constructor, attr, def, validator, after) {
@ -2259,14 +2223,15 @@ var Kinetic = {};
}; };
Kinetic.Validators = { Kinetic.Validators = {
/**
* @return {number}
*/
RGBComponent: function(val) { RGBComponent: function(val) {
if (val > 255) { if (val > 255) {
return 255; return 255;
} } else if (val < 0) {
else if (val < 0) {
return 0; return 0;
} } else {
else {
return Math.round(val); return Math.round(val);
} }
}, },
@ -2287,7 +2252,6 @@ var Kinetic = {};
// CONSTANTS // CONSTANTS
var ABSOLUTE_OPACITY = 'absoluteOpacity', var ABSOLUTE_OPACITY = 'absoluteOpacity',
ABSOLUTE_TRANSFORM = 'absoluteTransform', ABSOLUTE_TRANSFORM = 'absoluteTransform',
BEFORE = 'before',
CHANGE = 'Change', CHANGE = 'Change',
CHILDREN = 'children', CHILDREN = 'children',
DOT = '.', DOT = '.',
@ -2434,8 +2398,8 @@ var Kinetic = {};
y = conf.y || 0, y = conf.y || 0,
width = conf.width || this.width(), width = conf.width || this.width(),
height = conf.height || this.height(), height = conf.height || this.height(),
drawBorder = conf.drawBorder || false, drawBorder = conf.drawBorder || false;
layer = this.getLayer();
if (width === 0 || height === 0) { if (width === 0 || height === 0) {
Kinetic.Util.warn('Width or height of caching configuration equals 0. Cache is ignored.'); Kinetic.Util.warn('Width or height of caching configuration equals 0. Cache is ignored.');
return; return;
@ -2454,9 +2418,6 @@ var Kinetic = {};
width: width, width: width,
height: height height: height
}), }),
origTransEnabled = this.transformsEnabled(),
origX = this.x(),
origY = this.y(),
sceneContext = cachedSceneCanvas.getContext(), sceneContext = cachedSceneCanvas.getContext(),
hitContext = cachedHitCanvas.getContext(); hitContext = cachedHitCanvas.getContext();
@ -3316,8 +3277,11 @@ var Kinetic = {};
* node.moveTo(layer2); * node.moveTo(layer2);
*/ */
moveTo: function(newContainer) { moveTo: function(newContainer) {
Kinetic.Node.prototype.remove.call(this); // do nothing if new container is already parent
if (this.getParent() !== newContainer) {
this.remove();
newContainer.add(this); newContainer.add(this);
}
return this; return this;
}, },
/** /**
@ -3404,7 +3368,7 @@ var Kinetic = {};
* @method * @method
* @memberof Kinetic.Node.prototype * @memberof Kinetic.Node.prototype
* @param {String} eventType event type. can be a regular event, like click, mouseover, or mouseout, or it can be a custom event, like myCustomEvent * @param {String} eventType event type. can be a regular event, like click, mouseover, or mouseout, or it can be a custom event, like myCustomEvent
* @param {EventObject} [evt] event object * @param {Event} [evt] event object
* @param {Boolean} [bubble] setting the value to false, or leaving it undefined, will result in the event * @param {Boolean} [bubble] setting the value to false, or leaving it undefined, will result in the event
* not bubbling. Setting the value to true will result in the event bubbling. * not bubbling. Setting the value to true will result in the event bubbling.
* @returns {Kinetic.Node} * @returns {Kinetic.Node}
@ -3514,7 +3478,7 @@ var Kinetic = {};
* for another node * for another node
* @method * @method
* @memberof Kinetic.Node.prototype * @memberof Kinetic.Node.prototype
* @param {Object} attrs override attrs * @param {Object} obj override attrs
* @returns {Kinetic.Node} * @returns {Kinetic.Node}
* @example * @example
* // simple clone * // simple clone
@ -3735,11 +3699,8 @@ var Kinetic = {};
* @example * @example
* node.setAttr('x', 5); * node.setAttr('x', 5);
*/ */
setAttr: function() { setAttr: function(attr, val) {
var args = Array.prototype.slice.call(arguments), var method = SET + Kinetic.Util._capitalize(attr),
attr = args[0],
val = args[1],
method = SET + Kinetic.Util._capitalize(attr),
func = this[method]; func = this[method];
if(Kinetic.Util._isFunction(func)) { if(Kinetic.Util._isFunction(func)) {
@ -3835,8 +3796,8 @@ var Kinetic = {};
* and setImage() methods * and setImage() methods
* @method * @method
* @memberof Kinetic.Node * @memberof Kinetic.Node
* @param {String} JSON string * @param {String} json
* @param {DomElement} [container] optional container dom element used only if you're * @param {Element} [container] optional container dom element used only if you're
* creating a stage node * creating a stage node
*/ */
Kinetic.Node.create = function(json, container) { Kinetic.Node.create = function(json, container) {
@ -3874,7 +3835,7 @@ var Kinetic = {};
* @memberof Kinetic.Node.prototype * @memberof Kinetic.Node.prototype
* @param {Object} pos * @param {Object} pos
* @param {Number} pos.x * @param {Number} pos.x
* @param {Nubmer} pos.y * @param {Number} pos.y
* @returns {Object} * @returns {Object}
* @example * @example
* // get position * // get position
@ -5474,7 +5435,6 @@ var Kinetic = {};
rMin = data[0], rMax = rMin, r, rMin = data[0], rMax = rMin, r,
gMin = data[1], gMax = gMin, g, gMin = data[1], gMax = gMin, g,
bMin = data[2], bMax = bMin, b, bMin = data[2], bMax = bMin, b,
aMin = data[3], aMax = aMin,
i; i;
// If we are not enhancing anything - don't do any computation // If we are not enhancing anything - don't do any computation
@ -5501,12 +5461,10 @@ var Kinetic = {};
if( rMax === rMin ){ rMax = 255; rMin = 0; } if( rMax === rMin ){ rMax = 255; rMin = 0; }
if( gMax === gMin ){ gMax = 255; gMin = 0; } if( gMax === gMin ){ gMax = 255; gMin = 0; }
if( bMax === bMin ){ bMax = 255; bMin = 0; } if( bMax === bMin ){ bMax = 255; bMin = 0; }
if( aMax === aMin ){ aMax = 255; aMin = 0; }
var rMid, rGoalMax,rGoalMin, var rMid, rGoalMax,rGoalMin,
gMid, gGoalMax,gGoalMin, gMid, gGoalMax,gGoalMin,
bMid, bGoalMax,aGoalMin, bMid, bGoalMax,bGoalMin;
aMid, aGoalMax,bGoalMin;
// If the enhancement is positive - stretch the histogram // If the enhancement is positive - stretch the histogram
if ( enhanceAmount > 0 ){ if ( enhanceAmount > 0 ){
@ -5516,8 +5474,6 @@ var Kinetic = {};
gGoalMin = gMin - enhanceAmount*(gMin-0); gGoalMin = gMin - enhanceAmount*(gMin-0);
bGoalMax = bMax + enhanceAmount*(255-bMax); bGoalMax = bMax + enhanceAmount*(255-bMax);
bGoalMin = bMin - enhanceAmount*(bMin-0); bGoalMin = bMin - enhanceAmount*(bMin-0);
aGoalMax = aMax + enhanceAmount*(255-aMax);
aGoalMin = aMin - enhanceAmount*(aMin-0);
// If the enhancement is negative - compress the histogram // If the enhancement is negative - compress the histogram
} else { } else {
rMid = (rMax + rMin)*0.5; rMid = (rMax + rMin)*0.5;
@ -5529,9 +5485,6 @@ var Kinetic = {};
bMid = (bMax + bMin)*0.5; bMid = (bMax + bMin)*0.5;
bGoalMax = bMax + enhanceAmount*(bMax-bMid); bGoalMax = bMax + enhanceAmount*(bMax-bMid);
bGoalMin = bMin + enhanceAmount*(bMin-bMid); bGoalMin = bMin + enhanceAmount*(bMin-bMid);
aMid = (aMax + aMin)*0.5;
aGoalMax = aMax + enhanceAmount*(aMax-aMid);
aGoalMin = aMin + enhanceAmount*(aMin-aMid);
} }
// Pass 2 - remap everything, except the alpha // Pass 2 - remap everything, except the alpha
@ -5601,7 +5554,7 @@ var Kinetic = {};
* @function * @function
* @name Noise * @name Noise
* @memberof Kinetic.Filters * @memberof Kinetic.Filters
* @param {Object} imagedata * @param {Object} imageData
* @author ippo615 * @author ippo615
* @example * @example
* node.cache(); * node.cache();
@ -6322,9 +6275,9 @@ var Kinetic = {};
}; };
Kinetic.Animation._runFrames = function() { Kinetic.Animation._runFrames = function() {
var layerHash = {}, var layerHash,
animations = this.animations, animations = this.animations,
anim, layers, func, n, i, layersLen, layer, key; anim, layers, func, n, i, layersLen, layer, key, needRedraw;
/* /*
* loop through all animations and execute animation * loop through all animations and execute animation
* function. if the animation object has specified node, * function. if the animation object has specified node,
@ -6336,8 +6289,8 @@ var Kinetic = {};
* WARNING: don't cache animations.length because it could change while * WARNING: don't cache animations.length because it could change while
* the for loop is running, causing a JS error * the for loop is running, causing a JS error
*/ */
var needRedraw = false;
for(n = 0; n < animations.length; n++) { for(n = 0; n < animations.length; n++) {
layerHash = {};
anim = animations[n]; anim = animations[n];
layers = anim.layers; layers = anim.layers;
func = anim.func; func = anim.func;
@ -6345,22 +6298,25 @@ var Kinetic = {};
anim._updateFrameObject(now()); anim._updateFrameObject(now());
layersLen = layers.length; layersLen = layers.length;
for (i=0; i<layersLen; i++) {
layer = layers[i];
if(layer._id !== undefined) {
layerHash[layer._id] = layer;
}
}
// if animation object has a function, execute it // if animation object has a function, execute it
if(func) { if (func) {
// allow anim bypassing drawing // allow anim bypassing drawing
needRedraw = (func.call(anim, anim.frame) !== false) || needRedraw; needRedraw = (func.call(anim, anim.frame) !== false);
}
} }
if (needRedraw) { if (needRedraw) {
for(key in layerHash) { for (i = 0; i < layersLen; i++) {
layer = layers[i];
if (layer._id !== undefined) {
layerHash[layer._id] = layer;
}
}
}
}
if (needRedraw) {
for (key in layerHash) {
layerHash[key].draw(); layerHash[key].draw();
} }
} }
@ -6624,7 +6580,6 @@ var Kinetic = {};
* @returns {Tween} * @returns {Tween}
*/ */
reset: function() { reset: function() {
var node = this.node;
this.tween.reset(); this.tween.reset();
return this; return this;
}, },
@ -6636,7 +6591,6 @@ var Kinetic = {};
* @returns {Tween} * @returns {Tween}
*/ */
seek: function(t) { seek: function(t) {
var node = this.node;
this.tween.seek(t * 1000); this.tween.seek(t * 1000);
return this; return this;
}, },
@ -6657,7 +6611,6 @@ var Kinetic = {};
* @returns {Tween} * @returns {Tween}
*/ */
finish: function() { finish: function() {
var node = this.node;
this.tween.finish(); this.tween.finish();
return this; return this;
}, },
@ -7027,7 +6980,7 @@ var Kinetic = {};
;(function() { ;(function() {
Kinetic.DD = { Kinetic.DD = {
// properties // properties
anim: new Kinetic.Animation(function(frame) { anim: new Kinetic.Animation(function() {
var b = this.dirty; var b = this.dirty;
this.dirty = false; this.dirty = false;
return b; return b;
@ -7349,13 +7302,13 @@ var Kinetic = {};
* return node.getClassName() === 'Circle'; * return node.getClassName() === 'Circle';
* }); * });
*/ */
getChildren: function(predicate) { getChildren: function(filterFunc) {
if (!predicate) { if (!filterFunc) {
return this.children; return this.children;
} else { } else {
var results = new Kinetic.Collection(); var results = new Kinetic.Collection();
this.children.each(function(child){ this.children.each(function(child){
if (predicate(child)) { if (filterFunc(child)) {
results.push(child); results.push(child);
} }
}); });
@ -7426,11 +7379,11 @@ var Kinetic = {};
for (var i = 0; i < arguments.length; i++) { for (var i = 0; i < arguments.length; i++) {
this.add(arguments[i]); this.add(arguments[i]);
} }
return; return this;
} }
if (child.getParent()) { if (child.getParent()) {
child.moveTo(this); child.moveTo(this);
return; return this;
} }
var children = this.children; var children = this.children;
this._validateAdd(child); this._validateAdd(child);
@ -7893,14 +7846,14 @@ var Kinetic = {};
* @param {Number} point.y * @param {Number} point.y
* @returns {Boolean} * @returns {Boolean}
*/ */
intersects: function(pos) { intersects: function(point) {
var stage = this.getStage(), var stage = this.getStage(),
bufferHitCanvas = stage.bufferHitCanvas, bufferHitCanvas = stage.bufferHitCanvas,
p; p;
bufferHitCanvas.getContext().clear(); bufferHitCanvas.getContext().clear();
this.drawScene(bufferHitCanvas); this.drawScene(bufferHitCanvas);
p = bufferHitCanvas.context.getImageData(Math.round(pos.x), Math.round(pos.y), 1, 1).data; p = bufferHitCanvas.context.getImageData(Math.round(point.x), Math.round(point.y), 1, 1).data;
return p[3] > 0; return p[3] > 0;
}, },
// extends Node.prototype.destroy // extends Node.prototype.destroy
@ -7962,8 +7915,8 @@ var Kinetic = {};
if (layer) { if (layer) {
layer._applyTransform(this, context, top); layer._applyTransform(this, context, top);
} else { } else {
var m = this.getAbsoluteTransform(top).getMatrix(); var o = this.getAbsoluteTransform(top).getMatrix();
context.transform(m[0], m[1], m[2], m[3], m[4], m[5]); context.transform(o[0], o[1], o[2], o[3], o[4], o[5]);
} }
if (hasShadow) { if (hasShadow) {
@ -8066,7 +8019,7 @@ var Kinetic = {};
} }
return this; return this;
}, }
}); });
Kinetic.Util.extend(Kinetic.Shape, Kinetic.Node); Kinetic.Util.extend(Kinetic.Shape, Kinetic.Node);
@ -9209,9 +9162,7 @@ var Kinetic = {};
TOUCHMOVE = 'touchmove', TOUCHMOVE = 'touchmove',
CONTENT_MOUSEOUT = 'contentMouseout', CONTENT_MOUSEOUT = 'contentMouseout',
CONTENT_MOUSELEAVE = 'contentMouseleave',
CONTENT_MOUSEOVER = 'contentMouseover', CONTENT_MOUSEOVER = 'contentMouseover',
CONTENT_MOUSEENTER = 'contentMouseenter',
CONTENT_MOUSEMOVE = 'contentMousemove', CONTENT_MOUSEMOVE = 'contentMousemove',
CONTENT_MOUSEDOWN = 'contentMousedown', CONTENT_MOUSEDOWN = 'contentMousedown',
CONTENT_MOUSEUP = 'contentMouseup', CONTENT_MOUSEUP = 'contentMouseup',
@ -9219,7 +9170,6 @@ var Kinetic = {};
CONTENT_DBL_CLICK = 'contentDblclick', CONTENT_DBL_CLICK = 'contentDblclick',
CONTENT_TOUCHSTART = 'contentTouchstart', CONTENT_TOUCHSTART = 'contentTouchstart',
CONTENT_TOUCHEND = 'contentTouchend', CONTENT_TOUCHEND = 'contentTouchend',
CONTENT_TAP = 'contentTap',
CONTENT_DBL_TAP = 'contentDbltap', CONTENT_DBL_TAP = 'contentDbltap',
CONTENT_TOUCHMOVE = 'contentTouchmove', CONTENT_TOUCHMOVE = 'contentTouchmove',
@ -9861,7 +9811,7 @@ var Kinetic = {};
var container = this.getContainer(); var container = this.getContainer();
if (!container) { if (!container) {
if (Kinetic.Util.isBrowser()) { if (Kinetic.Util.isBrowser()) {
throw 'Stage has not container. But container is required'; throw 'Stage has no container. A container is required.';
} else { } else {
// automatically create element for jsdom in nodejs env // automatically create element for jsdom in nodejs env
container = Kinetic.document.createElement(DIV); container = Kinetic.document.createElement(DIV);
@ -9902,7 +9852,6 @@ var Kinetic = {};
// TODO: may be it is better to cache all children layers? // TODO: may be it is better to cache all children layers?
cache: function() { cache: function() {
Kinetic.Util.warn('Cache function is not allowed for stage. You may use cache only for layers, groups and shapes.'); Kinetic.Util.warn('Cache function is not allowed for stage. You may use cache only for layers, groups and shapes.');
return;
}, },
clearCache : function() { clearCache : function() {
} }
@ -10210,15 +10159,13 @@ var Kinetic = {};
} }
} }
// if no shape, and no antialiased pixel, we should end searching // if no shape, and no antialiased pixel, we should end searching
if (!continueSearch) { if (continueSearch) {
return;
} else {
spiralSearchDistance += 1; spiralSearchDistance += 1;
} else {
return;
} }
} }
} else {
}
else {
return null; return null;
} }
}, },
@ -10337,7 +10284,7 @@ var Kinetic = {};
* @name enableHitGraph * @name enableHitGraph
* @method * @method
* @memberof Kinetic.Layer.prototype * @memberof Kinetic.Layer.prototype
* @returns {Node} * @returns {Layer}
*/ */
enableHitGraph: function() { enableHitGraph: function() {
this.setHitGraphEnabled(true); this.setHitGraphEnabled(true);
@ -10348,7 +10295,7 @@ var Kinetic = {};
* @name disableHitGraph * @name disableHitGraph
* @method * @method
* @memberof Kinetic.Layer.prototype * @memberof Kinetic.Layer.prototype
* @returns {Node} * @returns {Layer}
*/ */
disableHitGraph: function() { disableHitGraph: function() {
this.setHitGraphEnabled(false); this.setHitGraphEnabled(false);
@ -10381,14 +10328,9 @@ var Kinetic = {};
* // enable hit graph * // enable hit graph
* layer.hitGraphEnabled(true); * layer.hitGraphEnabled(true);
*/ */
Kinetic.Collection.mapMethods(Kinetic.Layer); Kinetic.Collection.mapMethods(Kinetic.Layer);
})(); })();
;(function() { ;(function() {
// constants
var HASH = '#',
BEFORE_DRAW ='beforeDraw',
DRAW = 'draw';
Kinetic.Util.addMethods(Kinetic.FastLayer, { Kinetic.Util.addMethods(Kinetic.FastLayer, {
____init: function(config) { ____init: function(config) {
@ -10773,18 +10715,28 @@ var Kinetic = {};
// implements Shape.prototype.setWidth() // implements Shape.prototype.setWidth()
setWidth: function(width) { setWidth: function(width) {
Kinetic.Node.prototype.setWidth.call(this, width); Kinetic.Node.prototype.setWidth.call(this, width);
if (this.radius() !== width / 2) {
this.setRadius(width / 2); this.setRadius(width / 2);
}
}, },
// implements Shape.prototype.setHeight() // implements Shape.prototype.setHeight()
setHeight: function(height) { setHeight: function(height) {
Kinetic.Node.prototype.setHeight.call(this, height); Kinetic.Node.prototype.setHeight.call(this, height);
if (this.radius() !== height / 2) {
this.setRadius(height / 2); this.setRadius(height / 2);
} }
},
setRadius : function(val) {
this._setAttr('radius', val);
this.setWidth(val * 2);
this.setHeight(val * 2);
}
}; };
Kinetic.Util.extend(Kinetic.Circle, Kinetic.Shape); Kinetic.Util.extend(Kinetic.Circle, Kinetic.Shape);
// add getters setters // add getters setters
Kinetic.Factory.addGetterSetter(Kinetic.Circle, 'radius', 0); Kinetic.Factory.addGetter(Kinetic.Circle, 'radius', 0);
Kinetic.Factory.addOverloadedGetterSetter(Kinetic.Circle, 'radius');
/** /**
* get/set radius * get/set radius
@ -11063,13 +11015,22 @@ var Kinetic = {};
// implements Shape.prototype.setWidth() // implements Shape.prototype.setWidth()
setWidth: function(width) { setWidth: function(width) {
Kinetic.Node.prototype.setWidth.call(this, width); Kinetic.Node.prototype.setWidth.call(this, width);
if (this.outerRadius() !== width / 2) {
this.setOuterRadius(width / 2); this.setOuterRadius(width / 2);
}
}, },
// implements Shape.prototype.setHeight() // implements Shape.prototype.setHeight()
setHeight: function(height) { setHeight: function(height) {
Kinetic.Node.prototype.setHeight.call(this, height); Kinetic.Node.prototype.setHeight.call(this, height);
if (this.outerRadius() !== height / 2) {
this.setOuterRadius(height / 2); this.setOuterRadius(height / 2);
} }
},
setOuterRadius : function(val) {
this._setAttr('outerRadius', val);
this.setWidth(val * 2);
this.setHeight(val * 2);
}
}; };
Kinetic.Util.extend(Kinetic.Ring, Kinetic.Shape); Kinetic.Util.extend(Kinetic.Ring, Kinetic.Shape);
@ -11091,7 +11052,8 @@ var Kinetic = {};
* ring.innerRadius(20); * ring.innerRadius(20);
*/ */
Kinetic.Factory.addGetterSetter(Kinetic.Ring, 'outerRadius', 0); Kinetic.Factory.addGetter(Kinetic.Ring, 'outerRadius', 0);
Kinetic.Factory.addOverloadedGetterSetter(Kinetic.Ring, 'outerRadius');
/** /**
* get/set outerRadius * get/set outerRadius
@ -11295,8 +11257,6 @@ var Kinetic = {};
Kinetic.Collection.mapMethods(Kinetic.Wedge); Kinetic.Collection.mapMethods(Kinetic.Wedge);
})(); })();
;(function() { ;(function() {
var PI_OVER_180 = Math.PI / 180;
/** /**
* Arc constructor * Arc constructor
* @constructor * @constructor
@ -11507,7 +11467,7 @@ var Kinetic = {};
* @memberof Kinetic * @memberof Kinetic
* @augments Kinetic.Shape * @augments Kinetic.Shape
* @param {Object} config * @param {Object} config
* @param {ImageObject} config.image * @param {Image} config.image
* @param {Object} [config.crop] * @param {Object} [config.crop]
* @param {String} [config.fill] fill color * @param {String} [config.fill] fill color
* @param {Integer} [config.fillRed] set fill red component * @param {Integer} [config.fillRed] set fill red component
@ -11672,7 +11632,7 @@ var Kinetic = {};
* @name setImage * @name setImage
* @method * @method
* @memberof Kinetic.Image.prototype * @memberof Kinetic.Image.prototype
* @param {ImageObject} image * @param {Image} image
*/ */
/** /**
@ -11680,7 +11640,7 @@ var Kinetic = {};
* @name getImage * @name getImage
* @method * @method
* @memberof Kinetic.Image.prototype * @memberof Kinetic.Image.prototype
* @returns {ImageObject} * @returns {Image}
*/ */
Kinetic.Factory.addComponentsGetterSetter(Kinetic.Image, 'crop', ['x', 'y', 'width', 'height']); Kinetic.Factory.addComponentsGetterSetter(Kinetic.Image, 'crop', ['x', 'y', 'width', 'height']);
@ -12741,11 +12701,21 @@ var Kinetic = {};
Kinetic.Shape.call(this, config); Kinetic.Shape.call(this, config);
this.className = 'Sprite'; this.className = 'Sprite';
this.anim = new Kinetic.Animation(); this._updated = true;
var that = this;
this.anim = new Kinetic.Animation(function() {
// if we don't need to redraw layer we should return false
var updated = that._updated;
that._updated = false;
return updated;
});
this.on('animationChange.kinetic', function() { this.on('animationChange.kinetic', function() {
// reset index when animation changes // reset index when animation changes
this.frameIndex(0); this.frameIndex(0);
}); });
this.on('frameIndexChange.kinetic', function() {
this._updated = true;
});
// smooth change for frameRate // smooth change for frameRate
this.on('frameRateChange.kinetic', function() { this.on('frameRateChange.kinetic', function() {
if (!this.anim.isRunning()) { if (!this.anim.isRunning()) {
@ -14714,7 +14684,7 @@ var Kinetic = {};
pointerDirection = this.getPointerDirection(), pointerDirection = this.getPointerDirection(),
pointerWidth = this.getPointerWidth(), pointerWidth = this.getPointerWidth(),
pointerHeight = this.getPointerHeight(); pointerHeight = this.getPointerHeight();
//cornerRadius = this.getCornerRadius(); cornerRadius = this.getCornerRadius();
context.beginPath(); context.beginPath();
context.moveTo(0,0); context.moveTo(0,0);
@ -14725,7 +14695,12 @@ var Kinetic = {};
context.lineTo((width + pointerWidth)/2, 0); context.lineTo((width + pointerWidth)/2, 0);
} }
if(!cornerRadius) {
context.lineTo(width, 0); context.lineTo(width, 0);
} else {
context.lineTo(width - cornerRadius, 0);
context.arc(width - cornerRadius, cornerRadius, cornerRadius, Math.PI * 3 / 2, 0, false);
}
if (pointerDirection === RIGHT) { if (pointerDirection === RIGHT) {
context.lineTo(width, (height - pointerHeight)/2); context.lineTo(width, (height - pointerHeight)/2);
@ -14733,7 +14708,12 @@ var Kinetic = {};
context.lineTo(width, (height + pointerHeight)/2); context.lineTo(width, (height + pointerHeight)/2);
} }
if(!cornerRadius) {
context.lineTo(width, height); context.lineTo(width, height);
} else {
context.lineTo(width, height - cornerRadius);
context.arc(width - cornerRadius, height - cornerRadius, cornerRadius, 0, Math.PI / 2, false);
}
if (pointerDirection === DOWN) { if (pointerDirection === DOWN) {
context.lineTo((width + pointerWidth)/2, height); context.lineTo((width + pointerWidth)/2, height);
@ -14741,7 +14721,12 @@ var Kinetic = {};
context.lineTo((width - pointerWidth)/2, height); context.lineTo((width - pointerWidth)/2, height);
} }
if(!cornerRadius) {
context.lineTo(0, height); context.lineTo(0, height);
} else {
context.lineTo(cornerRadius, height);
context.arc(cornerRadius, height - cornerRadius, cornerRadius, Math.PI / 2, Math.PI, false);
}
if (pointerDirection === LEFT) { if (pointerDirection === LEFT) {
context.lineTo(0, (height + pointerHeight)/2); context.lineTo(0, (height + pointerHeight)/2);
@ -14749,6 +14734,11 @@ var Kinetic = {};
context.lineTo(0, (height - pointerHeight)/2); context.lineTo(0, (height - pointerHeight)/2);
} }
if(cornerRadius) {
context.lineTo(0, cornerRadius);
context.arc(cornerRadius, cornerRadius, cornerRadius, Math.PI, Math.PI * 3 / 2, false);
}
context.closePath(); context.closePath();
context.fillStrokeShape(this); context.fillStrokeShape(this);
} }

10
kinetic.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -197,9 +197,9 @@
}; };
Kinetic.Animation._runFrames = function() { Kinetic.Animation._runFrames = function() {
var layerHash = {}, var layerHash,
animations = this.animations, animations = this.animations,
anim, layers, func, n, i, layersLen, layer, key; anim, layers, func, n, i, layersLen, layer, key, needRedraw;
/* /*
* loop through all animations and execute animation * loop through all animations and execute animation
* function. if the animation object has specified node, * function. if the animation object has specified node,
@ -211,8 +211,8 @@
* WARNING: don't cache animations.length because it could change while * WARNING: don't cache animations.length because it could change while
* the for loop is running, causing a JS error * the for loop is running, causing a JS error
*/ */
var needRedraw = false;
for(n = 0; n < animations.length; n++) { for(n = 0; n < animations.length; n++) {
layerHash = {};
anim = animations[n]; anim = animations[n];
layers = anim.layers; layers = anim.layers;
func = anim.func; func = anim.func;
@ -220,22 +220,25 @@
anim._updateFrameObject(now()); anim._updateFrameObject(now());
layersLen = layers.length; layersLen = layers.length;
for (i=0; i<layersLen; i++) {
layer = layers[i];
if(layer._id !== undefined) {
layerHash[layer._id] = layer;
}
}
// if animation object has a function, execute it // if animation object has a function, execute it
if(func) { if (func) {
// allow anim bypassing drawing // allow anim bypassing drawing
needRedraw = (func.call(anim, anim.frame) !== false) || needRedraw; needRedraw = (func.call(anim, anim.frame) !== false);
}
} }
if (needRedraw) { if (needRedraw) {
for(key in layerHash) { for (i = 0; i < layersLen; i++) {
layer = layers[i];
if (layer._id !== undefined) {
layerHash[layer._id] = layer;
}
}
}
}
if (needRedraw) {
for (key in layerHash) {
layerHash[key].draw(); layerHash[key].draw();
} }
} }

View File

@ -55,11 +55,21 @@
Kinetic.Shape.call(this, config); Kinetic.Shape.call(this, config);
this.className = 'Sprite'; this.className = 'Sprite';
this.anim = new Kinetic.Animation(); this._updated = true;
var that = this;
this.anim = new Kinetic.Animation(function() {
// if we don't need to redraw layer we should return false
var updated = that._updated;
that._updated = false;
return updated;
});
this.on('animationChange.kinetic', function() { this.on('animationChange.kinetic', function() {
// reset index when animation changes // reset index when animation changes
this.frameIndex(0); this.frameIndex(0);
}); });
this.on('frameIndexChange.kinetic', function() {
this._updated = true;
});
// smooth change for frameRate // smooth change for frameRate
this.on('frameRateChange.kinetic', function() { this.on('frameRateChange.kinetic', function() {
if (!this.anim.isRunning()) { if (!this.anim.isRunning()) {

View File

@ -73,6 +73,128 @@ suite('Sprite', function() {
imageObj.src = 'assets/scorpion-sprite.png'; imageObj.src = 'assets/scorpion-sprite.png';
}); });
// ======================================================
test('don`t update layer too many times', function(done) {
var imageObj = new Image();
imageObj.onload = function() {
var stage = addStage();
var layer = new Kinetic.Layer();
var sprite = new Kinetic.Sprite({
x: 200,
y: 50,
image: imageObj,
animation: 'standing',
animations: {
standing: [
0, 0, 49, 109,
52, 0, 49, 109,
105, 0, 49, 109,
158, 0, 49, 109,
210, 0, 49, 109,
262, 0, 49, 109
]
},
frameRate: 5,
draggable: true,
shadowColor: 'black',
shadowBlur: 3,
shadowOffset: {x: 3, y:1},
shadowOpacity: 0.3
});
layer.add(sprite);
stage.add(layer);
var oldDraw = layer.draw;
var updateCount = 0;
layer.draw = function() {
updateCount++;
oldDraw.call(layer);
};
sprite.start();
setTimeout(function() {
sprite.stop();
assert.equal(updateCount < 7, true);
done();
}, 1000);
};
imageObj.src = 'assets/scorpion-sprite.png';
});
// ======================================================
test('don`t update layer too many times 2', function(done) {
var imageObj = new Image();
imageObj.onload = function() {
var stage = addStage();
var layer = new Kinetic.Layer();
var sprite = new Kinetic.Sprite({
x: 200,
y: 50,
image: imageObj,
animation: 'standing',
animations: {
standing: [
0, 0, 49, 109,
52, 0, 49, 109,
105, 0, 49, 109,
158, 0, 49, 109,
210, 0, 49, 109,
262, 0, 49, 109
]
},
frameRate: 5
});
var sprite2 = new Kinetic.Sprite({
x: 200,
y: 50,
image: imageObj,
animation: 'standing',
animations: {
standing: [
0, 0, 49, 109,
52, 0, 49, 109,
105, 0, 49, 109,
158, 0, 49, 109,
210, 0, 49, 109,
262, 0, 49, 109
]
},
frameRate: 20
});
layer.add(sprite).add(sprite2);
stage.add(layer);
var oldDraw = layer.draw;
var updateCount = 0;
layer.draw = function() {
updateCount++;
oldDraw.call(layer);
};
sprite.start();
sprite2.start();
setTimeout(function() {
sprite.stop();
sprite2.stop();
assert.equal(updateCount > 15, true);
assert.equal(updateCount < 27, true);
done();
}, 1000);
};
imageObj.src = 'assets/scorpion-sprite.png';
});
test('check is sprite running', function(done){ test('check is sprite running', function(done){
var imageObj = new Image(); var imageObj = new Image();
imageObj.onload = function() { imageObj.onload = function() {