diff --git a/src/AnimationSupport/AnimationInterfaceSupport.js b/src/AnimationSupport/AnimationInterfaceSupport.js deleted file mode 100644 index f45bcd933..000000000 --- a/src/AnimationSupport/AnimationInterfaceSupport.js +++ /dev/null @@ -1,366 +0,0 @@ -require('enyo'); - -var - kind = require('../kind'), - animator = require('./Core'), - frame = require('./Frame'), - utils = require('../utils'), - dispatcher = require('../dispatcher'); - -var extend = kind.statics.extend; - -kind.concatenated.push('animation'); - -var AnimationInterfaceSupport = { - - /** - * @private - */ - patterns: [], - - /** - * @private - */ - checkX: 0, - - /** - * @private - */ - checkY: 0, - - /** - * @private - */ - deltaX: 0, - - /** - * @private - */ - deltaY: 0, - - /** - * @private - */ - translateX: 0, - - /** - * @private - */ - translateY: 0, - - /** - * @private - */ - scrollValue: 0, - - /** - * @private - */ - deltaValueX: 0, - - /** - * @private - */ - deltaValueY: 0, - - /** - * @private - */ - checkDragStartX: 0, - - /** - * @private - */ - checkDragStartY: 0, - - /** - * @private - */ - deltaDragValueX: 0, - - /** - * @private - */ - deltaDragValueY: 0, - - /** - * @private - */ - setAnimateOne: 0, - - /** - * @private - */ - setAnimateTwo: 0, - - /** - * @private - */ - setAnimateThree: 0, - - /** - * @private - */ - eventArray: [ - "dragstart", - "dragend", - "drag", - "flick", - "down", - "move", - "scroll", - // "mousewheel", - "touchstart", - "touchmove", - "touchend", - "mousemove" - ], - - /** - * @public - */ - initialize: function() { - var i, eventArrayLength = this.eventArray.length; - for (i = 0; i < eventArrayLength; i++) { - dispatcher.listen(this.node, this.eventArray[i], this.bindSafely(this.detectTheEvent)); - } - }, - - /** - * @public - */ - detectTheEvent: function(inSender, inEvent) { - var eventType = inSender.type; - switch (eventType) { - case "dragstart": - this.touchDragStart(inSender, inEvent, inSender.pageX, inSender.pageY); - break; - case "drag": - this.touchDragMove(inSender, inEvent, inSender.pageX, inSender.pageY); - break; - case "dragend": - this.touchDragEnd(inSender, inEvent); - break; - case "flick": - this.handleMyEvent(inSender, inEvent); - break; - case "down": - this.handleMyEvent(inSender, inEvent); - break; - case "move": - this.handleMyEvent(inSender, inEvent); - break; - case "scroll": - this.scrollEvent(inSender, inEvent); - break; - case "mousewheel": - this.mousewheelEvent(inSender, inEvent); - break; - case "touchstart": - this.touchDragStart(inSender, inEvent, inSender.targetTouches[0].pageX, inSender.targetTouches[0].pageY); - break; - case "touchmove": - this.touchDragMove(inSender, inEvent, inSender.targetTouches[0].pageX, inSender.targetTouches[0].pageY); - break; - case "touchend": - this.touchDragEnd(inSender, inEvent); - break; - case "mousemove": - this.touchDragMove(inSender, inEvent, (inEvent, inSender.pageX) / 1.5, (inSender.pageY) / 1.5); - break; - default: - this.handleMyEvent(inSender, inEvent); - } - }, - - /** - * @public - */ - touchDragStart: function(inSender, inEvent, x, y) { - this.checkX = x; - this.checkY = y; - }, - - /** - * @public - */ - touchDragMove: function(inSender, inEvent, x, y) { - var currentX = x, - currentY = y; - - if (currentX !== 0 || currentY !== 0) { - this.deltaValueX = this.checkX - currentX; - - this.checkX = currentX; // set the initial position to the current position while moving - - this.deltaValueY = this.checkY - currentY; - - this.checkY = currentY; // set the initial position to the current position while moving - - //call commonTasks function with delta values - this.translateX = this.translateX + this.deltaValueX; - this.translateY = this.translateY + this.deltaValueY; - - this.setAnimateOne = this.translateX; - this.setAnimateTwo = this.translateX; - this.setAnimateThree = this.translateY; - - } - - }, - - /** - * @public - */ - touchDragEnd: function(inSender, inEvent, x, y) { - this.checkX = 0; - this.checkY = 0; - this.deltaValueX = 0; - this.deltaValueY = 0; - }, - - /** - * @public - */ - scrollEvent: function(inSender, inEvent) { - var delta = inSender.deltaY, - scrollTop = inSender.target.scrollTop, - scrollLeft = inSender.target.scrollLeft; - - if (this.scrollValue === 0) { - this.scrollValue = inSender.target.scrollTop; - } - - delta = inSender.target.scrollTop - this.scrollValue; - - this.deltaX = scrollLeft - this.deltaX; - this.deltaY = scrollTop - this.deltaY; - this.scrollValue = scrollTop; - - this.translateX = this.translateX + this.deltaX; - this.translateY = this.translateY + this.deltaY; - - //call commonTasks function with delta values - this.setAnimateOne = delta; - this.setAnimateTwo = this.translateX; - this.setAnimateThree = this.translateY; - - - this.deltaX = scrollLeft; - this.deltaY = scrollTop; - }, - - /** - * @public - */ - mousewheelEvent: function(inSender, inEvent) { - var delta = inSender.deltaY, - deltaX = inSender.wheelDeltaX, - deltaY = inSender.wheelDeltaY; - - this.translateX = this.translateX + deltaX; - this.translateY = this.translateY + deltaY; - - //call commonTasks function with delta values - this.setAnimateOne = delta; - this.setAnimateTwo = (-1 * (this.translateX)); - this.setAnimateThree = (-1 * (this.translateY)); - if (this.patterns[0].name === "Slideable" || this.patterns[0].name === "Parallax") { - this.setAnimateTwo = this.setAnimateThree; - } - }, - - /** - * @public - */ - commonTasks: function(delta, deltax, deltay) { - var patternsLength = this.patterns.length; - if (delta !== 0) { - delta = delta / Math.abs(delta); - } - //Call specific interface - for (var i = 0; i < patternsLength; i++) { - if (this.patterns[i].name === "Fadeable") { - this.patterns[i].fadeByDelta.call(this, delta); - } else if (this.patterns[i].name === "Flippable") { - this.patterns[i].doFlip.call(this, delta); - } else if (this.patterns[i].name === "Slideable") { - if (this.parallax === true) { - for (var j = 0; j < this.children.length; j++) { - var current = this.children[j]; - animator.trigger(current); - this.patterns[i].slide.call(current, (-1 * deltax / current.speed), (-1 * deltay / current.speed), 0); - current.start(true); - } - } else { - this.patterns[i].slide.call(this, (-1 * deltax), (-1 * deltay), 0); - } - } - if (this.patterns[i].name !== "Slideable") { - this.setAnimateOne = 0; - this.setAnimateTwo = 0; - this.setAnimateThree = 0; - } - } - this.start(true); - }, - - /** - * @public - */ - handleMyEvent: function(inSender, inEvent) { - /*TODO:*/ - }, - - /** - * @public - */ - commitAnimation: function(x, y, z) { - var i, len; - - if (this.patterns && Object.prototype.toString.call(this.patterns) === "[object Array]" && (len = this.patterns.length)) { - for (i = 0; i < len; i++) { - /*if (typeof this.patterns[i].triggerEvent === 'function') { - patterns[i].triggerEvent(); - }*/ - this.commonTasks(this.setAnimateOne, this.setAnimateTwo, this.setAnimateThree); - } - } - }, - - /** - * @private - */ - rendered: kind.inherit(function(sup) { - return function() { - sup.apply(this, arguments); - this.initialize(); - }; - }) -}; - -module.exports = AnimationInterfaceSupport; - -/** - Hijacking original behaviour as in other Enyo supports. -*/ -var sup = kind.concatHandler; - -/** - * @private - */ -kind.concatHandler = function(ctor, props, instance) { - sup.call(this, ctor, props, instance); - var aPattern = props.pattern; - if (aPattern && Object.prototype.toString.call(aPattern) === "[object Array]") { - var proto = ctor.prototype || ctor; - extend(AnimationInterfaceSupport, proto); - - this.patterns = aPattern; - var len = this.patterns.length; - for (var i = 0; i < len; i++) { - extend(this.patterns[i], proto); - } - animator.register(proto); - } -}; diff --git a/src/AnimationSupport/AnimationSupport.js b/src/AnimationSupport/AnimationSupport.js deleted file mode 100644 index f7894217b..000000000 --- a/src/AnimationSupport/AnimationSupport.js +++ /dev/null @@ -1,295 +0,0 @@ -require('enyo'); - -var - kind = require('../kind'), - animation = require('./Core'), - activator = require('./KeyFrame'), - delegator = require('./EventDelegator'), - EventEmitter = require('../EventEmitter'), - FrameEditor = require('./FrameEditor'), - frame = require('./Frame'), - utils = require('../utils'); - -var extend = kind.statics.extend; - -kind.concatenated.push('animation'); - -var AnimationSupport = { - - /** - * @private - */ - //name: 'AnimationSupport', - animating: false, - - /** - * To keep a character active for it to apply some other - * animation at runtime. This gives a preformance boost when on - * character an animation is reapplied. - * @default false - So the once the animation is completed, it has to be retriggered to - * start a new animation. - * @private - */ - active: false, - - /** - * Holds variouts states of animation. - * Like: 'started' - Character animation has started(within rAF) - * 'paused' - Character animation has paused(within rAF) - * 'resumed' - Character animation has resumed(within rAF) - * 'completed' - Character animation has finished(within rAF) - * @private - */ - animationState: "", - - /** - * To check if the event delta value is changed - * @private - */ - deltaChanged: false, - - /** - * To hold the name of the animation event which occured on the character - * @private - */ - eventName: "", - - /** - * Holds delta value in the order [x, y, z, rad] - * @private - */ - animDelta: [], - - - vScrollX: 0, - - vScrollY: 0, - - vScrollZ: 0, - - prevDur: 0, - - totalDuration: 0, - - - /** - * Maximum threshold for animation - * @private - */ - animMaxThreshold: [], - - _animPose: [], - - _pose: [], - - mixins: [EventEmitter, FrameEditor], - - _eventCache: {}, - - - /** - * Check if the character is suitable for animation - * @public - */ - ready: function() { - var ret = this.generated && this.animating; - if (ret && this._startTime) - ret = this._startTime <= utils.perfNow(); - - if(ret) this.set('animationState', 'started'); - return ret; - }, - - /** - * Sets current animation state for this character - * @public - */ - setInitial: function (initial) { - this._startAnim = initial ? frame.copy(initial) : {}; - }, - - - /** - * Sets animation distance for this character - * @public - */ - setDistance: function (dist) { - this.distance = dist; - }, - - /** - * Gets animation distance for this character - * @public - */ - getDistance: function () { - return this.distance; - }, - - /** - * Gets current state of animation for this character - * @public - */ - initiate: function (current) { - var dom = this.hasNode(), dur, - pose = frame.getComputedProperty(dom, undefined, current); - pose.duration = 0; - this._animPose = []; - this._animPose.push(pose); - this.currentState = pose.currentState; - frame.accelerate(dom, pose.matrix); - - if(this.animate !== true) { - dur = this.getDuration() || 0; - this.addAnimation(this.animate, dur); - } - }, - - /** - * Adds new animation on already existing animation for this character. - * @public - */ - addAnimation: function (newProp, duration) { - if (this.prevDur === 0 && duration === 0) { - this._animPose[0] = {animate: newProp, duration: 0}; - } else { - this.prevDur = duration || this.prevDur; - this.totalDuration += this.prevDur; - this._animPose.push({animate: newProp, duration: this.totalDuration}); - } - }, - - /** - * Sets new animation for this character. - * @public - */ - setAnimation: function (newProp) { - this._prop = newProp; - }, - - - /** - * Sets the delta values of x, y and z for events - * @param {Object} obj - Object contains dX, dY and dZ as keys - * @public - */ - setAnimationDelta: function (ev) { - this._eventCache.dX = ev.dX + this._eventCache.dX || 0; - this._eventCache.dY = ev.dY + this._eventCache.dY || 0; - this._eventCache.dZ = ev.dZ + this._eventCache.dZ || 0; - this._eventCache[ev.vtype] = ev; - - this.deltaChanged = true; - this.eventCacheUpdated = true; - - }, - - /** - * Gets the delta values of x, y and z for events - * @public - */ - getAnimationDelta: function () { - return this._eventCache[this._virtualEvent]; - }, - /** - * Gets how long animation is active on this character - * @public - */ - getDuration: function() { - return this._duration || this.duration; - }, - - /** - * Sets how long animation should be active on this character - * @public - */ - setDuration: function (newDuration) { - this._duration = newDuration; - }, - - /** - * Idnetify when the character has done animating. - * This triggers "onAnimated" event on this character - * @public - */ - completed: function() { - return this.onAnimated && this.onAnimated(this); - }, - - /** - * Trigger animation for this character. - * @public - */ - start: function (active, delay) { - this._startTime = utils.perfNow() + (delay || 0) ; - this._lastTime = this._startTime + this._duration; - this.animating = true; - this.active = active; - }, - - /** - * Trigger the registered event to all the listeners - * @public - */ - triggerEvent: function () { - this.deltaChanged = false; - return delegator.emitEvent(this, this.getAnimationDelta()); - }, - - /** - * @private - */ - rendered: kind.inherit(function (sup) { - return function () { - sup.apply(this, arguments); - this.initiate(); - if (this.handleAnimationEvents) { - delegator.register(this); - } - }; - }), - - /** - * @private - */ - destroy: kind.inherit(function(sup) { - return function() { - animation.remove(this); - animation.deRegister(this); - if (this.handleAnimationEvents) { - delegator.deRegister(this); - } - sup.apply(this, arguments); - }; - }) -}; - -module.exports = AnimationSupport; - -/** - Hijacking original behaviour as in other Enyo supports. -*/ -var sup = kind.concatHandler; - -/** -* @private -*/ -kind.concatHandler = function (ctor, props, instance) { - sup.call(this, ctor, props, instance); - if (props.animate || props.keyFrame || props.pattern || props.handleAnimationEvents) { - var proto = ctor.prototype || ctor; - extend(AnimationSupport, proto); - // if (props.keyFrame && typeof props.keyFrame != 'function') { - // activator.animate(proto, props); - // } - if ((props.animate && typeof props.animate != 'function' ) || - (props.keyFrame && typeof props.keyFrame != 'function')) { - animation.trigger(proto); - } - if (props.handleAnimationEvents && typeof props.handleAnimationEvents != 'function') { - animation.register(proto); - } - if (props.pattern && typeof props.pattern != 'function') { - animation.register(proto); - } - } -}; \ No newline at end of file diff --git a/src/AnimationSupport/Core.js b/src/AnimationSupport/Core.js deleted file mode 100644 index 723bd4fdf..000000000 --- a/src/AnimationSupport/Core.js +++ /dev/null @@ -1,221 +0,0 @@ -require('enyo'); - -var - kind = require('../kind'), - animation = require('../animation'), - utils = require('../utils'), - director = require('./Director'); - -var ts, wasTs, - CoreObject = require('../CoreObject'); - -/** -* This module returns the Loop singleton -* Core module is responsible for handling all animations happening in Enyo. -* The responsibilities of this module is to; -* - Trigger vendor specific rAF. -* - Knowing all elements which have requested for animation. -* - Tween animation frames for each characters. -* -* @module enyo/Core -*/ -module.exports = kind.singleton({ - /** @lends module:enyo/Core */ - - /** - * @private - */ - name: 'enyo.Core', - /** - * @private - */ - kind: CoreObject, - - /** - * @private - */ - chracs: [], - - /** - * @private - */ - evnts: [], - - /** - * @private - */ - req: 0, - - /** - * @private - */ - running: false, - - /** - * Core base API to start animation functionalities. - * The purpose of this method is to check if the animation is already started or not - * otherwise wake up core to handle animation for a character. - * - * As of now this method is provided as an interface for application - * to directly trigger an animation. However, this will be later made private - * and will be accessible only by the interfaces exposed by framework. - * @parameter charc- Animation character - * - * @public - */ - trigger: function (charc) { - if (!charc.animating) { - this.chracs.push(charc); - } - if (!this.running) { - this.running = true; - this.start(); - } - }, - - /** - * Core public API to check if core is handling animation for particular - * document element. - * - * As of now this method is provided as an interface for application - * to directly trigger an animation. However, this will be later made private - * and will be accessible only by the interfaces exposed by framework. - * @parameter charc- Animation character - * - * @public - */ - exists: function (eventTarget) { - for (var i = 0; i < this.chracs.length; i++) { - if (this.chracs[i].hasNode() === eventTarget) { // Already Animating - return this.chracs[i]; - } - } - }, - - /** - * Animator public API to remove animation happening on a particular - * document element. - * - * As of now this method is provided as an interface for application - * to directly trigger an animation. However, this will be later made private - * and will be accessible only by the interfaces exposed by framework. - * @parameter charc- Animation character - * - * @public - */ - remove: function (curr) { - var i = this.chracs.indexOf(curr); - if (i >= 0) this.chracs.splice(i, 1); - }, - - /** - * Animator public API to pause animation happening on all the - * characters. - * - * As of now this method is provided as an interface for application - * to directly trigger an animation. However, this will be later made private - * and will be accessible only by the interfaces exposed by framework. - * - * @public - */ - pause: function () { - for (var i = 0; i < this.chracs.length; i++) { - this.chracs[i].animating = false; - } - }, - - - /** - * Animator public API to register character with event - * - * @parameter charc- Animation character - * - * @public - */ - register: function (charc) { - this.deRegister(charc); - this.evnts.push(charc); - this.remove(charc); - charc.animating = true; - - if (!this.isTicking) { - this.dummy(); - this.isTicking = true; - } - }, - - deRegister: function (curr) { - var idx = this.evnts.indexOf(curr); - if (idx >= 0) this.evnts.splice(idx, 1); - }, - - /** - * @private - */ - start: function () { - this.req = animation.requestAnimationFrame(this.bindSafely(this.loop)); - }, - - /** - * @private - */ - cancel: function () { - animation.cancelRequestAnimationFrame(this.req); - }, - - /** - * @private - */ - loop: function () { - var i, curr, - len = this.chracs.length; - - if (len <= 0) { - this.cancel(); - this.running = false; - return; - } - - for (i = 0; i < len; i++) { - curr = this.chracs[i]; - if (curr && curr.ready()) { - ts = utils.perfNow(); - director.take(curr, ts - (curr.wasTs || ts)); - curr.wasTs = ts; - } - } - this.start(); - }, - - /** - * @private - */ - eventLoop: function () { - var i, curr, status, evlen = this.evnts.length; - for (i = 0; i < evlen; i++) { - curr = this.evnts[i]; - if (this.evnts[i].patterns && typeof this.evnts[i].patterns.length > 0) { - this.evnts[i].commitAnimation(); - } - ts = utils.perfNow(); - if (curr && curr.ready()) { - if (curr.deltaChanged) { - status = curr.triggerEvent(); - } - if (!status && curr.eventCacheUpdated) { - director.shot(curr, ts - (wasTs || ts)); - } - } - wasTs = ts; - } - this.dummy(); - }, - - /** - * TODO: Merge this implementation with actual start - * @private - */ - dummy: function () { - animation.requestAnimationFrame(this.eventLoop.bind(this)); - } -}); \ No newline at end of file diff --git a/src/AnimationSupport/Director.js b/src/AnimationSupport/Director.js index 12ec4ba3a..7deb94795 100644 --- a/src/AnimationSupport/Director.js +++ b/src/AnimationSupport/Director.js @@ -1,105 +1,137 @@ require('enyo'); -var frame = require('./Frame'), - tween = require('./Tween'), - utils = require('../utils'); +var tween = require('./Tween'), + utils = require('../utils'); + +var dur, tm, t; /** -* This module returns the Loop singleton -* Core module is responsible for handling all animations happening in Enyo. -* The responsibilities of this module is to; -* - Trigger vendor specific rAF. -* - Knowing all elements which have requested for animation. -* - Tween animation frames for each characters. -* -* @module enyo/Core +* Contains the declaration for the {@link module:enyo/AnimationSupport/Director} module. +* This modules exposes the features to support 'Director' approach. +* @module enyo/AnimationSupport/Director */ module.exports = { -/** - * Tweens public API which notifies to change current state of - * a character. This method is normally trigger by the Animation Core to - * update the animating characters state based on the current timestamp. - * - * As of now this method is provided as an interface for application - * to directly trigger an animation. However, this will be later made private - * and will be accessible only by the interfaces exposed by framework. - * @parameter chrac- Animating character - * ts- DOMHighResTimeStamp - * - * @public + + /** + * This method checks if all the actors of the given {@link @module enyo/AnimationSupport/Scene} object + * is rendered. If the actors are rendered then all them are initialized and prepared to be ready for animation. + * @param {@link @module enyo/AnimationSupport/Scene} scene Scene which contains actors to be prepared for animation. + * @return {boolean} Returns true if all the actors + * of the scene is active and ready for action, + * otherwise false */ - - take: function(actor, ts) { - var dur = actor.totalDuration, - tm = actor.rolePlay(ts); - - if (tm < 0) return; - if (tm < dur) { - this.action(actor, tm); - } else { - this.action(actor, tm); - this.cut(actor); - } - }, + roll: function (scene) { + var actor, + actors = scene.rolePlays ? scene.rolePlays[scene.getID()]: [], + l = actors ? actors.length: 0, + active = true; - cut: function (actor) { - actor.animating = false; - actor.timeline = 0; - actor.completed(actor); - actor.set('animationState', 'completed'); - if (!actor.active) { - // this.remove(actor); + for (var i = 0; i < l; i++) { + actor = actors[i]; + if(actor.generated) { + tween.init(actor); + active = false; + } } + scene.active = active; }, - action: function(actor, since) { - var pose, t, - index = this.poseByTime(actor._animPose, since), - props = actor._animPose[index], - prevDur = actor._animPose[(index - 1) < 0 ? 0 : (index - 1)].duration, - currentAnimSince = since - prevDur, - runningDur = props.duration - prevDur; - if (!props._startAnim) { - pose = frame.getComputedProperty(actor.hasNode(), props.animate, actor.currentState); - utils.mixin(props, pose); - } + /** + * take method is invloved in time based animation. This method will + * be executed continuously in order tween the actor for every frame until the animation + * is completed (i.e. until elapsed time is equal to the duration). + * be animated based on the delta and the acceleration. + * @param {@link @module enyo/AnimationSupport/Scene} scene Scene on which the animation will be performed + * @param {Number} ts Elapsed time since the animation of this pose has started (ratio in factor of 1) + */ + take: function (scene, ts) { + dur = scene.span; + tm = scene.timeline; - if (currentAnimSince < 0) return; - if (currentAnimSince <= runningDur && runningDur !== 0) { - t = currentAnimSince / runningDur; - tween.step(actor, props, ( t > 0.98) ? t = 1 : t, runningDur); + if (isNaN(tm) || tm < 0) return; + if (tm <= dur) { + scene.pose = scene.action(ts, scene.pose); } else { - tween.step(actor, props, 1, runningDur); + scene.timeline = dur; + scene.animating = false; } }, - poseByTime: function(arr, duration) { - var startIndex = 0, - stopIndex = arr.length - 1, - middle = Math.floor((stopIndex + startIndex) / 2); + /** + * action is the primary method which triggers the animation of the actor for every frame. + * This method calculates the start and end animation positions and the elapsed time since the animation + * has started and tweens the actor based on the these values. + * @param {Object} pose Animation poses + * @param {@link module:enyo/Component~Component} actor Component on which the animation should be performed + * @param {Number} since Elapsed time since the animation of this pose has started + * @param {Number} dur Total duration of this pose + */ + action: function (pose, actor, since, dur) { + if (!pose._startAnim) tween.init(actor, pose); - if(duration === 0) { - return startIndex; + if (since < 0) since = 0; + if (since <= dur && dur !== 0) { + t = since / dur; + tween.step(actor, pose, ( t > 0.98) ? 1 : t, dur); + } else { + tween.step(actor, pose, 1, dur); } + }, + + /** + * Casts an actor or all the actors in the array to the given scene. + * @param {@link module:enyo/Component~Component} actors actor or Array of actors which needs to be casted in the scene. + * @param {@link @module enyo/AnimationSupport/Scene} scene Scene to which the actors has to be connected. + */ + cast: function (actors, scene) { + var acts = utils.isArray(actors) ? actors : [actors], + id = scene.getID(), + rolePlays = scene.rolePlays || {}; - while (arr[middle].duration != duration && startIndex < stopIndex) { - if (duration < arr[middle].duration) { - stopIndex = middle; - } else if (duration > arr[middle].duration) { - startIndex = middle + 1; - } - - middle = Math.floor((stopIndex + startIndex) / 2); + if (!rolePlays[id]) { + rolePlays[id] = acts; + } else { + rolePlays[id] = acts.reduce(function(actors, actor) { + actors.push( actor ); + return actors; + }, rolePlays[id]); } + scene.rolePlays = rolePlays; + }, - return (arr[middle].duration != duration) ? startIndex : middle; + /** + * Disconnects actor or Array of actors from the scene + * @param {Array.} actors actor or Array of actors which needs to be casted in the scene. + * @param {@link @module enyo/AnimationSupport/Scene} scene Scene from which the actors has to be removed. + */ + reject: function (scene, actors) { + var id = scene.getID(), acts, + rolePlays = scene.rolePlays || []; + actors = actors || rolePlays[id]; + acts = utils.isArray(actors) ? actors : [actors]; + if (rolePlays[id]) { + rolePlays[id] = acts.reduce(function(actors, actor) { + var i = actors.indexOf(actor); + if (i >= 0) actors.splice(i, 1); + return actors; + }, rolePlays[id]); + } + scene.rolePlays = rolePlays; }, - shot: function(chrac, ts) { - var v1, s, a, v, + /** + * shot method is invloved in distance based animation in which the distance definite and + * indefinite (Event based animations). This method calculates the distance to which the actor has to + * be animated based on the delta and the acceleration. + * @param {@link module:enyo/Component~Component} actor Component on which the animation should be performed + * @param {Number} ts delta distance + * @return {Number} The distance to which the actor has to be transformed + */ + shot: function(actor, ts) { + var v1, s, a, v = 0, t = ts, - dt = chrac._eventCache, - dir = this.angle(chrac.direction), + dt = actor.getAnimationDelta(), + dir = this.angle(actor.direction), v0 = dt.velocity || 0; v1 = dt[dir] / t; @@ -115,10 +147,13 @@ module.exports = { dt[dir] = 0; } dt.velocity = v1; - this.take(chrac, dt[dir] > 0 ? v : -v); - } + } + return dt[dir] > 0 ? v : -v; }, + /** + * @private + */ angle: function (direction) { switch(direction) { case "X" : diff --git a/src/AnimationSupport/Easings.js b/src/AnimationSupport/Easings.js index cfcbf6d58..8ec19ab99 100644 --- a/src/AnimationSupport/Easings.js +++ b/src/AnimationSupport/Easings.js @@ -1,25 +1,32 @@ /** - * Interface to achieve Easings in various animations - * - * @module enyo/AnimationSupport/Easings - * @public + * Beginning time + * @type {number} */ -var matrixUtil = require('./Matrix'); - var b = 0, - c = 1; -var temp = null, + /** + * Change in time + * @type {number} + */ + c = 1, + temp = null, tempProp = null, tempOldState = [], tempNewState = []; +/** + * This module provdes an interface to achieve various types of Easings in animations + * + * @module enyo/AnimationSupport/Easings + */ +var easings = module.exports = { -var easings = { /** + * Use this function to check whether the ease object has changed or not. * @public - * apply the function to check whether the ease object has changed - * @params currentEase : the ease object which is currently available + * @param {object} currentEase - ease object which we want to check + * @return {boolean} - Boolean value for easechanged. + * True - Yes. The ease object got changed. + * False - No. The ease object has not changed. */ - easeChanged: function(currentEase) { if (temp === null) { // setting the values for the first time @@ -36,10 +43,12 @@ var easings = { } }, + /** + * Use this function to check whether the animating property of the object has changed or not * @public - * apply the function to check whether the animating property of the object has changed - * @params currentProp : the animating property of the object which is currently available + * @param {string} currentProp - Name of the animating property like - "Translate/Opacity/Scale/Rotate" + * @return {boolean} - Boolean value for propChange. Either True or False */ propChange: function(currentProp) { @@ -57,12 +66,13 @@ var easings = { } }, + /** + * Use this function to check whether the oldState of the object has changed or not * @public - * apply the function to check whether the oldState of the object has changed - * @params currentOldState : the oldState of the object which is currently available + * @param {object} currentOldState - currentOldState object + * @return {boolean} - Boolean value for oldStateChange. Either True or False */ - oldStateChange: function(currentOldState) { if (tempOldState === null) { // setting the values for the first time @@ -80,12 +90,13 @@ var easings = { } }, + /** - * @public - * apply the function to check whether the newState of the object has changed - * @params currentOldState : the newState of the object which is currently available + * Use this function to check whether the newStateChange of the object has changed or not + * @public + * @param {object} currentNewState -currentNewState object + * @return {boolean} - Boolean value for newStateChange. Either True or False */ - newStateChange: function(currentNewState) { if (tempNewState === null) { // setting the values for the first time @@ -103,12 +114,14 @@ var easings = { } }, + /** + * Use this function to compare the states which are arrays * @public - * apply the function to compare the states which are arrays - * @params currentOldState : x is the previous state and y is the current state + * @param {Number[]} x - old array of the same + * @param {Number[]} y - current array of the same + * @return {boolean} - True/ False after comparing the parameters */ - compareStates: function(x, y) { var xLen = x.length; var yLen = y.length; @@ -134,40 +147,12 @@ var easings = { return true; }, - calculateEase: function(easeObj, startPoint, endPoint) { - var order = (easeObj && Object.keys(easeObj).length) ? (Object.keys(easeObj).length + 1) : 0; - var controlPoints = [startPoint], - bValues = [], - m1 = [], - m2 = [], - m3 = [], - m4 = [], - l = 0; - - var t, a; - for (var key in easeObj) { - t = parseFloat(key) / 100; - a = parseFloat(easeObj[key]) / 100; - bValues = easings.getBezierValues(t, order); - bValues.shift(); - m1.push(a - bValues.pop()); - m2.push(bValues); - } - - m3 = matrixUtil.inverseN(m2, bValues.length); - - m4 = matrixUtil.multiplyN(m3, m1); - l = m4.length; - for (var i = 0; i < l; i++) { - controlPoints.push([m4[i], m4[i], m4[i]]); - } - - controlPoints.push(endPoint); - return controlPoints; - }, /** + * This function returns the coefficents based on the order and the current position * @private - * @params n: order, k: current position + * @param {number} n - order + * @param {number} k - current position + * @return {object} - coefficients */ getCoeff: function(n, k) { n = parseInt(n, 10); @@ -189,9 +174,13 @@ var easings = { return n * this.getCoeff(n - 1, k - 1) / k; }, + /** + * Function to get the bezier coeffients based on the time and order * @public - * @params t: time, n: order + * @param {number} t - time + * @param {number} n - order + * @return {object} - bezier coefficients */ getBezierValues: function(t, n) { t = parseFloat(t, 10), @@ -206,7 +195,7 @@ var easings = { var c, values = [], - + x = (1 - t), y = t; // @@ -220,154 +209,231 @@ var easings = { return values; }, - + /** + * Current time multiplied with duration + * @public + * @param {number} t - current time + * @param {number} d - duration + * @return {number} - time + */ timeCheck: function(t, d) { t = t * d; return t; }, + /** + * EaseInQuad * @public - * apply the below type of ease to the DOM element + * @param {number} t - current time + * @param {number} d - duration + * @return {number} - calculated time */ easeInQuad: function(t, d) { t = easings.timeCheck(t, d); return c * (t /= d) * t + b; }, + /** + * EaseOutQuad * @public - * apply the below type of ease to the DOM element + * @param {number} t - current time + * @param {number} d - duration + * @return {number} calculated time */ easeOutQuad: function(t, d) { t = easings.timeCheck(t, d); return -c * (t /= d) * (t - 2) + b; }, + /** + * EaseInOutQuad * @public - * apply the below type of ease to the DOM element + * @param {number} t - current time + * @param {number} d - duration + * @return {number} calculated time */ easeInOutQuad: function(t, d) { t = easings.timeCheck(t, d); if ((t /= d / 2) < 1) return c / 2 * t * t + b; return -c / 2 * ((--t) * (t - 2) - 1) + b; }, + /** + * EaseInCubic * @public - * apply the below type of ease to the DOM element + * @param {number} t - current time + * @param {number} d - duration + * @return {number} calculated time */ easeInCubic: function(t, d) { t = easings.timeCheck(t, d); return c * (t /= d) * t * t + b; }, + /** + * EaseOutCubic * @public - * apply the below type of ease to the DOM element + * @param {number} t - current time + * @param {number} d - duration + * @return {number} calculated time */ easeOutCubic: function(t, d) { t = easings.timeCheck(t, d); return c * ((t = t / d - 1) * t * t + 1) + b; }, + /** + * EaseInOutCubic * @public - * apply the below type of ease to the DOM element + * @param {number} t - current time + * @param {number} d - duration + * @return {number} calculated time */ easeInOutCubic: function(t, d) { t = easings.timeCheck(t, d); if ((t /= d / 2) < 1) return c / 2 * t * t * t + b; return c / 2 * ((t -= 2) * t * t + 2) + b; }, + /** + * EaseInQuart * @public - * apply the below type of ease to the DOM element + * @param {number} t - current time + * @param {number} d - duration + * @return {number} calculated time */ easeInQuart: function(t, d) { t = easings.timeCheck(t, d); return c * (t /= d) * t * t * t + b; }, + /** + * EaseOutQuart * @public - * apply the below type of ease to the DOM element + * @param {number} t - current time + * @param {number} d - duration + * @return {number} calculated time */ easeOutQuart: function(t, d) { t = easings.timeCheck(t, d); return -c * ((t = t / d - 1) * t * t * t - 1) + b; }, + /** + * EaseInOutQuart * @public - * apply the below type of ease to the DOM element + * @param {number} t - current time + * @param {number} d - duration + * @return {number} calculated time */ easeInOutQuart: function(t, d) { t = easings.timeCheck(t, d); if ((t /= d / 2) < 1) return c / 2 * t * t * t * t + b; return -c / 2 * ((t -= 2) * t * t * t - 2) + b; }, + /** + * EaseInQuint * @public - * apply the below type of ease to the DOM element + * @param {number} t - current time + * @param {number} d - duration + * @return {number} calculated time */ easeInQuint: function(t, d) { t = easings.timeCheck(t, d); return c * (t /= d) * t * t * t * t + b; }, /** + * EaseOutQuint * @public - * apply the below type of ease to the DOM element + * @param {number} t - current time + * @param {number} d - duration + * @return {number} calculated time */ easeOutQuint: function(t, d) { t = easings.timeCheck(t, d); return c * ((t = t / d - 1) * t * t * t * t + 1) + b; }, + /** + * EaseInOutQuint * @public - * apply the below type of ease to the DOM element + * @param {number} t - current time + * @param {number} d - duration + * @return {number} calculated time */ easeInOutQuint: function(t, d) { t = easings.timeCheck(t, d); if ((t /= d / 2) < 1) return c / 2 * t * t * t * t * t + b; return c / 2 * ((t -= 2) * t * t * t * t + 2) + b; }, + /** + * EaseInSine * @public - * apply the below type of ease to the DOM element + * @param {number} t - current time + * @param {number} d - duration + * @return {number} calculated time */ easeInSine: function(t, d) { t = easings.timeCheck(t, d); return -c * Math.cos(t / d * (Math.PI / 2)) + c + b; }, + /** + * EaseOutSine * @public - * apply the below type of ease to the DOM element + * @param {number} t - current time + * @param {number} d - duration + * @return {number} calculated time */ easeOutSine: function(t, d) { t = easings.timeCheck(t, d); return c * Math.sin(t / d * (Math.PI / 2)) + b; }, + /** + * EaseInOutSine * @public - * apply the below type of ease to the DOM element + * @param {number} t - current time + * @param {number} d - duration + * @return {number} calculated time */ easeInOutSine: function(t, d) { t = easings.timeCheck(t, d); return -c / 2 * (Math.cos(Math.PI * t / d) - 1) + b; }, + /** + * EaseInExpo * @public - * apply the below type of ease to the DOM element + * @param {number} t - current time + * @param {number} d - duration + * @return {number} calculated time */ easeInExpo: function(t, d) { t = easings.timeCheck(t, d); return (t === 0) ? b : c * Math.pow(2, 10 * (t / d - 1)) + b; }, + /** + * EaseOutExpo * @public - * apply the below type of ease to the DOM element + * @param {number} t - current time + * @param {number} d - duration + * @return {number} calculated time */ easeOutExpo: function(t, d) { t = easings.timeCheck(t, d); return (t === d) ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b; }, + /** + * EaseInOutExpo * @public - * apply the below type of ease to the DOM element + * @param {number} t - current time + * @param {number} d - duration + * @return {number} calculated time */ easeInOutExpo: function(t, d) { t = easings.timeCheck(t, d); @@ -376,34 +442,50 @@ var easings = { if ((t /= d / 2) < 1) return c / 2 * Math.pow(2, 10 * (t - 1)) + b; return c / 2 * (-Math.pow(2, -10 * --t) + 2) + b; }, + /** + * EaseInCirc * @public - * apply the below type of ease to the DOM element + * @param {number} t - current time + * @param {number} d - duration + * @return {number} calculated time */ easeInCirc: function(t, d) { t = easings.timeCheck(t, d); return -c * (Math.sqrt(1 - (t /= d) * t) - 1) + b; }, + /** + * EaseOutCirc * @public - * apply the below type of ease to the DOM element + * @param {number} t - current time + * @param {number} d - duration + * @return {number} calculated time */ easeOutCirc: function(t, d) { t = easings.timeCheck(t, d); return c * Math.sqrt(1 - (t = t / d - 1) * t) + b; }, + /** + * EaseInOutCirc * @public - * apply the below type of ease to the DOM element + * @param {number} t - current time + * @param {number} d - duration + * @return {number} calculated time */ easeInOutCirc: function(t, d) { t = easings.timeCheck(t, d); if ((t /= d / 2) < 1) return -c / 2 * (Math.sqrt(1 - t * t) - 1) + b; return c / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1) + b; }, + /** + * EaseInElastic * @public - * apply the below type of ease to the DOM element + * @param {number} t - current time + * @param {number} d - duration + * @return {number} calculated time */ easeInElastic: function(t, d) { var a = c, @@ -419,9 +501,13 @@ var easings = { } else s = p / (2 * Math.PI) * Math.asin(c / a); return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b; }, + /** + * EaseOutElastic * @public - * apply the below type of ease to the DOM element + * @param {number} t - current time + * @param {number} d - duration + * @return {number} calculated time */ easeOutElastic: function(t, d) { var a = c, @@ -437,9 +523,13 @@ var easings = { } else s = p / (2 * Math.PI) * Math.asin(c / a); return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b; }, + /** + * EaseInOutElastic * @public - * apply the below type of ease to the DOM element + * @param {number} t - current time + * @param {number} d - duration + * @return {number} calculated time */ easeInOutElastic: function(t, d) { var a = c, @@ -456,27 +546,39 @@ var easings = { if (t < 1) return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b; return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p) * 0.5 + c + b; }, + /** + * EaseInBack * @public - * apply the below type of ease to the DOM element + * @param {number} t - current time + * @param {number} d - duration + * @return {number} calculated time */ easeInBack: function(t, d, s) { t = easings.timeCheck(t, d); if (!s) s = 1.70158; return c * (t /= d) * t * ((s + 1) * t - s) + b; }, + /** + * EaseOutBack * @public - * apply the below type of ease to the DOM element + * @param {number} t - current time + * @param {number} d - duration + * @return {number} calculated time */ easeOutBack: function(t, d, s) { t = easings.timeCheck(t, d); if (s === undefined) s = 1.70158; return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b; }, + /** + * EaseInOutBack * @public - * apply the below type of ease to the DOM element + * @param {number} t - current time + * @param {number} d - duration + * @return {number} calculated time */ easeInOutBack: function(t, d, s) { t = easings.timeCheck(t, d); @@ -484,17 +586,25 @@ var easings = { if ((t /= d / 2) < 1) return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b; return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b; }, + /** + * EaseInBounce * @public - * apply the below type of ease to the DOM element + * @param {number} t - current time + * @param {number} d - duration + * @return {number} calculated time */ easeInBounce: function(t, d) { t = easings.timeCheck(t, d); return c - easings.easeOutBounce((d - t) / d, d) + b; }, + /** + * EaseOutBounce * @public - * apply the below type of ease to the DOM element + * @param {number} t - current time + * @param {number} d - duration + * @return {number} calculated time */ easeOutBounce: function(t, d) { t = easings.timeCheck(t, d); @@ -508,9 +618,13 @@ var easings = { return c * (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375) + b; } }, + /** + * EaseInOutBounce * @public - * apply the below type of ease to the DOM element + * @param {number} t - current time + * @param {number} d - duration + * @return {number} calculated time */ easeInOutBounce: function(t, d) { t = easings.timeCheck(t, d); @@ -519,4 +633,3 @@ var easings = { } }; -module.exports = easings; diff --git a/src/AnimationSupport/EventDelegator.js b/src/AnimationSupport/EventDelegator.js deleted file mode 100644 index b2691cf27..000000000 --- a/src/AnimationSupport/EventDelegator.js +++ /dev/null @@ -1,222 +0,0 @@ -require('enyo'); - -var - dispatcher = require('../dispatcher'), - emitter = require('../EventEmitter'); - - -var eventsMap = { - vdrag: "drag", - vscroll: "scroll", - vmousewheel: "mousewheel", - vtouch: "touchmove", - drag: "vdrag", - scroll: "vscroll", - mousewheel: "vmousewheel", - touchmove: "vtouch" -}; -/** -* This module handles the animation events for the character. -* If the character has opted to have animations handled by animation framework, -* then it can add "handleAnimationEvents" as true as its property. -* The character can also mention which events he wants to be handled by the framework by -* providing list of animation events in "animationEvents" block like; -* { -* name: "myKind", -* animationEvents: [ -* "scroll", -* "mousewheel", -* "touchstart", -* "touchmove", -* "touchend" -* ] -* } -* -* By default these events are handled within the framework(others for now have to be handled by the application). -* -* This module is here temporarily, need to have a proper mechanism -* like dispatcher to handle animation related events along with application events. -* -* @module enyo/AnimationSupport/EventDelegator -*/ -var EventDelegator = { - - /** - * @private - */ - eventArray: [ - "drag", - "scroll", - "dragstart", - "mousewheel", - "touchstart", - "touchmove", - "touchend" - ], - - /** - * Attaches the evnet handlers to the character either its own events or - * else default events with the framework. As of now only these events are - * supported; - * - scroll - * - touch - * - mousewheel - * @public - */ - register: function (charc) { - var events = charc.handleAnimationEvents || {}; - for (var key in events) { - this.addRemoveListener(charc, key, events[key]); - } - }, - - /** - * Detaches the evnet handlers from the character either its own events or - * else default events from with the framework. As of now only these events are - * supported; - * - scroll - * - touch - * - mousewheel - * @public - */ - deRegister: function (charc) { - var events = charc.handleAnimationEvents || {}; - for (var key in events) { - this.addRemoveListener(charc, key, events[key], true); - } - }, - - /** - * @private - */ - addRemoveListener: function(charc, name, callback, remove) { - var d = remove ? dispatcher.stopListening : dispatcher.listen, - e = eventsMap[name]; - d(charc.hasNode(), e, charc.bindSafely(this[e + 'Event'], charc)); - - var fn = remove ? emitter.off : emitter.on; - fn.apply(emitter, [name, charc[callback], charc]); - }, - - /** - * @private - */ - emitEvent: function(charc, data) { - return emitter.vemit.call(emitter, data); - }, - - /** - * @private - */ - touchstartEvent: function (sender, inEvent) { - sender.touchX = inEvent.targetTouches[0].pageX; - sender.touchY = inEvent.targetTouches[0].pageY; - }, - - /** - * @private - */ - touchmoveEvent: function (sender, inEvent) { - var x = inEvent.targetTouches[0].pageX, - y = inEvent.targetTouches[0].pageY; - - if(x !== 0 || y !== 0) { - /*sender.animDelta[0] = sender.touchX - x; - sender.animDelta[1] = sender.touchY - y; - sender.animDelta[2] = 0;*/ - - // var o = { - // dX: (sender.touchX - x), - // dY: (sender.touchY - y), - // dZ: 0 - // }; - // sender.setAnimationDelta(o); - // sender.touchX = x; - // sender.touchY = y; - - // this.eventName = eventsMap[inEvent.type]; - - console.log(inEvent.targetTouches[0]); - - inEvent.dX = inEvent.deltaX; - inEvent.dY = inEvent.deltaY; - inEvent.dZ = 0; - inEvent.vtype = eventsMap[inEvent.type]; - - inSender.setAnimationDelta(inEvent); - inSender._virtualEvent = eventsMap[inEvent.type]; - } - }, - - /** - * @private - */ - touchendEvent: function (sender, ev) { - sender.touchX = 0; - sender.touchY = 0; - }, - - /** - * @private - */ - scrollEvent: function (inSender, inEvent) { - inEvent.dX = inEvent.deltaX; - inEvent.dY = inEvent.deltaY; - inEvent.dZ = 0; - inEvent.vtype = eventsMap[inEvent.type]; - - inSender.setAnimationDelta(inEvent); - inSender._virtualEvent = eventsMap[inEvent.type]; - }, - - /** - * @private - */ - dragstartEvent: function (inSender, inEvent) { - this.dragLeft = inEvent.offsetX, - this.dragTop = inEvent.offsetY; - }, - - /** - * @private - */ - dragEvent: function (inSender, inEvent) { - var dragLeft = inEvent.offsetX, - dragTop = inEvent.offsetY; - if (dragLeft && dragTop) { - this.deltaX = this.dragLeft - dragLeft; - this.deltaY = this.dragTop - dragTop; - - this.dragLeft = dragLeft, - this.dragTop = dragTop; - - /*this.animDelta[0] = this.deltaX; - this.animDelta[1] = this.deltaY; - this.animDelta[2] = 0;*/ - - var o = { - dX: this.deltaX, - dY: this.deltaY, - dZ: 0 - }; - this.setAnimationDelta(o); - - this.eventName = eventsMap[inEvent.type]; - } - }, - - /** - * @private - */ - mousewheelEvent: function (sender, inEvent) { - inEvent.dX = inEvent.deltaX; - inEvent.dY = inEvent.deltaY; - inEvent.dZ = 0; - inEvent.vtype = eventsMap[inEvent.type]; - - sender.setAnimationDelta(inEvent); - sender._virtualEvent = eventsMap[inEvent.type]; - } -}; - -module.exports = EventDelegator; \ No newline at end of file diff --git a/src/AnimationSupport/Fadeable.js b/src/AnimationSupport/Fadeable.js deleted file mode 100644 index 425589767..000000000 --- a/src/AnimationSupport/Fadeable.js +++ /dev/null @@ -1,86 +0,0 @@ -var - kind = require('../kind'), - animation = require('./Core'); - -/** - * Interface to achieve fade animation - * - * @module enyo/AnimationSupport/Fadeable - * @public - */ -module.exports = { - - /** - * @private - */ - name: 'Fadeable', - - /** - * To start animation - */ - animate: true, - - /** - * @private - */ - fadableValue: 0, - - /** - * @public - * Make the character invisible - */ - invisible: function() { - this.addAnimation({ - opacity: 0 - }); - }, - - /** - * @public - * Make the character transparent - * @default 0.5 - * @parameter value - set transparency value - */ - transparent: function(value) { - value = value || 0.5; - this.addAnimation({ - opacity: value - }); - }, - - /** - * @public - * Make the character visible - */ - opaque: function() { - this.addAnimation({ - opacity: 1 - }); - }, - - /** - * @public - * Fade element based on event trigger - */ - fadeByDelta: function(deltaValue) { - if (deltaValue !== 0) { - this.fadableValue = this.fadableValue + deltaValue * 0.1; - if (this.fadableValue <= 0) { - this.fadableValue = 0; - } else if (this.fadableValue >= 1) { - this.fadableValue = 1; - } - } - this.addAnimation({ - opacity: this.fadableValue - }); - }, - - /** - * @public - * Bubble the fadeable event - */ - /*triggerEvent: function(e) { - this.doFadeStart(); - }*/ -}; diff --git a/src/AnimationSupport/Flippable.js b/src/AnimationSupport/Flippable.js deleted file mode 100644 index aaa5c727c..000000000 --- a/src/AnimationSupport/Flippable.js +++ /dev/null @@ -1,71 +0,0 @@ -var - kind = require('../kind'), - animation = require('./Core'); - -/** - * Interface to achieve flip animation - * - * @module enyo/AnimationSupport/Flippable - * @public - */ -module.exports = { - - /** - * @private - */ - name: 'Flippable', - - /** - * To start animation - */ - animate: true, - - /** - * Specifies the direction of flip. Accepted value are 'X', 'Y', 'Z' - * - * @type {String} - * @default 'X' - * @public - */ - flipDirection: 'X', - - /** - * Specifies the flip up-to angle. Accepted value are in degree - * - * @type {Number} - * @default '0' - * @public - */ - flipAngle: 0, - - /** - * @public - * apply animation to the flippable DOM object - */ - doFlip: function(deltaValue) { - this.setAxis(deltaValue); - }, - - /** - * @public - * set axis of rotation of flippable DOM object - */ - setAxis: function(delta) { - var css = {}; - var dir = ""; - this.flipAngle = this.flipAngle + delta * 10; - switch (this.flipDirection) { - case "X": - dir = "1,0,0," + this.flipAngle; - break; - case "Y": - dir = "0,1,0," + this.flipAngle; - break; - case "Z": - dir = "0,0,1," + this.flipAngle; - break; - } - css["rotate"] = dir; - this.addAnimation(css); - } -}; diff --git a/src/AnimationSupport/Frame.js b/src/AnimationSupport/Frame.js index 2393d0f77..64958157a 100644 --- a/src/AnimationSupport/Frame.js +++ b/src/AnimationSupport/Frame.js @@ -1,34 +1,40 @@ +/*jslint white: true*/ require('enyo'); var Dom = require('../dom'), Vector = require('./Vector'), + utils = require('../utils'), Matrix = require('./Matrix'); var - COLOR = {"color": 1, "backgroundColor": 1}, - TRANSFORM = {"translate": 1, "translateX": 1, "translateY": 1, "translateZ": 1, "rotateX": 1, "rotateY": 1, "rotateZ": 1, "rotate": 1, "skew": 1, "scale": 1, "perspective": 1}; + BORDER = {'border-radius': 1, 'border-top-left-radius': 1, 'border-top-right-radius': 1, 'border-bottom-left-radius': 1, 'border-bottom-right-radius': 1, 'border-image-slice': 1}, + COLOR = {'color': 1, 'background-color': 1, 'border-color': 1, 'border-top-color': 1, 'border-left-color': 1, 'border-right-color': 1, 'border-bottom-color': 1, 'fill': 1, 'flood-color': 1,'lighting-color': 1, 'stop-color': 1, 'outline-color': 1}, + INT_UNIT = {'z-index': 1}, + OPACITY = {'opacity': 1, 'flood-opacity': 1, 'stop-opacity': 1, 'fill-opacity': 1, 'stroke-opacity': 1}, + TRANSFORM = {translate: 1, translateX: 1, translateY: 1, translateZ: 1, rotateX: 1, rotateY: 1, rotateZ: 1, rotate: 1, skew: 1, scale: 1, perspective: 1}; /** -* Frame is a module responsible for providing animation features required for a frame. -* This module exposes bunch of animation API's like matrix calculation, -* fetching inital DOM properties and also applying style updates to DOM. -* -* These methods need to be merged with DOM API's of enyo. -* -* @module enyo/AnimationSupport/Frame -*/ + * Frame is a module responsible for providing animation features required for a frame. + * This module exposes bunch of animation API's like matrix calculation, + * fetching initial DOM properties and also applying style updates to DOM. + * + * These methods need to be merged with DOM API's of enyo. + * + * @module enyo/AnimationSupport/Frame + */ var frame = module.exports = { /** - * @public - * Creates a matrix based on transformation vectors. - * @param: trns- translate vector - * rot - rotate quaternion vector - * sc - scale vector - * sq - sqew vector - * per - perspective vector - */ - recomposeMatrix: function(trns, rot, sc, sq, per) { + * Calculate matrix3d of a frame based on transformation vectors. + * @public + * @param {Number[]} trns Translate vector + * @param {Number[]} rot Rotate quaternion vector + * @param {Number[]} sc Scale vector + * @param {Number[]} sq Skew vector + * @param {Number[]} per Perspective vector + * @return {Number[]} Final Matrix3d for particular frame + */ + recomposeMatrix: function (trns, rot, sc, sq, per) { var i, x = rot[0], y = rot[1], @@ -91,13 +97,13 @@ var frame = module.exports = { }, /** - * @public - * Get transformation vectors out of matrix - * @param matrix - Transformation matrix - * ret - Return object which holds translate, - * rotate, scale, sqew & perspective. - */ - decomposeMatrix: function(matrix, ret) { + * Decompose transformation vectors into various properties out of matrix3d. + * @public + * @param {Number[]} matrix Matrix3d + * @param {Object} ret To store various transformation properties like translate, rotate, scale, skew and perspective. + * @return {Boolean} true, if matrix exists else false. + */ + decomposeMatrix: function (matrix, ret) { var i, tV = [], rV = [], @@ -135,7 +141,7 @@ var frame = module.exports = { row[1] = Vector.normalize(row[1]); skV[0] /= scV[1]; - // Compute XZ and YZ shears, orthogonalize 3rd row + // Compute XZ and YZ shears, orthogonalized 3rd row skV[1] = Vector.dot(row[0], row[2]); row[2] = Vector.combine(row[2], row[0], 1.0, -skV[1]); skV[2] = Vector.dot(row[1], row[2]); @@ -175,34 +181,43 @@ var frame = module.exports = { }, /** - * Clones an array based on offset value. - * @public - */ - copy: function(v, offset) { + * Clones an array based on offset value. + * @public + * @param {Object} v Object with transformation properties like translate, rotate, scale, skew and perspective. + * @param {Number} offset Determine how many Object to copy. + * @return {Number[]} Array with sliced value based on offset. + */ + copy: function (v, offset) { return Array.prototype.slice.call(v, offset || 0); }, /** - * Validates if property is a transform property. - * @public - */ - isTransform: function(transform) { + * Validates if property is a transform property. + * @public + * @param {String} transform Any transform property, for which we want to identify whether or not the property is transform. + * @return {Number} Value of the required transform property. + */ + isTransform: function (transform) { return TRANSFORM[transform]; }, /** - * Applies trasnformation to DOM element with the matrix values. - * @public - */ - accelerate: function (ele, m) { + * Applies transformation to DOM element with the Matrix3d values. + * @public + * @param {enyo.Component} actor Component to be animated. + * @param {Number[]} m Matrix3d + */ + accelerate: function (actor, m) { m = m ? m : Matrix.identity(); - frame.setTransformProperty(ele, m); + frame.setTransformProperty(actor, m); }, /** - * Reform matrix 2D to 3D - * @public - */ + * Reform matrix 2D to 3D + * @public + * @param {Number[]} v Matrix(2d) + * @return {Number[]} Matrix3d + */ parseMatrix: function (v) { var m = Matrix.identity(); v = v.replace(/^\w*\(/, '').replace(')', ''); @@ -221,19 +236,33 @@ var frame = module.exports = { }, /** - * Converts comma seperated values to array. - * @public - */ + * Converts comma separated values to array. + * @public + * @param {String} val Value of required animation in any property. + * @return {Number[]} Create array from val. + */ parseValue: function (val) { return val.toString().split(",").map(function(v) { return parseFloat(v, 10); }); }, + parseShortHand: function (val, length) { + var res; + if (val.indexOf('rgb') === 0) { + res = this.parseValue(val.split(')')[0].replace(/^\w*\(/, '').concat(val.split(')')[1])); + } else { + res = this.parseValue(val.split('rgb(')[1].replace(')',',').concat(val.split('rgb(')[0]).replace(/, $/,'')); + } + return res.concat(Array(length - res.length).fill(0)); + }, + /** - * Gets a matrix for DOM element. - * @public - */ + * Gets a matrix for DOM element. + * @public + * @param {HTMLElement} style CSS style declaration. + * @return {Number[]} Matrix3d + */ getMatrix: function (style) { var m = style.getPropertyValue('transform') || style.getPropertyValue('-moz-transform') || @@ -247,16 +276,21 @@ var frame = module.exports = { }, /** - * Gets a style property applied from the DOM element. - * @param: style - Computed style of a DOM. - * key - property name for which style has to be fetched. - * @public - */ + * Gets a style property applied from the DOM element. + * @public + * @param {HTMLElement} style Computed style of a DOM. + * @param {String} key Property name for which style has to be fetched. + * @return {Number|HTMLElement} + */ getStyleValue: function (style, key) { var v = style.getPropertyValue(key) || style[key]; - if (v === undefined || v === null || v == "auto") { + if (!v || v === "auto") { return 0; } + if (key === 'box-shadow') { + if (v === 'none') return Array(7).fill(0); + return v.split(')')[0].replace(/^\w*\(/, '').concat(v.split(')')[1].split(' ').join(',')); + } if (COLOR[key]) { return v.replace(/^\w*\(/, '').replace(')', ''); } @@ -267,60 +301,82 @@ var frame = module.exports = { return v; }, - /** - * Applies style property to DOM element. - * @public - */ - setProperty: function (ele, prop, val) { + * Applies style property to DOM element. + * @public + * @param {enyo.Component} actor Component to be animated. + * @param {String} prop CSS property to be applied. + * @param {Number} val Value of the property applied. + */ + setProperty: function (actor, prop, val) { if (COLOR[prop]) { val = val.map(function(v) { return parseInt(v, 10);}); - val = 'rgb('+ val + ')'; - } else if (prop == 'opacity') { + val = 'rgb('+ val + ')'; + } else if(INT_UNIT[prop]) { + val = parseInt(val[0], 10); + } else if (BORDER[prop]) { + val = val[0] + '%'; + } else if (OPACITY[prop]) { val = val[0].toFixed(6); val = (val <= 0) ? '0.000001' : val; + } else if (prop === 'box-shadow') { + val = 'rgb('+ val.slice(0, 3).map(function(v) { return parseInt(v, 10);}) + + ') ' + val.slice(3).map(function(v) {return v + 'px'}).join(' '); } else { val = val[0] + 'px'; } - ele.style[prop] = val; + + actor.addStyles(prop + ':' + val + ';'); }, /** - * Applies transform property to DOM element. - * @public - */ - setTransformProperty: function (element, matrix) { + * Applies transform property to DOM element. + * @public + * @param {enyo.Component} actor Component to be animated. + * @param {Number[]} matrix Matrix3d + */ + setTransformProperty: function (actor, matrix) { var mat = Matrix.toString(matrix); - element.style.transform = mat; + /*element.style.transform = mat; element.style.webkitTransform = mat; element.style.MozTransform = mat; element.style.msTransform = mat; - element.style.OTransform = mat; + element.style.OTransform = mat;*/ + actor.addStyles('transform:' + mat + ';' + + 'webkitTransform:' + mat + ';' + + 'MozTransform:' + mat + ';' + + 'msTransform' + mat + ';' + + 'OTransform' + mat + ';'); }, /** - * Get DOM node animation properties. - * @param: node- DOM node - * props- Properties to fetch from DOM. - * initial-Default properties to be applied. - * @public - */ + * Get DOM node animation properties. + * @public + * @param {HTMLElement} node DOM node + * @param {Object} props Properties to fetch from DOM. + * @param {Object} initial Default properties to be applied. + * @return {Object} Object with various animation properties. + */ getComputedProperty: function (node, props, initial) { if(!node) return; var eP = {}, - sP = initial ? this.copy(initial) : {}, + sP = initial ? utils.mixin({}, initial) : {}, tP = {}, dP = {}, - m, k, v, + m, k, u, v, s = initial ? undefined : Dom.getComputedStyle(node); for (k in props) { v = sP[k]; if (!this.isTransform(k)) { v = v || this.getStyleValue(s || Dom.getComputedStyle(node), k); - eP[k] = this.parseValue(props[k]); sP[k] = this.parseValue(v); + if (k === 'box-shadow' || COLOR[k]) { + eP[k] = this.parseShortHand(props[k], sP[k].length); + } else { + eP[k] = this.parseValue(props[k]); + } } else { v = this.parseValue(props[k]); //tP[k] = k == 'rotate' ? Vector.toQuant(v) : v; @@ -330,7 +386,7 @@ var frame = module.exports = { if (initial) { dP.translate = initial.translate; - dP.rotate = initial.rotate; + dP.rotate = initial.rotate.length < 4 ? Vector.toQuant(initial.rotate) : initial.rotate; dP.scale = initial.scale; dP.skew = initial.skew; dP.perspective = initial.perspective; @@ -346,10 +402,18 @@ var frame = module.exports = { return {_startAnim: sP, _endAnim: eP, _transform: dP, currentState: dP, matrix: m, props: props}; }, + /** + * Get DOM node animation distance. + * @public + * @param {HTMLElement} prop DOM node properties. + * @param {Object} initalProp Initial properties to fetch from DOM. + * @param {Object} finalProp Final properties to be applied. + * @return {Object} Total computed distance to animate. + */ getComputedDistance: function (prop, initalProp, finalProp) { var k, sV, eV, dst, tot = 0; for (k in prop) { - sV = k==='rotate' ? Vector.quantToVector(initalProp[k]) : initalProp[k]; + sV = (k === 'rotate' ? Vector.quantToVector(initalProp[k]) : initalProp[k]); eV = finalProp[k]; dst = Vector.distance(eV, sV); tot += dst; diff --git a/src/AnimationSupport/FrameEditor.js b/src/AnimationSupport/FrameEditor.js deleted file mode 100644 index 20632f5fb..000000000 --- a/src/AnimationSupport/FrameEditor.js +++ /dev/null @@ -1,63 +0,0 @@ -require('enyo'); - -module.exports = { -/** - * - * - * @public - */ - - timeline: 0, - _cachedValue: 0, - _frameSpeed: 1, - - - cache: function(){ - if(this._frameSpeed === 0){ - this._frameSpeed = this._cachedValue; - } - }, - play : function (){ - this._frameSpeed = 1; - }, - - resume: function() { - this.cache(); - this._frameSpeed *= 1; - }, - - pause: function () { - this._cachedValue = this._frameSpeed; - this._frameSpeed = 0; - }, - - reverse: function () { - this.cache(); - this._frameSpeed *= -1; - }, - - fast: function (mul) { - this.cache(); - this._frameSpeed *= mul; - }, - - slow: function (mul) { - this.cache(); - this._frameSpeed *= mul; - }, - - stop: function () { - this._cachedValue = 1; - this._frameSpeed = 0; - this.timeline = 0; - }, - - rolePlay: function (t) { - this.timeline += _rolePlay(t, this._frameSpeed); - return this.timeline; - } -}; - -function _rolePlay(t, mul) { - return mul * t; -} \ No newline at end of file diff --git a/src/AnimationSupport/HierarchicalMixin.js b/src/AnimationSupport/HierarchicalMixin.js deleted file mode 100644 index e618f58c4..000000000 --- a/src/AnimationSupport/HierarchicalMixin.js +++ /dev/null @@ -1,120 +0,0 @@ -require('enyo'); - -var - kind = require('../kind'), - Dom = require('../dom'), - animation = require('./Core'), - VerticalDelegate = require('../VerticalDelegate'); - -/** -* A mixin support for Hierarchical components -* @module enyo/HierarchicalMixin -*/ -module.exports = { - /** - * @private - */ - _pageScrolltop: 0, - - /** - * @private - */ - _paged: false, - - /** - * Mixin creation - * - * @method - * @private - */ - create: kind.inherit(function(sup) { - return function() { - sup.apply(this, arguments); - this.addListener('paging', this._pagingHandler.bind(this)); - this.clientHeight = Dom.getWindowHeight(); - }; - }), - - /** - * Mixin creation - * - * @method - * @private - */ - didScroll: kind.inherit(function(sup) { - return function() { - sup.apply(this, arguments); - var top = event ? event.scrollBounds.top : 0; - if (this._paged) { - this._pageScrolltop = top; - this._paged = false; - } - this._animateChild(this.controls, event ? event.scrollBounds.top - this._pageScrolltop: 0); - }; - }), - - /** - * Handler for pagging event when its triggered from vertical delegate of data grid list - * - * @method - * @private - */ - _pagingHandler: function() { - this._paged = true; - for (var i=0, node; (node = this.controls[i]); i++) { - node._bounds = node.getAbsoluteBounds(); - } - }, - - /** - * Apply animation on all the child nodes which are visible inside the viewport - * - * @method - * @private - */ - _animateChild: function(nodes, top) { - var showing; - for (var i=0, node; (node = nodes[i]); i++) { - showing = this._getNodeShowing(node, top); - if (node.hasNode() && showing && !node.animating) { - node.start(true); - } - } - }, - - /** - * Checks if the node element in visible inside the viewport - * - * @method - * @private - */ - _getNodeShowing: function(node, top) { - var showing, rect = node._bounds; - if (rect) { - showing = ((rect.top >= top) && ((rect.top - top) <= this.clientHeight)); - } else { - showing = false; - } - return showing; - } -}; - -/** - Hijacking original behaviour of delegates. -*/ -var sup = VerticalDelegate.generate; - -VerticalDelegate.generate = function(list) { - sup.call(this, list); - for (var i=0, p; (p=list.pages[i]); ++i) { - for (var j=0, c; (c=p.children[j]); ++j) { - c._bounds = c.getAbsoluteBounds(); - c.animate = list.animate; - c.duration = list.duration; - animation.trigger(c); - if (list._getNodeShowing(c, 0)) { - c.start(true); - } - } - } -}; \ No newline at end of file diff --git a/src/AnimationSupport/KeyFrame.js b/src/AnimationSupport/KeyFrame.js deleted file mode 100644 index 61b904498..000000000 --- a/src/AnimationSupport/KeyFrame.js +++ /dev/null @@ -1,133 +0,0 @@ -require('enyo'); - -var - kind = require('../kind'), - animation = require('./Core'), - utils = require('../utils'), - CoreObject = require('../CoreObject'); - -/** - * This module returns the Loop singleton - * @module enyo/KeyFrame - */ -var keyFrame = module.exports = kind.singleton({ - /** @lends module:enyo/KeyFrame */ - - /** - * @private - */ - name: 'enyo.KeyFrame', - /** - * @private - */ - kind: CoreObject, - - /** - * KeyFrame base API to perform animation on any document element - * repersented as a Character. The purpose of this method is to add a new - * character to Animation Core based on animation properties passed as - * parameter to this function and also to manage the frames allocated to - * each of individual poses. - * - * As of now this method is provided as an interface for application - * to directly trigger an animation. However, this will be later made private - * and will be accessible only by the interfaces exposed by framework. - * @parameter charc- Character responsible for animation. - * keyframe- Key frame Animation propeties represented as CSS objects. - * like: {0: {"rotateX": "0"}, 50: {"rotateX": "90"}, 100: {"rotateX": "180"}} - * @public - */ - animate: function(charc, proto) { - var prop, easeInd, - cb = proto.completed, - keyframe = proto.keyFrame; - charc.keyProps = []; - charc.keyTime = []; - charc.currentIndex = 0; - for (prop in keyframe) { - charc.keyTime.push(prop); - charc.keyProps.push(keyframe[prop]); - } - charc.keyframeCallback = cb; - charc.totalDuration = proto.duration; - this.keyFraming(charc); - charc.completed = this.bindSafely(this.reframe); - //this.keyFraming(charc); - this.trigger(charc); - }, - - /** - * KeyFrame's public API to reverse an animation. - * The purpose of this method is to find the animating character based on - * the DOM provided and reversing a keyframe animation by interchanging its intial - * state with final state and final state with current state - * - * As of now this method is provided as an interface for application - * to directly trigger an animation. However, this will be later made private - * and will be accessible only by the interfaces exposed by framework. - * @parameter dom- Document element on which animation will be reversed. - * - * @public - */ - reverse: function(dom) { - var charc = animation.exists(dom), - finalState, duration; - if (charc) { - finalState = charc._startAnim; - duration = utils.perfNow() - charc.initialTime; - animation.remove(charc); - - charc.setAnimation(finalState); - charc.setInitial(charc.currentState); - charc.setDuration(duration); - charc.totalDuration = duration; - charc.keyProps = []; - charc.keyTime = []; - charc.animating = false; - this.trigger(charc); - } - }, - - trigger: function(charc) { - if (charc.handleAnimationEvents && typeof charc.handleAnimationEvents != 'function') { - animation.register(charc); - } else - animation.trigger(charc); - } -}); - -/** - * @private - */ -keyFrame.keyFraming = function(charc) { - var index = charc.currentIndex || 0, - old = charc.keyTime[index - 1] || 0, - next = charc.keyTime[index], - total = charc.totalDuration, - change = total ? total * ((next - old) / 100) : "0"; - charc.addAnimation(charc.keyProps[index]); - - // code to separate the ease component from keyframe and making it available for animation - if (charc.keyProps[index].hasOwnProperty('ease')) { - charc.ease = charc.keyProps[index].ease; - delete charc.keyProps[index].ease; - } - - if (charc.totalDuration) charc.setDuration(change); - charc.animating = false; - charc.currentIndex = index; -}; - -/** - * @private - */ -keyFrame.reframe = function(charc) { - charc.reverse ? charc.currentIndex-- : charc.currentIndex++; - if (charc.currentIndex >= 0 && charc.currentIndex < charc.keyTime.length) { - this.keyFraming(charc); - charc.start(true); - } else { - //Tigerring callback function at end of animation - charc.keyframeCallback && charc.keyframeCallback(this); - } -}; diff --git a/src/AnimationSupport/Matrix.js b/src/AnimationSupport/Matrix.js index fb59144ed..9befb02c4 100644 --- a/src/AnimationSupport/Matrix.js +++ b/src/AnimationSupport/Matrix.js @@ -1,56 +1,81 @@ require('enyo'); /** -* Matrix module for matrix related calculation -* -* @module enyo/AnimationSupport/Matrix -*/ + * Matrix module for matrix related calculation + * + * @module enyo/AnimationSupport/Matrix + */ module.exports = { /** - * @public - */ + * To create Identity Matrix3d (4X4 order). + * @public + * @return {Number[]} Identity Matrix3d + */ identity: function() { return [1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]; }, /** - * @public - */ + * To translate in any dimension based on co-ordinates. + * @public + * @param {Number} x Translate value in X axis + * @param {Number} y Translate value in Y axis + * @param {Number} z Translate value in Z axis + * @return {Number[]} Matrix3d + */ translate: function (x, y, z) { return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x, y ? y : 0, z ? z : 0, 1]; }, /** - * @public - */ + * To translate in x dimension + * @public + * @param {Number} x Translate value in X axis + * @return {Number[]} Matrix3d + */ translateX: function (x) { return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x ? x : 0, 0, 0, 1]; }, /** - * @public - */ + * To translate in y dimension + * @public + * @param {Number} y Translate value in Y axis + * @return {Number[]} Matrix3d + */ translateY: function (y) { return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, y ? y : 0, 0, 1]; }, /** - * @public - */ + * To translate in z dimension + * @public + * @param {Number} z Translate value in Z axis + * @return {Number[]} Matrix3d + */ translateZ: function (z) { return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, z ? z : 0, 1]; }, /** - * @public - */ + * To scale in any dimension + * @public + * @param {Number} x Scale value in X axis + * @param {Number} y Scale value in Y axis + * @param {Number} z Scale value in Z axis + * @return {Number[]} Matrix3d + */ scale: function (x, y, z) { return [x, 0, 0, 0, 0, y ? y : 1, 0, 0, 0, 0, z ? z : 1, 0, 0, 0, 0, 1]; }, /** - * @public - */ + * To skew in any dimension (skew can only happen in 2d) + * @public + * @param {Number} a Skew value in X axis + * @param {Number} b Skew value in Y axis + * @return {Number[]} Matrix3d + */ skew: function (a, b) { a = a ? Math.tan(a * Math.PI / 180): 0; b = b ? Math.tan(b * Math.PI / 180): 0; @@ -58,8 +83,11 @@ module.exports = { }, /** - * @public - */ + * To rotate in x-axis + * @public + * @param {Number} a Rotate value in X axis + * @return {Number[]} Matrix3d + */ rotateX: function (a) { var cosa, sina; a = a * Math.PI / 180; @@ -69,8 +97,11 @@ module.exports = { }, /** - * @public - */ + * To rotate in y-axis + * @public + * @param {Number} b Rotate value in Y axis + * @return {Number[]} Matrix3d + */ rotateY: function (b) { var cosb, sinb; b = b * Math.PI / 180; @@ -80,8 +111,11 @@ module.exports = { }, /** - * @public - */ + * To rotate in z-axis + * @public + * @param {Number} g Rotate value in Z axis + * @return {Number[]} Matrix3d + */ rotateZ: function (g) { var cosg, sing; g = g * Math.PI / 180; @@ -91,8 +125,13 @@ module.exports = { }, /** - * @public - */ + * To rotate in any dimension + * @public + * @param {Number} a Rotate value in X axis + * @param {Number} b Rotate value in Y axis + * @param {Number} g Rotate value in Z axis + * @return {Number[]} Matrix3d + */ rotate: function (a, b, g) { a = a * Math.PI / 180; b = b * Math.PI / 180; @@ -122,8 +161,12 @@ module.exports = { }, /** - * @public - */ + * To multiply 2 Martix3d (4x4 order) + * @public + * @param {Number[]} m1 1st Matrix3d + * @param {Number[]} m2 2nd Matrix3d + * @return {Number[]} Resultant Matrix3d + */ multiply: function(m1, m2) { return [ m1[0] * m2[0] + m1[4] * m2[1] + m1[8] * m2[2], @@ -146,8 +189,12 @@ module.exports = { }, /** - * @public - */ + * To multiply 2 matrices (NxN order) + * @public + * @param {Number[]} m1 1st Matrix of order N + * @param {Number[]} m2 2nd Matrix of order N + * @return {Number[]} Resultant Matrix of order N + */ multiplyN: function(m1, m2) { var i, j, sum, m = [], @@ -165,8 +212,12 @@ module.exports = { }, /** - * @public - */ + * To inverse matrix of order N + * @public + * @param {Number[]} matrix Matrix (NxN order) + * @param {Number} n Order of the matrix + * @return {Number[]} Inverted Matrix + */ inverseN: function(matrix, n) { var i, j, k, r, t, precision = 100000, @@ -212,8 +263,11 @@ module.exports = { }, /** - * @public - */ + * Convert Matrix3d array to Matrix3d String + * @public + * @param {Number[]} m Matrix3d Array + * @return {String} Matrix3d String + */ toString: function (m) { var ms = 'matrix3d('; for (var i = 0; i < 15; i++) { diff --git a/src/AnimationSupport/Parallax.js b/src/AnimationSupport/Parallax.js deleted file mode 100644 index e52680d8c..000000000 --- a/src/AnimationSupport/Parallax.js +++ /dev/null @@ -1,63 +0,0 @@ -/*jslint white: true*/ -var - kind = require('../kind'), - utils = require('../utils'), - animation = require('./Core'), - Slideable = require('enyo/AnimationSupport/Slideable'); - -/** - * Interface to achieve Parallax animation - * - * @module enyo/AnimationSupport/Parallax - * @public - */ -module.exports = { - - /** - * @private - */ - name: 'Parallax', - - /** - * @private - */ - animate: true, - - /** - * @public - * - */ - rendered: kind.inherit(function(sup) { - return function() { - sup.apply(this, arguments); - this.doParallax(this); - }; - }), - - /** - * @public - * - */ - commonTasks: function(delta, deltax, deltay) { - var children = this.children; - for (var i = 0; i < children.length; i++) { - var speed = children[i].speed; - children[i].slide.call(children[i], (-1 * deltax) / speed, (-1 * deltay) / speed, 0); - children[i].start(true); - } - }, - - /** - * @public - * - */ - doParallax: function(container, deltax, deltay) { - var container = this.children; - for (var i = 0; i < this.children.length; i++) { - var currentElement = this.children[i]; - utils.mixin(currentElement, Slideable); - animation.trigger(currentElement); - } - } - -}; diff --git a/src/AnimationSupport/Scene.js b/src/AnimationSupport/Scene.js new file mode 100644 index 000000000..fa6b7ad4a --- /dev/null +++ b/src/AnimationSupport/Scene.js @@ -0,0 +1,405 @@ +var + editor = require('./SceneEditor'), + director = require('./Director'), + animation = require('../animation'), + utils = require('../utils'); + +/** + * This module exports "Scene" which is a class/constructor so that we can create an instance of the same. + * We can define all the animation properties we want in the application in the instance of the "Scene". + * + * @module enyo/AnimationSupport/Scene + */ +var Scene = module.exports = function(props) { + var scene = Scene.create(), + dur = props.duration || 0; + + utils.mixin(scene, editor); + + if (props.animation) { + var anims = utils.isArray(props.animation) ? props.animation : [props.animation]; + + for (var i = 0; i < anims.length; i++) { + scene.addAnimation(anims[i], anims[i].span || anims[i].duration || dur, anims[i].delay); + delete anims[i].duration; + delete anims[i].delay; + } + delete props.animation; + delete props.duration; + } + + utils.mixin(scene, props); + utils.mixin(scene, SceneAction); + return scene; +}; + + +/** + * Creates a empty instance of scene. + * Can be used for runtime creation of animations + * @memberOf module:enyo/AnimationSupport/Scene + * @public + * @return {Object} An instance of the constructor + */ +Scene.create = function() { + return new sceneConstructor(utils.uid("@")); +}; + + +/** + * Connects an actor/s to a scene. + * All the actors should be added before initiating animation otherwise actors will animate for remaining time span + * @memberOf module:enyo/AnimationSupport/Scene + * @public + * @param {Object} actors - The elements which needs to be animated + * @param {Object} scene - The instance of the Scene we've created in the application + */ +Scene.link = function(actors, scene) { + director.cast(actors, scene); +}; + + +/** + * Disconnects an actor/s from a scene. + * (Actors could be delinked during the animation + * however they will current their state when delinked) + * @memberOf module:enyo/AnimationSupport/Scene + * @public + * @param {Object} actors - The elements which needs to be animated + * @param {Object} scene - The instance of the Scene we've created in the application + */ +Scene.delink = function(actors, scene) { + director.reject(scene, actors); +}; + + +/** + * Function to construct all the scenes instantiated from the Scene + * @memberOf module:enyo/AnimationSupport/Scene + * @private + * @param {number} id - id of the scene generated when created + * @return {object} Constructed instance + */ +var sceneConstructor = function(id) { + var + _ts, _wasts, _req, + _framerate = 16.6, + /** + * Stores the id of the instance created + * @memberOf module:enyo/AnimationSupport/Scene + * @private + * @type {Array} + */ + _id = id, + + /** + * Holds refereneces of the all animations added to this scene. + * @memberOf module:enyo/AnimationSupport/Scene + * @private + * @type {Array} + */ + _poses = [], + + /** + * Holds old animation time span, useful for scenarios where same + * time span is expected to be added for the latest added animation. + * This provides the felxibility to add animation without duration. + * + * Like: scene.addAnimation({translate: '50,0,0'}); + * + * As no duration is mentioned the old animations duration is taken. + * @type {Number} + * @memberOf module:enyo/AnimationSupport/Scene + * @private + */ + _prevDur = 0; + + /** + * An exposed property to know if know the animating state of this scene. + * 'true' - the scene is asked for animation(doesn't mean animation is happening) + * 'false' - the scene is not active(has completed or its actors are not visible) + * @type {Boolean} + * @memberOf module:enyo/AnimationSupport/Scene + * @public + */ + this.animating = false; + + /** + * An exposed property to know if the scene is ready with actors performing action. + * 'true' - the scene actors are ready for action + * 'false' - some or all actors are not ready + * @type {Boolean} + * @memberOf module:enyo/AnimationSupport/Scene + * @public + */ + this.active = false; + + /** + * Holds refereneces of complete time span for this scene. + * @type {Number} + * @memberOf module:enyo/AnimationSupport/Scene + * @public + */ + this.span = 0; + + + /** + * Function used to loop in all the animations in a scene + * @memberOf module:enyo/AnimationSupport/Scene + * @private + */ + function loop() { + if (this.animating) { + _ts = utils.perfNow(); + _ts = _ts - (_wasts !== undefined ? _wasts : _ts); + _ts = (_ts > _framerate) ? _framerate : _ts; + director.take(this, _ts); + _wasts = _ts; + this.trigger(true); + } else { + _wasts = undefined; + this.cancel(); + this.completed && this.completed(); + } + } + /** + * Function used to make start the animation if it is "true" for animating. + * @memberOf module:enyo/AnimationSupport/Scene + * @public + */ + this.ready = function() { + if (this.animating) { + if (!this.active) { + director.roll(this); + } + return this.active; + } + return false; + }; + + /** + * Cancel the animation + * @memberOf module:enyo/AnimationSupport/Scene + * @public + */ + this.cancel = function() { + animation.cancelRequestAnimationFrame(_req); + }; + + + /** + * Triggers the Request Animation Frame + * @param {boolean} force - A boolean value for letting the rAF start. + * @memberOf module:enyo/AnimationSupport/Scene + * @public + */ + this.trigger = function(force) { + if (force || !this.animating) { + _req = animation.requestAnimationFrame(utils.bindSafely(this, loop)); + } + }; + + + /** + * Gets the unique ID assigned to this sceen. + * @return {number} - id + * @memberOf module:enyo/AnimationSupport/Scene + * @public + */ + this.getID = function() { + return _id; + }; + + + /** + * Returns the life span/duration of this sceen. + * @return {number} life span/duration of this sceen + * @memberOf module:enyo/AnimationSupport/Scene + * @public + */ + this.totalSpan = function() { + return this.span; + }; + + /** + * Adds new animation on already existing animation for this character. + * @memberOf module:enyo/AnimationSupport/Scene + * @public + */ + this.addAnimation = function(newProp, span, delay) { + if (_prevDur === 0 && span === 0) { + var index = 0; + if (delay) { + _prevDur = delay; + this.span += _prevDur; + _poses[index++] = { + span: this.span + }; + } + _prevDur = 0; + _poses[index] = { + animate: newProp, + span: this.span + }; + } else { + if (delay) { + _prevDur = delay; + this.span += _prevDur; + _poses.push({ + span: this.span + }); + } + _prevDur = span || _prevDur; + this.span += _prevDur; + _poses.push({ + animate: newProp, + span: this.span + }); + } + }; + /** + * Function which returns the length of the poses. + * @memberOf module:enyo/AnimationSupport/Scene + * @public + * @return {number} - length of the poses + */ + this.length = function() { + return _poses.length; + }; + /** + * Returns animation pose index for a particular + * instance of time from the list of + * animations added to the scene. + * @param {number} span - Time span from the animation timeline + * @return {number} - index of the animation + * @memberOf module:enyo/AnimationSupport/Scene + * @public + */ + this.animateAtTime = function(span) { + var startIndex = 0, + stopIndex = _poses.length - 1, + middle = Math.floor((stopIndex + startIndex) / 2); + + if (span === 0) { + return startIndex; + } + + while (_poses[middle].span != span && startIndex < stopIndex) { + if (span < _poses[middle].span) { + stopIndex = middle; + } else if (span > _poses[middle].span) { + startIndex = middle + 1; + } + + middle = Math.floor((stopIndex + startIndex) / 2); + } + return (_poses[middle].span != span) ? startIndex : middle; + }; + + /** + * Clears/removes the animation + * @memberOf module:enyo/AnimationSupport/Scene + * @public + */ + this.clearAnimation = function() { + for (var i = 0; i < _poses.length; i++) { + _poses[i]._startAnim = undefined; + } + }; + + /** + * Returns animation pose based on index from the list of + * animations added to this scene. + * @param {number} index - animation's index from the list of animations + * @return {Object} pose of the animation based on the index in the list + * @memberOf module:enyo/AnimationSupport/Scene + * @public + */ + this.getAnimation = function(index) { + return index < 0 || _poses[index]; + }; + + /** + * Sets the newly added animation to the poses + * @param {Numner} index - index to which the new animation should set + * @param {Object} pose - newly added animation + * @memberOf module:enyo/AnimationSupport/Scene + * @public + */ + this.setAnimation = function(index, pose) { + _poses[index] = pose; + }; + + + //TODO: Move these events to Event Delegator + /** + * Event to identify when the scene has done animating. + * @memberOf module:enyo/AnimationSupport/Scene + * @public + */ + this.completed = function() {}; + + /** + * Event to identify when the scene has done a step(rAF updatation of time) in the animation. + * @memberOf module:enyo/AnimationSupport/Scene + * @public + */ + this.step = function() {}; + + /** + * Event to identify when the actor has done animating. + * @param {Object} actor - animating element + * @memberOf module:enyo/AnimationSupport/Scene + * @public + */ + this.actorCompleted = function(actor) {}; +}; + +/** + * SceneAction exposes the api which performs the action on the animation in a given scene + * @type {Object} + * @memberOf module:enyo/AnimationSupport/Scene + * @private + */ +var SceneAction = { + /** + * This function initiates action on the animation + * from the list of animations for a given scene. + * @param {number} ts - timespan + * @param {Object} pose - pose from the animation list + * @return {Object} - pose + * @memberOf module:enyo/AnimationSupport/Scene + * @private + */ + action: function(ts, pose) { + var past, + actor, + actors,i, + tm = this.rolePlay(ts), + index = this.animateAtTime(tm); + + if (index < 0) { + return; + } + pose = this.getAnimation(index); + past = pose.animate; + + if (past instanceof sceneConstructor) { + past._frameSpeed = this._frameSpeed; + director.take(past, ts); + } else { + past = index ? this.getAnimation(index - 1).span : 0; + actors = this.rolePlays[this.getID()]; + for (i = 0; (actor = actors[i]); i++) { + if (actor.generated) { + director.action(pose, + actors[i], + tm - past, + pose.span - past); + this.step && this.step(actor); + } + } + } + return pose; + } +}; \ No newline at end of file diff --git a/src/AnimationSupport/SceneActor.js b/src/AnimationSupport/SceneActor.js new file mode 100644 index 000000000..81eb194fd --- /dev/null +++ b/src/AnimationSupport/SceneActor.js @@ -0,0 +1,65 @@ +var + Scene = require('./Scene'), + director = require('./Director'), + utils = require('../utils'); + +var + tm, actor, actors, len, dur; + +var CharacterAction = { + /** + * Overridden function initiates action on the animation + * for the given scene actor. + * @param {number} ts - timespan + * @param {Object} pose - pose from the animation list + * @return {Object} - pose + * @memberOf module:enyo/AnimationSupport/SceneActor + * @private + * @override + */ + action: function (ts, pose) { + var i, past, index; + + actors = this.rolePlays[this.getID()]; + len = actors.length; + dur = this.span; + for (i = 0; (actor = actors[i]); i++) { + //give priority to individual actor than scene. + if (!actor._frameSpeed) { + actor._frameSpeed = this._frameSpeed; + } + + if (actor.generated && actor._frameSpeed) { + tm = this.rolePlay(ts, actor); + if (isNaN(tm) || tm < 0) continue; + else if (tm <= dur) { + index = this.animateAtTime(tm); + pose = this.getAnimation(index); + past = index ? this.getAnimation(index - 1).span : 0; + director.action(pose, actor, tm - past, pose.span - past); + this.step && this.step(actor); + } else { + actor.timeline = dur; + actor._frameSpeed = 0; + this.actorCompleted && this.actorCompleted(actor); + } + } + } + return pose; + } +}; + +/** + * Scene Actor is used to individually manage all the actors

+ * The Scene Actor is similar to Scene but can receive + * an actor for playing the animation.
+ * Scene Actor's play when called without the actor, + * it works same as Scene playing all the actors.

+ * Usage - SceneActorInstance.play(actor) + * @module enyo/AnimationSupport/SceneActor + */ +module.exports = function(props) { + var scene = Scene(props); + utils.mixin(scene, CharacterAction); + return scene; +}; diff --git a/src/AnimationSupport/SceneEditor.js b/src/AnimationSupport/SceneEditor.js new file mode 100644 index 000000000..14824613a --- /dev/null +++ b/src/AnimationSupport/SceneEditor.js @@ -0,0 +1,192 @@ +require('enyo'); + +/** +* @module enyo/AnimationSupport/SceneEditor +*/ + +/** +* This modules exposes API's for controlling animations. +* @private +*/ +module.exports = { + /** + * @private + */ + timeline: 0, + /** + * @private + */ + _cachedValue: 0, + /** + * @private + */ + _frameSpeed: 0, + /** + * @private + */ + _startTime: 0, + + /** + * @private + */ + cache: function(actor) { + actor = actor || this; + if(actor._frameSpeed === 0){ + actor._frameSpeed = actor._cachedValue; + } + this.animating = true; + }, + + /** + * Starts the animation of the actor given in argument. + * If actor is not provided, animation of all the components linked to the {@link module:enyo/AnimationSupport/Scene} will be started. + * @param [Component]{@link module:enyo/Component~Component} actor The component to be animated + * @public + */ + play: function (actor) { + actor = actor || this; + actor._frameSpeed = 1; + if (isNaN(actor.timeline) || !actor.timeline) { + actor.timeline = 0; + } + this.trigger(); + this.animating = true; + }, + + /** + * Resumes the paused animation of the actor given in argument. + * If actor is not provided, animation of all the components linked to the {@link module:enyo/AnimationSupport/Scene} will be resumed. + * @param [Component]{@link module:enyo/Component~Component} actor The component to be animated + * @public + */ + resume: function(actor) { + this.cache(actor); + actor = actor || this; + actor._frameSpeed *= 1; + }, + + /** + * Pauses the animation of the actor given in argument. + * If actor is not provided, animation of all the components linked to the {@link module:enyo/AnimationSupport/Scene} will be paused. + * @param [Component]{@link module:enyo/Component~Component} actor The component to be animated + * @public + */ + pause: function (actor) { + actor = actor || this; + actor._cachedValue = actor._frameSpeed; + actor._frameSpeed = 0; + }, + + /** + * Reverses the animation of the actor given in argument. + * If actor is not provided, animation of all the components linked to the {@link module:enyo/AnimationSupport/Scene} will be reversed. + * @param [Component]{@link module:enyo/Component~Component} actor The component to be animated + * @public + */ + reverse: function (actor) { + this.cache(actor); + actor = actor || this; + actor._frameSpeed *= -1; + }, + + /** + * fast description goes here + * @param {Number} mul description goes here + * @param [Component]{@link module:enyo/Component~Component} actor description goes here + * @public + */ + fast: function (mul, actor) { + this.cache(actor); + actor = actor || this; + actor._frameSpeed *= mul; + }, + + /** + * slow description goes here + * @param {Number} mul description goes here + * @param [Component]{@link module:enyo/Component~Component} actor description goes here + * @public + */ + slow: function (mul, actor) { + this.cache(actor); + actor = actor || this; + actor._frameSpeed *= mul; + }, + + /** + * Changes the speed of the animation.
+ * Speed of the animation changed based on the factor.
+ * To slow down the speed use values between 0 and 1. For Example 0.5 to reduce the speed by 50%.
+ * To increase the speed use values above 1. For Example 2 to increase the speed by 200%.
+ * Animation will be paused if factor is 0. To pause the animation use {@link enyo/AnimationSupport/SceneEditor.pause pause} API.
+ * Speed will not be affected incase of negative multiplication factor. + * @param {Number} factor Multiplication factor which changes the speed + * @param [Component {@link module:enyo/Component~Component}] actor The component whose animating speed should be changed + * @public + */ + speed: function(mul, actor) { + if (mul < 0) return; + this.cache(actor); + actor = actor || this; + actor._frameSpeed *= mul; + }, + + /** + * Stops the animation of the actor given in argument. + * If actor is not provided, animation of all the components linked to the {@link module:enyo/AnimationSupport/Scene} will be stopped. + * @param [Component]{@link module:enyo/Component~Component} actor The component to be animated + * @public + */ + stop: function (actor) { + actor = actor || this; + actor._cachedValue = 1; + actor._frameSpeed = 0; + actor.timeline = 0; + this.animating = false; + this.cancel(); + }, + + /** + * Seeks the animation of the actor to the position provided in timeline + * The value of timeline should be between 0 to duration of the animation. + * @param {Number} timeline Value in timeline where the animation has to be seeked + * @param [Component]{@link module:enyo/Component~Component} actor The component to be animated + * @public + */ + seek: function(timeline, actor) { + actor = actor || this; + if (this.animating !== true) { + this.play(actor); + this.pause(actor); + } + actor.timeline = timeline; + }, + + /** + * rolePlay updated the timeline of the actor which is currently animating. + * @param {Number} t Elapsed time since the animation of this pose has started (ratio in factor of 1) + * @param {@link module:enyo/Component~Component} actor The component which is animating + * @return {Number} Returns the updated timeline of the actor + * @access public + */ + rolePlay: function (t, actor) { + actor = actor || this; + if (actor.timeline === undefined || actor.timeline < 0) + actor.timeline = 0; + + if(actor.delay > 0) { + actor.delay -= _rolePlay(t, actor._frameSpeed); + } else { + actor.timeline += _rolePlay(t, actor._frameSpeed); + } + return actor.timeline; + } +}; + +/** + * Returns the time based on the current speed of animation. + * @private + */ +function _rolePlay(t, mul) { + return mul * t; +} \ No newline at end of file diff --git a/src/AnimationSupport/SceneEvent.js b/src/AnimationSupport/SceneEvent.js new file mode 100644 index 000000000..0a13e1040 --- /dev/null +++ b/src/AnimationSupport/SceneEvent.js @@ -0,0 +1,275 @@ +var + Scene = require('./Scene'), + director = require('./Director'), + dispatcher = require('../dispatcher'), + emitter = require('../EventEmitter'), + utils = require('../utils'); + +var eventsMap = { + vdrag: "drag", + vscroll: "scroll", + vmousewheel: "mousewheel", + vtouch: "touchmove", + drag: "vdrag", + scroll: "vscroll", + mousewheel: "vmousewheel", + touchmove: "vtouch" +}; + + +/** +* This module handles the animation events for the character. +* If the character has opted to have animations handled by animation framework, +* then it can add "handleAnimationEvents" as true as its property. +* The character can also mention which events he wants to be handled by the framework by +* providing list of animation events in "handlers" block like; +* { +* name: "myKind", +* handlers: [ +* "scroll", +* "mousewheel", +* "touchstart", +* "touchmove", +* "touchend" +* ] +* } +* +* By default these events are handled within the framework(others for now have to be handled by the application). +*/ +var EventDelegator = { + + /** + * Attaches the evnet handlers to the character either its own events or + * else default events with the framework. As of now only these events are + * supported; + * - scroll + * - touch + * - mousewheel + * @public + */ + register: function (scene, charc) { + var events = scene.handlers || {}; + for (var key in events) { + this.addRemoveListener(scene, charc, key, events[key]); + } + }, + + /** + * Detaches the evnet handlers from the character either its own events or + * else default events from with the framework. As of now only these events are + * supported; + * - scroll + * - touch + * - mousewheel + * @public + */ + deRegister: function (scene, charc) { + var events = scene.handlers || {}; + for (var key in events) { + this.addRemoveListener(scene, charc, key, events[key], true); + } + }, + + /** + * @private + */ + addRemoveListener: function(scene, charc, name, callback, remove) { + var d = remove ? dispatcher.stopListening : dispatcher.listen, + e = eventsMap[name]; + d(charc.hasNode(), e, charc.bindSafely(this[e + 'Event'], charc)); + + var fn = remove ? emitter.off : emitter.on; + fn.apply(emitter, [name, scene[callback], charc]); + }, + + /** + * @private + */ + emitEvent: function(action, name) { + emitter.emit(name, + action.eventOriginator, + action.getAnimationDelta()); + }, + + /** + * @private + */ + touchmoveEvent: function (sender, inEvent) { + var x = inEvent.targetTouches[0].pageX, + y = inEvent.targetTouches[0].pageY; + + if(x !== 0 || y !== 0) { + inEvent.dX = x; + inEvent.dY = y; + inEvent.dZ = 0; + inEvent.vtype = eventsMap['touchmove']; + EventAction.setAnimationDelta(inEvent); + } + }, + + /** + * @private + */ + scrollEvent: function (inSender, inEvent) { + inEvent.dX = inEvent.deltaX; + inEvent.dY = inEvent.deltaY; + inEvent.dZ = 0; + inEvent.vtype = eventsMap['scroll']; + EventAction.setAnimationDelta(inEvent); + }, + + /** + * @private + */ + dragEvent: function (inSender, inEvent) { + inEvent.dX = inEvent.offsetX; + inEvent.dY = inEvent.offsetY; + inEvent.dZ = 0; + inEvent.vtype = eventsMap['drag']; + EventAction.setAnimationDelta(inEvent); + }, + + /** + * @private + */ + mousewheelEvent: function (sender, inEvent) { + inEvent.dX = inEvent.deltaX; + inEvent.dY = inEvent.deltaY; + inEvent.dZ = 0; + inEvent.vtype = eventsMap[inEvent.type]; + EventAction.setAnimationDelta(inEvent); + } +}; + + + +var sup, + + /** + * Holds references for DOM event updates to be used for + * virtual events. + * @private + */ + _eventCache = {}, + + /** + * Checks if registered DOM event is been triggered for the actors + * added to this scene. + * @private + */ + _isTriggered = false, + + /** + * Holds refereneces of the activator who has initiated a virtual + * event for the actor/s in this scene. + * @private + */ + _triggerer = ''; + +var EventAction = { + + eventOriginator: undefined, + + /** + * Sets the delta values of x, y and z for events + * @param {Object} obj - Object contains dX, dY and dZ as keys + * @memberOf module:enyo/AnimationSupport/SceneEvent + * @public + */ + setAnimationDelta: function(ev) { + _eventCache.dX = ev.dX + _eventCache.dX || 0; + _eventCache.dY = ev.dY + _eventCache.dY || 0; + _eventCache.dZ = ev.dZ + _eventCache.dZ || 0; + _eventCache[ev.vtype] = ev; + + _isTriggered = true; + _triggerer = ev.vtype; + }, + + /** + * To get event changes captured, for delta values of x, y and z. + * @return {Object} delta - pose + * @memberOf module:enyo/AnimationSupport/SceneEvent + * @public + */ + getAnimationDelta: function() { + return _eventCache; + }, + + /** + * To be when an virtual event has to be triggered for the last event captured. + * @return {void} + * @memberOf module:enyo/AnimationSupport/SceneEvent + * @public + */ + triggerEvent: function() { + _isTriggered = false; + EventDelegator.emitEvent(this, _triggerer); + }, + + /** + * To be used when an actor is registerd for event tracking. + * Its keeps track of only on actor which is refered as + * the originator. + * @param {Object} actor - Component on which events will be captured. + * @return {void} + * @memberOf module:enyo/AnimationSupport/SceneEvent + * @public + */ + register: function (actor) { + if (this.handlers) { + this.eventOriginator = actor; + EventDelegator.register(this, actor); + } + }, + + /** + * Overridden function initiates action on the animation + * for the given scene event. + * @param {number} ts - timespan + * @param {Object} pose - pose from the animation list + * @return {Object} - pose + * @memberOf module:enyo/AnimationSupport/SceneEvent + * @private + * @override + */ + action: function (ts, pose) { + if (_isTriggered && _triggerer && this.handlers && this.handlers[_triggerer] !== undefined) { + if (this.handlers[_triggerer] === "") { + ts = director.shot(this, ts); + pose = sup.call(this, ts, pose); + if(ts === 0) _isTriggered = false; + } else { + this.triggerEvent(); + } + } + return pose; + } +}; + + +/** + * Scene Event A child entity of Scene which helps + * to perform user interaction based animations.
+ * This Scene can register virtual events for actors + * to enhance performance of delegation of events.
+ * SceneEvent can hold many actors on which animation has + * to performed, however only one actor{originator} can be + * registered for event tracking.
+ * To capture events which are tracked by SceneEvent, + * handlers could be added within the scene block with there + * respective call backs.

+ * Usage - var sceneInstance = SceneEvent({ + * animation: [{ rotate: "180,0,0"}], + * handlers: {vmousewheel: ""}, + * }); + * sceneInstance.register(eventActor); + * sceneInstance.play(); + * @module enyo/AnimationSupport/SceneEvent + */ +module.exports = function(props) { + var scene = Scene(props); + sup = scene.action; + utils.mixin(scene, EventAction); + return scene; +}; \ No newline at end of file diff --git a/src/AnimationSupport/Slideable.js b/src/AnimationSupport/Slideable.js deleted file mode 100644 index 77284ba94..000000000 --- a/src/AnimationSupport/Slideable.js +++ /dev/null @@ -1,98 +0,0 @@ -var - kind = require('../kind'), - animation = require('./Core'); - -/** - * Interface to achieve slide animation - * - * @module enyo/AnimationSupport/Slideable - * @public - */ -module.exports = { - - /** - * @private - */ - name: 'Slideable', - - /** - * To start animation - */ - animate: true, - - /** - * @public - * slide animation in left direction - * @parameter: slideDistance - distance in pixels to slide in left direction - */ - left: function(slideDistance) { - this.slide((-1 * slideDistance), 0, 0); - }, - - /** - * @public - * slide animation in right direction - * @parameter: slideDistance - distance in pixels to slide in right direction - */ - right: function(slideDistance) { - this.slide(slideDistance, 0, 0); - }, - - /** - * @public - * slide animation upward - * @parameter: slideDistance - distance in pixels to slide upward - */ - up: function(slideDistance) { - this.slide(0, (-1 * slideDistance), 0); - }, - - /** - * @public - * slide animation downward - * @parameter: slideDistance - distance in pixels to slide downward - */ - down: function(slideDistance) { - this.slide(0, slideDistance, 0); - }, - - /** - * @public - * slide animation in custom direction - * @parameter: x - css property to slide in x-axis direction - * @parameter: y - css property to slide in y-axis direction - * @parameter: z - css property to slide in z-axis direction - */ - slide: function(x, y, z) { - x = x || 0; - y = y || 0; - z = z || 0; - switch (this.direction) { - case "horizontal": - this.addAnimation({ - translate: x + "," + 0 + "," + 0 - }); - break; - case "vertical": - this.addAnimation({ - translate: 0 + "," + y + "," + 0 - }); - break; - case "depth": - this.addAnimation({ - translate: 0 + "," + 0 + "," + x - }); - break; - case "depthForward": - this.addAnimation({ - translate: x + "," + 0 + "," + -0.009 * x - }); - break; - - default: - this.addAnimation({ - translate: x + "," + y + "," + z - }); - } - } -}; diff --git a/src/AnimationSupport/Tween.js b/src/AnimationSupport/Tween.js index cf419dce3..d4319771f 100644 --- a/src/AnimationSupport/Tween.js +++ b/src/AnimationSupport/Tween.js @@ -2,12 +2,12 @@ require('enyo'); var frame = require('./Frame'), - easings = require('./Easings'), - matrixUtil = require('./Matrix'), + Easings = require('./Easings'), Vector = require('./Vector'), + Matrix = require('./Matrix'), utils = require('../utils'); -var oldState, newState, node, matrix, cState = []; +var fn, state, ease, points, path, oldState, newState, node, matrix, cState = []; /** * Tween is a module responsible for creating intermediate frames for an animation. * The responsibilities of this module is to; @@ -17,128 +17,286 @@ var oldState, newState, node, matrix, cState = []; * @module enyo/AnimationSupport/Tween */ module.exports = { - + /** * @private */ - step: function(charc, pose, t, d) { - var k, c, pts, tState, oState, ease, points; + init: function (actor, pose, initial) { + if (!(actor && pose && pose.animate)) return; + node = actor.hasNode(); + utils.mixin(pose, frame.getComputedProperty(node, pose.animate, initial || actor.currentState)); + actor.currentState = pose.currentState; + return pose; + }, - node = charc.node; - newState = pose._endAnim; - ease = pose.animate && pose.animate.ease ? pose.animate.ease: this.ease; - oldState = pose._startAnim; - charc.currentState = charc.currentState || {}; + /** + * Step represents state of the actor at any point of time in the animation. + * @param {Object} actor - Element to be animated + * @param {Object} pose - Current behavior in the animation (at a given time) + * @param {Number} t - Fraction which represents the animation (between 0 to 1) + * @param {Number} d - Duration of the current pose + * @memberOf module:enyo/AnimationSupport/Tween + * @private + */ + step: function(actor, pose, t, d) { + if (!(actor && pose && pose.animate)) return; + if (t<0) t=0; + if (t>1) t=1; + var k; + node = actor.hasNode(); + state = actor.currentState = actor.currentState || pose.currentState || {}; + points = pose.controlPoints = pose.controlPoints || {}; + ease = pose.animate && pose.animate.ease ? pose.animate.ease : this.ease; + path = pose.animate && pose.animate.path; - if (pose.props) { + if (pose.props) { for (k in pose.props) { - cState = frame.copy(charc.currentState[k] || []); - if (newState[k]) { - if (ease && (typeof ease !== 'function')) { - var checkEaseChange = easings.easeChanged(ease); - var propChange = easings.propChange(k); + if (!pose._endAnim[k]) { + continue; + } - if (!charc.controlPoints || (propChange === true || checkEaseChange === true)) { - // for the first time or either of Ease/Propery changed - charc.controlPoints = easings.calculateEase(ease, frame.copy(oldState[k]), frame.copy(newState[k])); - } else if (propChange === false && checkEaseChange === false) { - // for the cases where property and ease remain same and the states are varying - var oldStateCheck = easings.oldStateChange(frame.copy(oldState[k])); - var newStateCheck = easings.newStateChange(frame.copy(newState[k])); - if (oldStateCheck === true || newStateCheck === true) { - charc.controlPoints = easings.calculateEase(ease, frame.copy(oldState[k]), frame.copy(newState[k])); - } - } - cState = this.getBezier(t, charc.controlPoints, cState); - if (k == 'rotate') - cState = Vector.toQuant(cState); + cState = utils.clone(state[k] || []); + newState = utils.clone(pose._endAnim[k]); + oldState = utils.clone(pose._startAnim[k]); + + if (ease && (typeof ease !== 'function')) { + if (k == 'rotate') { + newState = Vector.toQuant(newState); + points[k] = points[k] || + this.bezierSPoints(ease, oldState, newState, pose.props[k], points[k]); + fn = this.bezierSpline; + } else { + points[k] = points[k] || + this.bezierPoints(ease, oldState, newState, points[k]); + fn = this.bezier; + } + cState = fn.call(this, t, points[k], cState); + } else { + if (k == 'rotate') { + newState = Vector.toQuant(newState); + fn = this.slerp; } else { - if (k == 'rotate') { - tState = Vector.toQuant(newState[k]); - oState = oldState[k]; - c = this.slerp; - } else { - tState = newState[k]; - oState = oldState[k]; - c = this.lerp; - } - cState = c(oState, tState, ease(t, d), cState); + fn = this.lerp; } + cState = fn.call(this, oldState, newState, ease(t, d), cState); } - + if (!frame.isTransform(k)) { - frame.setProperty(node, k, cState); + + frame.setProperty(actor, k, cState); } - charc.currentState[k] = cState; + state[k] = cState; } } else { - utils.mixin(charc.currentState,oldState); + utils.mixin(state, oldState); } - if (charc.path) { - points = this.getBezier(t, charc.path, charc.currentState.translate, true); - charc.currentState.translate = points; + + //TODO: Support for properties other than translate + if (path) { + this.traversePath(t, path, state.translate); } + matrix = frame.recomposeMatrix( - charc.currentState.translate, - charc.currentState.rotate, - charc.currentState.scale, - charc.currentState.skew, - charc.currentState.perspective + state.translate, + state.rotate, + state.scale, + state.skew, + state.perspective ); - frame.accelerate(node, matrix); - - charc.animationStep && charc.animationStep(t,matrix); + frame.accelerate(actor, matrix); + state.matrix = matrix; + pose.currentState = state; }, /** + * Overridden function for applying the default ease. + * @param {Number} t - Fraction which represents the animation (between 0 to 1) + * @return {Number} t + * @memberOf module:enyo/AnimationSupport/Tween * @private + * @override */ ease: function(t) { return t; }, - //Without control points - beizerSlerpPoints: function(ease, startQuat, endQuat, endPoint) { - var tm, ag, q, key, - splinePoints = {}, - eD = frame.parseValue(endPoint), - aN = startQuat; + /** + * Draws linear interpolation between two values. + * @param {Number[]} vA - origin vector + * @param {Number[]} vB - Destination vector + * @param {Number} t - Fraction which represents the animation (between 0 to 1) + * @param {Number[]} vR - Resultant vector + * @return {Number[]} vR + * @memberOf module:enyo/AnimationSupport/Tween + * @private + */ + lerp: function(vA, vB, t, vR) { + if (!vA) return; + if (!vR) vR = []; + var i, l = vA.length; - if (ease && Object.keys(ease).length > 0) { - for (key in ease) { - tm = parseFloat(key) / 100; - ag = parseFloat(ease[key]); - eD.pop(); // remove angle from end point. - eD[eD.length] = ag; - q = Vector.toQuant(frame.copy(eD)); - splinePoints[tm] = [aN, q]; - aN = q; + for (i = 0; i < l; i++) { + vR[i] = (1 - t) * vA[i] + t * vB[i]; + } + return vR; + }, + + /** + * Draws sperical linear interpolation between two values. + * @param {Number[]} qA Quaternion origin + * @param {Number[]} qB - Quaternion destination + * @param {Number} t - Fraction which represents the animation (between 0 to 1) + * @param {Number[]} qR - Resultant quaternion + * @return {Number[]} qR + * @memberOf module:enyo/AnimationSupport/Tween + * @private + */ + slerp: function(qA, qB, t, qR) { + if (!qA) return; + if (!qR) qR = []; + var a, + b, + theta, + dot = Vector.quantDot(qA, qB), + l = qA.length; + + dot = Math.min(Math.max(dot, -1.0), 1.0); + if (dot == 1.0) { + qR = frame.copy(qA); + return qR; + } + theta = Math.acos(dot); + for (var i = 0; i < l; i++) { + a = (Math.sin((1 - t) * theta) / Math.sin(theta)) * qA[i]; + b = (Math.sin(t * theta) / Math.sin(theta)) * qB[i]; + qR[i] = a + b; + } + return qR; + }, + + /** + * Creates bezier curve path for animation. + * @param {Number} t - Fraction which represents the animation (between 0 to 1) + * @param {Number[]} points - knot and control points + * @param {Number[]} vR - Resulting points + * @return {Number[]} vR + * @memberOf module:enyo/AnimationSupport/Tween + */ + bezier: function(t, points, vR) { + if (!points) return; + if (!vR) vR = []; + + var i, j, + c = points.length, + l = points[0].length, + lastIndex = (c - 1), + startPoint = points[0], + endPoint = points[lastIndex], + values = Easings.getBezierValues(t, lastIndex); + + for (i = 0; i < l; i++) { + vR[i] = 0; + for (j = 0; j < c; j++) { + if ((j > 0) && (j < (c - 1))) { + vR[i] = vR[i] + ((startPoint[i] + (points[j][i] * (endPoint[i] - startPoint[i]))) * values[j]); + } else { + vR[i] = vR[i] + (points[j][i] * values[j]); + } } - splinePoints[1] = [aN, endQuat]; } - return splinePoints; + return vR; }, - //Without control points - beizerSlerp: function(t, points, vR) { - var p, key; - for (p in points) { - if (p >= t) key = p; + /** + * Returns the control points for bezier curve. + * @param {Object} easeObj- The easing object with values. + * @param {Number[]} startPoint - Starting point of the curve + * @param {Number[]} endPoint - End point of the curve + * @param {Number[]} points - control points + * @return {Number[]} points + * @memberOf module:enyo/AnimationSupport/Tween + */ + bezierPoints: function(easeObj, startPoint, endPoint, points) { + if (!easeObj) return; + var order = (easeObj && Object.keys(easeObj).length) ? (Object.keys(easeObj).length + 1) : 0; + var bValues = [], + m1 = [], + m2 = [], + m3 = [], + m4 = [], + l = 0; + points = [startPoint]; + + var t, a; + for (var key in easeObj) { + t = parseFloat(key) / 100; + a = parseFloat(easeObj[key]) / 100; + bValues = Easings.getBezierValues(t, order); + bValues.shift(); + m1.push(a - bValues.pop()); + m2.push(bValues); + } + + m3 = Matrix.inverseN(m2, bValues.length); + m4 = Matrix.multiplyN(m3, m1); + l = m4.length; + for (var i = 0; i < l; i++) { + points.push([m4[i], m4[i], m4[i]]); + } + + points.push(endPoint); + return points; + }, + + /** + * Traverses the path of the animation + * @param {Number} t - Fraction which represents the animation (between 0 to 1) + * @param {Number[]} path - Array of points + * @param {Number[]} vR Resulatant Array + * @return {Number[]} vR + * @memberOf module:enyo/AnimationSupport/Tween + */ + traversePath: function (t, path, vR) { + if (!path) return; + if (!vR) vR = []; + + var i, j, + c = path.length, + l = path[0].length, + lastIndex = (c - 1), + values = Easings.getBezierValues(t, lastIndex); + + for (i = 0; i < l; i++) { + vR[i] = 0; + for (j = 0; j < c; j++) { + vR[i] = vR[i] + (path[j][i] * values[j]); + } } - vR = this.slerp(points[key][0], points[key][1], t); return vR; }, - //With control points - beizerSPoints: function(ease, startQuat, endQuat, endPoint) { - var splinePoints = {}, - time = [0], + /** + * Returns the control points for bezier spline. + * @param {Object} ease- The easing object with values. + * @param {Number[]} startQuat - Quaternion origin + * @param {Number[]} endQuat - Quaternion destination + * @param {Number[]} endPoint - Final Destination point + * @param {Number[]} splinePoints - spline control points + * @return {Number[]} splinePoints + * @memberOf module:enyo/AnimationSupport/Tween + */ + bezierSPoints: function(ease, startQuat, endQuat, endPoint, splinePoints) { + if (!ease) return; + var time = [0], quats = [startQuat]; var t, a, q, n, _a, aI, bN, eD = frame.parseValue(endPoint); - if (ease && Object.keys(ease).length > 0) { + splinePoints = splinePoints || {}; + quats.push(startQuat); + time.push(0); + if (Object.keys(ease).length > 0) { for (var key in ease) { t = parseFloat(key) / 100; a = parseFloat(ease[key]); @@ -150,8 +308,9 @@ module.exports = { } quats.push(endQuat); time.push(1); - n = quats.length - 1; + ai = this.slerp(startQuat, endQuat, 0); + splinePoints[0] = [quats[0], aI, aI, quats[1]]; for (var i = 0, j = 1; i < n; i++, j++) { if (i === 0) { aI = this.slerp(quats[0], this.slerp(quats[2], quats[1], 2.0), 1.0 / 3); @@ -171,14 +330,24 @@ module.exports = { return splinePoints; }, - //With control points - beizerSpline: function(t, points, vR) { + /** + * Creates bezier spline path for animation. + * @param {Number} t - Fraction which represents the animation (between 0 to 1) + * @param {Number[]} points - knot and control points + * @param {Number[]} vR - Resulting points + * @return {Number[]} vR + * @memberOf module:enyo/AnimationSupport/Tween + */ + bezierSpline: function(t, points, vR) { + if (!points) return; if (!vR) vR = []; - var Q0, Q1, Q2, R0, R1; - - var p, key, pts; + var Q0, Q1, Q2, R0, R1, + p, key, pts; for (p in points) { - if (p >= t) key = p; + if (p >= t) { + key = p; + break; + } } pts = points[key]; @@ -192,75 +361,5 @@ module.exports = { } else vR = this.slerp(pts[0], pts[1], t); return vR; - }, - - lerp: function(vA, vB, t, vR) { - if (!vR) vR = []; - var i, l = vA.length; - - for (i = 0; i < l; i++) { - vR[i] = (1 - t) * vA[i] + t * vB[i]; - } - return vR; - }, - - slerp: function(qA, qB, t, qR) { - if (!qR) qR = []; - var a, - b, - theta, - dot = Vector.quantDot(qA, qB), - l = qA.length; - - dot = Math.min(Math.max(dot, -1.0), 1.0); - - if (dot == 1.0) { - qR = frame.copy(qA); - return qR; - } - - theta = Math.acos(dot); - for (var i = 0; i < l; i++) { - a = (Math.sin((1 - t) * theta) / Math.sin(theta)) * qA[i]; - b = (Math.sin(t * theta) / Math.sin(theta)) * qB[i]; - qR[i] = a + b; - } - - return qR; - }, - - /** - * @public - * @params t: time, points: knot and control points, vR: resulting point - */ - getBezier: function(t, points, vR, isPath) { - - if (!vR) vR = []; - - var i, j, - c = points.length, - l = points[0].length, - lastIndex = (c - 1), - startPoint = points[0], - endPoint = points[lastIndex], - values = easings.getBezierValues(t, lastIndex); - - for (i = 0; i < l; i++) { - vR[i] = 0; - for (j = 0; j < c; j++) { - if(isPath){ - vR[i] = vR[i] + (points[j][i] * values[j]); - } - else { - if((j > 0) && (j < (c - 1))){ - vR[i] = vR[i] + ((startPoint[i] + (points[j][i] * (endPoint[i] - startPoint[i]))) * values[j]); - } else { - vR[i] = vR[i] + (points[j][i] * values[j]); - } - } - } - } - return vR; } - -}; +}; \ No newline at end of file diff --git a/src/AnimationSupport/Vector.js b/src/AnimationSupport/Vector.js index 7d46b85b6..9f0db08e3 100644 --- a/src/AnimationSupport/Vector.js +++ b/src/AnimationSupport/Vector.js @@ -9,6 +9,10 @@ require('enyo'); module.exports = { /** * Divides vector with a scalar value. + * @param {Number[]} v - vector + * @param {Number} s - scalar value to divide + * @return {Number[]} resultant vector + * @memberOf module:enyo/AnimationSupport/Vector * @public */ divide: function (v, s) { @@ -17,6 +21,10 @@ module.exports = { /** * Add vector/quant with a vector/quant. + * @param {Number[]} q1 - vector/quant + * @param {Number[]} q2 - vector/quant + * @return {Number[]} added vector/quant + * @memberOf module:enyo/AnimationSupport/Vector * @public */ add: function (q1, q2) { @@ -30,6 +38,10 @@ module.exports = { /** * Sub vector/quant with a vector/quant. + * @param {Number[]} q1 - vector/quant + * @param {Number[]} q2 - vector/quant + * @return {Number[]} subracted vector/quant + * @memberOf module:enyo/AnimationSupport/Vector * @public */ subtract: function (q1, q2) { @@ -43,6 +55,10 @@ module.exports = { /** * Multiply vector/quant with a vector/quant. + * @param {Number[]} v - vector + * @param {Number} s - scalar value to divide + * @return {Number[]} resultant vector + * @memberOf module:enyo/AnimationSupport/Vector * @public */ multiply: function (q, s) { @@ -55,6 +71,11 @@ module.exports = { /** * Limits the vector/quant between a maximum and minimum value. + * @param {Number[]} q - vector/quant + * @param {Number} max - maximum range value + * @param {Number} min - minimum range value + * @return {Number[]} resultant vector/quant + * @memberOf module:enyo/AnimationSupport/Vector * @public */ range: function (q, max, min) { @@ -63,42 +84,14 @@ module.exports = { } }, - /** - * Compares each vector/qunat with a scalar value to check if its equal. - * Returns true when all the vector/quant values are equal to this scalar value. - * @public - */ - equalS: function (q1, s) { - return (q1.length > 0) && q1.every(function (e, i) { - return e === (s || 0); - }); - }, - - /** - * Compares each vector/qunat with a scalar value to check if its greater. - * Returns true when all the vector/quant values are greater or equal to this scalar value. - * @public - */ - greaterS: function (q1, s) { - return (q1.length > 0) && q1.every(function (e, i) { - return e >= (s || 0); - }); - }, - - /** - * Compares each vector/qunat with a scalar value to check if its lesser. - * Returns true when all the vector/quant values are lesser or equal to this scalar value. - * @public - */ - lesserS: function (q1, s) { - return (q1.length > 0) && q1.every(function (e, i) { - return e < (s || 0); - }); - }, - /** * Evaluates the gap between two vector values. * Returns the absolute distance between two vectors. + * @param {Number[]} v1 - vector + * @param {Number[]} v2 - vector + * @param {Number} d - distance between vector + * @return {Number} distance between vector + * @memberOf module:enyo/AnimationSupport/Vector * @public */ distance: function (v1, v2, d) { @@ -110,6 +103,11 @@ module.exports = { /** * Evaluates the gap between two quanterions values * Returns the absolute distance between two quanterions. + * @param {Number[]} v1 - quant + * @param {Number[]} v2 - quant + * @param {Number} d - distance between quant + * @return {Number} distance between quant + * @memberOf module:enyo/AnimationSupport/Vector * @public */ quantDistance: function (q1, q2, d) { @@ -121,6 +119,10 @@ module.exports = { /** * Gives the direction of motion from one vector to other. * Returns true if moving towards positive direction. + * @param {Number[]} v1 - quant + * @param {Number[]} v2 - quant + * @return {boolean} true if positive, false otherwise. + * @memberOf module:enyo/AnimationSupport/Vector * @public */ direction: function (q1, q2) { @@ -129,6 +131,9 @@ module.exports = { /** * Retunns an inverse of a quanterion. + * @param {Number[]} q - quant + * @return {Number[]} resultant quant + * @memberOf module:enyo/AnimationSupport/Vector * @public */ quantInverse: function (q) { @@ -137,31 +142,34 @@ module.exports = { }, /** - * Length of 3D vectors - * @public - */ - sumS: function (q, s) { - return q[0] * s + q[1] * s + q[2] * s + q[3] !== undefined ? q[3] * s : 0; - }, - - /** - * Length of 3D vectors + * Length of a vector + * @param {Number[]} v - vetor + * @return {Number} resultant length + * @memberOf module:enyo/AnimationSupport/Vector * @public */ - len: function (q) { - return Math.sqrt(q[0] * q[0] + q[1] * q[1] + q[2] * q[2]); + len: function (v) { + return Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); }, /** * Dot product of 3D vectors + * @param {Number[]} v1 - vetor + * @param {Number[]} v2 - vetor + * @return {Number} resultant dot product + * @memberOf module:enyo/AnimationSupport/Vector * @public */ - dot: function (q1, q2) { - return (q1[0] * q2[0]) + (q1[1] * q2[1]) + (q1[2] * q2[2]) + (q1[3] !== undefined && q2[3] !== undefined ? (q1[3] * q2[3]) : 0); + dot: function (v1, v2) { + return (v1[0] * v2[0]) + (v1[1] * v2[1]) + (v1[2] * v2[2]) + (v1[3] !== undefined && v2[3] !== undefined ? (v1[3] * v2[3]) : 0); }, /** - * Dot product of 3D vectors + * Dot product of 3D quanterion + * @param {Number[]} q1 - quanterion + * @param {Number[]} q2 - quanterion + * @return {Number} resultant dot product + * @memberOf module:enyo/AnimationSupport/Vector * @public */ quantDot: function (q1, q2) { @@ -169,7 +177,11 @@ module.exports = { }, /** - * Quant Dot product of 3D vectors + * Quant Cross product of 3D quanterion + * @param {Number[]} q1 - quanterion + * @param {Number[]} q2 - quanterion + * @return {Number[]} resultant cross product + * @memberOf module:enyo/AnimationSupport/Vector * @public */ quantCross: function (q1, q2) { @@ -183,19 +195,26 @@ module.exports = { /** * Cross product of two vectors + * @param {Number[]} v1 - vetor + * @param {Number[]} v2 - vetor + * @return {Number[]} resultant cross product + * @memberOf module:enyo/AnimationSupport/Vector * @public */ - cross: function (q1, q2) { + cross: function (v1, v2) { return [ - q1[1] * q2[2] - q1[2] * q2[1], - q1[2] * q2[0] - q1[0] * q2[2], - q1[0] * q2[1] - q1[1] * q2[0] + v1[1] * v2[2] - v1[2] * v2[1], + v1[2] * v2[0] - v1[0] * v2[2], + v1[0] * v2[1] - v1[1] * v2[0] ]; }, /** * Normalizing a vector is obtaining another unit vector in the same direction. * To normalize a vector, divide the vector by its magnitude. + * @param {Number[]} q1 - quanterion + * @return {Number[]} resultant quanterion + * @memberOf module:enyo/AnimationSupport/Vector * @public */ normalize: function (q) { @@ -205,6 +224,12 @@ module.exports = { /** * Combine scalar values with two vectors. * Required during parsing scaler values matrix. + * @param {Number[]} a - first vector + * @param {Number[]} b - second vector + * @param {Number[]} ascl - first vector scalar + * @param {Number[]} ascl - second vector scalar + * @return {Number[]} resultant vector + * @memberOf module:enyo/AnimationSupport/Vector * @public */ combine: function (a, b, ascl, bscl) { @@ -215,6 +240,9 @@ module.exports = { /** * Converts a quaternion vector to a rotation vector. + * @param {Number[]} rv - quanterion + * @return {Number[]} resultant rotation vector + * @memberOf module:enyo/AnimationSupport/Vector * @public */ toVector: function (rv) { @@ -226,6 +254,9 @@ module.exports = { /** * Converts a rotation vector to a quaternion vector. + * @param {Number[]} v - vector + * @return {Number[]} resultant quaternion + * @memberOf module:enyo/AnimationSupport/Vector * @public */ toQuant: function (v) { @@ -248,6 +279,13 @@ module.exports = { return q; }, + /** + * Converts a quaternion vector vector to a rotation vector. + * @param {Number[]} q - quaternion + * @return {Number[]} resultant vector + * @memberOf module:enyo/AnimationSupport/Vector + * @public + */ quantToVector: function (q) { var vector = [], h, a, b, x2 = q[0] * q[0], diff --git a/src/UiComponent.js b/src/UiComponent.js index 011ee7138..29d9e2c03 100644 --- a/src/UiComponent.js +++ b/src/UiComponent.js @@ -8,9 +8,7 @@ require('enyo'); var kind = require('./kind'), utils = require('./utils'), - master = require('./master'), - AnimationSupport = require('./AnimationSupport/AnimationSupport'), - AnimationInterfaceSupport = require('./AnimationSupport/AnimationInterfaceSupport'); + master = require('./master'); var Component = require('./Component'); @@ -130,12 +128,7 @@ var UiComponent = module.exports = kind( onresize: 'handleResize' }, - /** - * Adding animation support for controls - * @private - */ - mixins: [AnimationSupport, AnimationInterfaceSupport], - + /** * When set, provides a [control]{@link module:enyo/Control~Control} reference used to indicate where a * newly-created [component]{@link module:enyo/Component~Component} should be added in the diff --git a/test/tests/NewTweenTest.js b/test/tests/NewTweenTest.js new file mode 100644 index 000000000..157ec587f --- /dev/null +++ b/test/tests/NewTweenTest.js @@ -0,0 +1,380 @@ +var expect = chai.expect; + +var + kind = require('enyo/kind'), + Control = require('enyo/Control'), + Scene = require('enyo/AnimationSupport/Scene'), + Vector = require('enyo/AnimationSupport/Vector'), + Tween = require('enyo/AnimationSupport/Tween'); + +var b = 0, + c = 1; + +var parseMatrix = function (v) { + var m = [1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]; + v = v.replace(/^\w*\(/, '').replace(')', ''); + v = parseValue(v); + if (v.length <= 6) { + m[0] = v[0]; + m[1] = v[1]; + m[4] = v[2]; + m[5] = v[3]; + m[12] = v[4]; + m[13] = v[5]; + } else { + m = v; + } + return m; +}; + +var parseValue = function (val) { + return val.toString().split(",").map(function(v) { + return parseFloat(v, 10); + }); +}; + +var translateScene = Scene({ + animation: { + translate: "100, 0, 0" + }, + duration: 100 +}); + +var rotateScene = Scene({ + animation: { + rotate: "100, 0, 0" + }, + duration: 100 +}); + +var traverseScene = Scene({ + animation: { + path: [[0,0,0],[0,-50,0],[50,-50,0],[50,50,0],[0,50,0]] + }, + duration: 100 +}); + + +describe("Tween", function() { + var TestControl, testControl; + before(function () { + TestControl = kind({ + name: 'TestControl', + kind: Control, + components: [{ + name: "childDiv", + }] + }); + + testControl = new TestControl({parentNode: document.body}); + + }); + + after(function () { + testControl.destroy(); + TestControl = null; + }); + + describe("Tween.init", function() { + + it("should return undefined when called without any parameters", function() { + var scene = Tween.init(); + expect(scene).to.equal(undefined); + }); + + it("should return undefined when pose have no animation property", function() { + var scene = Tween.init(1, 1); + expect(scene).to.equal(undefined); + }); + + it("should return object with keys include '_startAnim' and '_endAnim'", function() { + testControl.render(); + Scene.link(testControl.$.childDiv, translateScene); + var pose = translateScene.getAnimation(0); + pose = Tween.init(testControl.$.childDiv, pose); + expect(pose).to.have.any.keys('_startAnim', '_endAnim'); + }); + }); + + describe("Tween.step", function() { + + it("should return undefined when called without any parameters", function() { + var scene = Tween.step(); + expect(scene).to.equal(undefined); + }); + + it("should return undefined when pose have no animation property", function() { + var scene = Tween.step(1, 1); + expect(scene).to.equal(undefined); + }); + + it("DOM should be at initial position at t = -0.1", function() { + testControl.render(); + Scene.link(testControl.$.childDiv, translateScene); + var pose = translateScene.getAnimation(0); + pose = Tween.init(testControl.$.childDiv, pose); + Tween.step(testControl.$.childDiv, pose, -0.1, 100); + var m = parseMatrix(testControl.$.childDiv.node.style.transform); + expect(m).to.deep.equal([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]); + }); + + it("DOM should be at initial position at t = 0", function() { + testControl.render(); + Scene.link(testControl.$.childDiv, translateScene); + var pose = translateScene.getAnimation(0); + pose = Tween.init(testControl.$.childDiv, pose); + Tween.step(testControl.$.childDiv, pose, 0, 100); + var m = parseMatrix(testControl.$.childDiv.node.style.transform); + expect(m).to.deep.equal([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]); + }); + + it("DOM should not be at initial position at t = 0.1", function() { + testControl.render(); + Scene.link(testControl.$.childDiv, translateScene); + var pose = translateScene.getAnimation(0); + pose = Tween.init(testControl.$.childDiv, pose); + Tween.step(testControl.$.childDiv, pose, 0.1, 100); + var m = parseMatrix(testControl.$.childDiv.node.style.transform); + expect(m).to.not.deep.equal([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]); + }); + + it("DOM should not be at final position at t = 0.9", function() { + testControl.render(); + Scene.link(testControl.$.childDiv, translateScene); + var pose = translateScene.getAnimation(0); + pose = Tween.init(testControl.$.childDiv, pose); + Tween.step(testControl.$.childDiv, pose, 0.9, 100); + var m = parseMatrix(testControl.$.childDiv.node.style.transform); + expect(m).to.not.deep.equal([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 100, 0, 0, 1]); + }); + + it("DOM should be at final position at t = 1", function() { + testControl.render(); + Scene.link(testControl.$.childDiv, translateScene); + var pose = translateScene.getAnimation(0); + pose = Tween.init(testControl.$.childDiv, pose); + Tween.step(testControl.$.childDiv, pose, 1, 100); + var m = parseMatrix(testControl.$.childDiv.node.style.transform); + expect(m).to.deep.equal([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 100, 0, 0, 1]); + }); + + it("DOM should be at final position at t = 1.1", function() { + testControl.render(); + Scene.link(testControl.$.childDiv, translateScene); + var pose = translateScene.getAnimation(0); + pose = Tween.init(testControl.$.childDiv, pose); + Tween.step(testControl.$.childDiv, pose, 1.1, 100); + var m = parseMatrix(testControl.$.childDiv.node.style.transform); + expect(m).to.deep.equal([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 100, 0, 0, 1]); + }); + + it("DOM should be at final position at t = 0 if d = 0", function() { + testControl.render(); + Scene.link(testControl.$.childDiv, translateScene); + var pose = translateScene.getAnimation(0); + pose = Tween.init(testControl.$.childDiv, pose); + Tween.step(testControl.$.childDiv, pose, 0, 0); + var m = parseMatrix(testControl.$.childDiv.node.style.transform); + expect(m).to.deep.equal([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 100, 0, 0, 1]); + }); + + it("DOM should be at final position at t = 1 if d = 0", function() { + testControl.render(); + Scene.link(testControl.$.childDiv, translateScene); + var pose = translateScene.getAnimation(0); + pose = Tween.init(testControl.$.childDiv, pose); + Tween.step(testControl.$.childDiv, pose, 1, 0); + var m = parseMatrix(testControl.$.childDiv.node.style.transform); + expect(m).to.deep.equal([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 100, 0, 0, 1]); + }); + }); + + describe("Tween.lerp", function() { + + it("should return undefined when called without any parameters", function() { + var m = Tween.lerp(); + expect(m).to.equal(undefined); + }); + + it("should return '_startAnim.translate' matrix when t = 0", function() { + testControl.render(); + Scene.link(testControl.$.childDiv, translateScene); + var pose = translateScene.getAnimation(0); + pose = Tween.init(testControl.$.childDiv, pose); + var m = Tween.lerp(pose._startAnim.translate, pose._endAnim.translate, 0); + expect(m).to.deep.equal(pose._startAnim.translate); + }); + + it("should return '_endAnim.translate' matrix when t = 1", function() { + testControl.render(); + Scene.link(testControl.$.childDiv, translateScene); + var pose = translateScene.getAnimation(0); + pose = Tween.init(testControl.$.childDiv, pose); + var m = Tween.lerp(pose._startAnim.translate, pose._endAnim.translate, 1); + expect(m).to.deep.equal(pose._endAnim.translate); + }); + }); + + describe("Tween.slerp", function() { + + it("should return undefined when called without any parameters", function() { + var m = Tween.slerp(); + expect(m).to.equal(undefined); + }); + + it("should return '_startAnim.rotate' matrix when t = 0", function() { + testControl.render(); + Scene.link(testControl.$.childDiv, rotateScene); + var pose = rotateScene.getAnimation(0); + pose = Tween.init(testControl.$.childDiv, pose); + var poseEndAnimToQuant = Vector.toQuant(pose._endAnim.rotate); + var m = Tween.slerp(pose._startAnim.rotate, poseEndAnimToQuant, 0); + expect(m).to.deep.equal(pose._startAnim.rotate); + }); + + it("should return '_endAnim.rotate' matrix when t = 1", function() { + testControl.render(); + Scene.link(testControl.$.childDiv, rotateScene); + var pose = rotateScene.getAnimation(0); + pose = Tween.init(testControl.$.childDiv, pose); + var poseEndAnimToQuant = Vector.toQuant(pose._endAnim.rotate); + var m = Tween.slerp(pose._startAnim.rotate, poseEndAnimToQuant, 1); + expect(m).to.deep.equal(poseEndAnimToQuant); + }); + }); + + describe("Tween.bezier", function() { + + it("should return undefined when called without any parameters", function() { + var m = Tween.bezier(); + expect(m).to.equal(undefined); + }); + + it("should return '_startAnim.translate' matrix when t = 0", function() { + testControl.render(); + Scene.link(testControl.$.childDiv, translateScene); + var pose = translateScene.getAnimation(0); + pose = Tween.init(testControl.$.childDiv, pose); + var bezierPoints = Tween.bezierPoints({20: 60, 80: 40}, pose._startAnim.translate, pose._endAnim.translate); + var m = Tween.bezier(0, bezierPoints); + expect(m).to.deep.equal(pose._startAnim.translate); + }); + + it("should return '_endAnim.translate' matrix when t = 1", function() { + testControl.render(); + Scene.link(testControl.$.childDiv, translateScene); + var pose = translateScene.getAnimation(0); + pose = Tween.init(testControl.$.childDiv, pose); + var bezierPoints = Tween.bezierPoints({20: 60, 80: 40}, pose._startAnim.translate, pose._endAnim.translate); + var m = Tween.bezier(1, bezierPoints); + expect(m).to.deep.equal(pose._endAnim.translate); + }); + }); + + describe("Tween.bezierPoints", function() { + + it("should return undefined when called without any parameters", function() { + var m = Tween.bezierPoints(); + expect(m).to.equal(undefined); + }); + + it("should return '_startAnim.translate' on array[0]", function() { + testControl.render(); + Scene.link(testControl.$.childDiv, translateScene); + var pose = translateScene.getAnimation(0); + pose = Tween.init(testControl.$.childDiv, pose); + var m = Tween.bezierPoints({20: 60, 80: 40}, pose._startAnim.translate, pose._endAnim.translate); + expect(m[0]).to.deep.equal(pose._startAnim.translate); + }); + + it("should return '_endAnim.translate' on array[3]", function() { + testControl.render(); + Scene.link(testControl.$.childDiv, translateScene); + var pose = translateScene.getAnimation(0); + pose = Tween.init(testControl.$.childDiv, pose); + var m = Tween.bezierPoints({20: 60, 80: 40}, pose._startAnim.translate, pose._endAnim.translate); + expect(m[3]).to.deep.equal(pose._endAnim.translate); + }); + }); + + describe("Tween.traversePath", function() { + + it("should return undefined when called without any parameters", function() { + var m = Tween.traversePath(); + expect(m).to.equal(undefined); + }); + + it("should return initial path array value when t = 0", function() { + testControl.render(); + /*Scene.link(testControl.$.childDiv, traverseScene); + var pose = traverseScene.getAnimation(0); + pose = Tween.init(testControl.$.childDiv, pose);*/ + var m = Tween.traversePath(0, [[0,0,0],[0,-50,0],[50,-50,0],[50,50,0],[0,50,0]]); + expect(m).to.deep.equal([0,0,0]); + }); + + it("should return final path array when t = 1", function() { + testControl.render(); + var m = Tween.traversePath(1, [[0,0,0],[0,-50,0],[50,-50,0],[50,50,0],[0,50,0]]); + expect(m).to.deep.equal([0,50,0]); + }); + }); + + describe("Tween.bezierSpline", function() { + + it("should return undefined when called without any parameters", function() { + var m = Tween.bezierSpline(); + expect(m).to.equal(undefined); + }); + + it("should return '_startAnim.rotate' matrix when t = 0", function() { + testControl.render(); + Scene.link(testControl.$.childDiv, rotateScene); + var pose = rotateScene.getAnimation(0); + pose = Tween.init(testControl.$.childDiv, pose); + var poseEndAnimToQuant = Vector.toQuant(pose._endAnim.rotate); + var bezierSPoints = Tween.bezierSPoints({20: 60, 80: 40}, pose._startAnim.rotate, poseEndAnimToQuant, "100, 0, 0"); + var m = Tween.bezierSpline(0, bezierSPoints); + expect(m).to.deep.equal(pose._startAnim.rotate); + }); + + it("should return '_endAnim.rotate' matrix when t = 1", function() { + testControl.render(); + Scene.link(testControl.$.childDiv, rotateScene); + var pose = rotateScene.getAnimation(0); + pose = Tween.init(testControl.$.childDiv, pose); + var poseEndAnimToQuant = Vector.toQuant(pose._endAnim.rotate); + var bezierSPoints = Tween.bezierSPoints({20: 60, 80: 40}, pose._startAnim.rotate, poseEndAnimToQuant, "100, 0, 0"); + var m = Tween.bezierSpline(1, bezierSPoints); + expect(m).to.deep.equal(poseEndAnimToQuant); + }); + }); + + describe("Tween.bezierSPoints", function() { + + it("should return undefined when called without any parameters", function() { + var m = Tween.bezierSPoints(); + expect(m).to.equal(undefined); + }); + + it("should return '_startAnim.rotate' on array[0][0]", function() { + testControl.render(); + Scene.link(testControl.$.childDiv, rotateScene); + var pose = rotateScene.getAnimation(0); + pose = Tween.init(testControl.$.childDiv, pose); + var poseEndAnimToQuant = Vector.toQuant(pose._endAnim.rotate); + var m = Tween.bezierSPoints({20: 60, 80: 40}, pose._startAnim.rotate, poseEndAnimToQuant, "100, 0, 0"); + expect(m[0][0]).to.deep.equal(pose._startAnim.rotate); + }); + + it("should return '_endAnim.rotate' on array[1][3]", function() { + testControl.render(); + Scene.link(testControl.$.childDiv, rotateScene); + var pose = rotateScene.getAnimation(0); + pose = Tween.init(testControl.$.childDiv, pose); + var poseEndAnimToQuant = Vector.toQuant(pose._endAnim.rotate); + var m = Tween.bezierSPoints({20: 60, 80: 40}, pose._startAnim.rotate, poseEndAnimToQuant, "100, 0, 0"); + expect(m[1][3]).to.deep.equal(poseEndAnimToQuant); + }); + }); +}); \ No newline at end of file