File: css_animation.js
/**
* @module CSSAnimation
*/
/**
* Animates CSS for a given element (constructor throws Direction error).
* @class CSSAnimation
* @constructor
* @param {Object} props properties to animate:
* @param {HTMLElement} props.targetEl the element to animate
* @param {String} props.cssProperty a CSS property, e.g. opacity, top
* @param {String} props.cssUnit a CSS 'length' unit, e.g. px
* @param {int} props.minValue the minimum of the range of values to change
* @param {int} props.maxValue the maximum of the range of values to change
* @param {String} props.direction up | down (min to max | max to min)
* @param {float} props.duration how long the animation lasts in seconds
* @param {float} props.fps frames per second
* @param {int} props.transitionType pre-defined constants: CSSAnimationLinearTransition |
* CSSAnimationEaseTransition | CSSAnimationEaseInOutTransition
* @param {float} props.lambdaPower exponent for easing: in > 1, out < 1, default 2
* @return {void}
*/
function CSSAnimation(props)
{
var opts = props || {};
// animated object properties
this.cssProperty = opts.cssProperty || "";
this.cssUnit = opts.cssUnit || "";
this.targetEl = opts.targetEl || null;
// animation properties
this.direction = opts.direction || "up";
this.duration = opts.duration || 0;
this.fps = opts.fps || 48;
this.lambdaPower = opts.lambdaPower || 2;
this.maxValue = opts.maxValue || 0;
this.minValue = opts.minValue || 0;
this.transitionType = opts.transitionType || CSSAnimation.LINEAR_TRANSITION;
// private members
this.totalFrames = this.fps * this.duration;
this.frame = 0;
this.mspf = 1000 / this.fps // ms per frame
// validate direction
switch (this.direction) {
case "down":
this.currentValue = this.maxValue;
break;
case "up":
this.currentValue = this.minValue;
break;
default:
throw "Error: direction must be 'up' or 'down'."
}
}
/**
* Transition type.
* @property LINEAR_TRANSITION
* @type {int}
* @static
* @final
*/
CSSAnimation.LINEAR_TRANSITION = 1;
/**
* Transition type.
* @property EASE_TRANSITION
* @type {int}
* @static
* @final
*/
CSSAnimation.EASE_TRANSITION = 2;
/**
* Transition type.
* @property EASE_IN_OUT_TRANSITION
* @type {int}
* @static
* @final
*/
CSSAnimation.EASE_IN_OUT_TRANSITION = 3;
/**
* Calculates the difference between each frame's animation value.
* @method _interpolate
* @protected
* @return {float}
*/
CSSAnimation.prototype._interpolate = function () {
var delta, lambda, lambdaPower;
switch (this.transitionType) {
case CSSAnimation.EASE_TRANSITION:
delta = this.maxValue - this.minValue;
lambda = Math.pow(this.frame * (1.0 / (this.totalFrames - 1.0)), this.lambdaPower);
break;
// case CSSAnimation.EASE_IN_OUT_TRANSITION:
// // ease in the first half, out the second half
// delta = (this.maxValue - this.minValue) / 2.0;
//
// if (this.frame < this.totalFrames / 2) {
// lambdaPower = this.lambdaPower;
// }
// else {
// lambdaPower = 1 / this.lambdaPower;
// }
//
// lambda = Math.pow(this.frame * (1.0 / (this.totalFrames / 2 - 1.0)), lambdaPower);
// break;
default:
delta = this.maxValue - this.minValue;
lambda = this.frame * (1.0 / (this.totalFrames - 1.0));
}
if (this.direction == "down") {
return this.maxValue - lambda * delta;
}
else {
output.innerHTML += (lambda * delta) + "\n";
return this.minValue + lambda * delta;
}
};
/**
* Performs the animation.
* @method animate
* @return {void}
*/
CSSAnimation.prototype.animate = function ()
{
var _this = this;
function innerAnimate()
{
_this.animate();
}
this.targetEl.style[this.cssProperty] = this._interpolate() + this.cssUnit;
this.frame = (this.frame + 1) % this.totalFrames;
if (this.frame + 1 < this.totalFrames) {
setTimeout(innerAnimate, this.mspf);
}
// if this is the last iteration then do the transform again but don't reiterate
if (this.frame + 1 == this.totalFrames) {
this.targetEl.style[this.cssProperty] = this._interpolate() + this.cssUnit;
}
}