« Back to Index

JavaScript CSS animation

View original Gist on GitHub

Tags: #js

Animate Class.js

// This script was written by @ded - I've just modified it slightly to take into account other browser vendor prefixes

/**
 * @constructor Animate
 * @param {HTMLElement} el the element we want to animate
 * @param {String} prop the CSS property we will be animating
 * @param {Object} opts a configuration object
 * object properties include
 * from {Int}
 * to {Int}
 * time {Int} time in milliseconds
 * callback {Function}
 */

function Animate(el, prop, opts) {
    this.el = el;
    this.prop = prop;
    this.from = opts.from;
    this.to = opts.to;
    this.time = opts.time;
    this.callback = opts.callback;
    this.animDiff = this.to - this.from;
}

/**
 * @private
 * @param {String} val the CSS value we will set on the property
 */
Animate.prototype._setStyle = function(val) {
	if (this.prop == 'opacity') {
    	this.el.style[this.prop] = val;
		this.el.style.filter = 'alpha(opacity=' + val * 100 + ')';
	} else {
    	this.el.style[this.prop] = val + 'px';
	}
};

/**
 * @private
 * this is the tweening function
 */
Animate.prototype._animate = function() {
    var that = this;
    this.now = new Date();
    this.diff = this.now - this.startTime;

    if (this.diff > this.time) {
        this._setStyle(this.to);

        if (this.callback) {
            this.callback.call(this);
        }
        clearInterval(this.timer);
        return;
    }

    this.percentage = (Math.floor((this.diff / this.time) * 100) / 100);
    this.val = (this.animDiff * this.percentage) + this.from;
    this._setStyle(this.val);
};

/**
 * @public
 * begins the animation
 */
Animate.prototype.start = function() {
    var that = this;
    this.startTime = new Date();

    this.timer = setInterval(function() {
        that._animate.call(that);
    }, 4);
};

/**
 * @static
 * @boolean
 * allows us to check if native CSS transitions are possible
 */
Animate.canTransition = (function() {
	/*
	var el = document.createElement('foo');
   el.style.cssText = '-webkit-transition: all .5s linear;';
   return !!el.style.webkitTransitionProperty;
   */
   
   // Modified check to match jQuery.support.transition (https://gist.github.com/373874)
   var thisBody = document.body || document.documentElement,
		 thisStyle = thisBody.style,
    	 support = thisStyle.WebkitTransition !== undefined || thisStyle.MozTransition !== undefined || thisStyle.OTransition !== undefined || thisStyle.transition !== undefined;
    
	return support; 
}());

/**
 * @static
 * @boolean
 * tells us whether CSS transitions are possible either natively or via a browser vendor prefixed
 */
Animate.whichTransition = (function() {
	var thisBody = document.body || document.documentElement,
		 thisStyle = thisBody.style,
    	 obj = {
    	 	'webkit': thisStyle.WebkitTransition !== undefined,
    	 	'mozilla': thisStyle.MozTransition !== undefined,
    	 	'opera': thisStyle.OTransition !== undefined,
    	 	'default': thisStyle.transition !== undefined
    	 };
   
   for (prop in obj) {
   	if (obj.hasOwnProperty(prop)) {
   		if (obj[prop]) {
   			return prop;
   		}
   	}
   }
}());

Animate Demo.js

var blocking = false;

alert(Animate.whichTransition);
	
document.getElementById('click').onclick = function(e) {
	if (blocking) {
   		return false;
 	}
 	
 	blocking = true;
 	
 	var that = this,
 	    el = document.getElementById('test'),
	    from = (this.className == 'animated') ? 1 : 0,
 	    to = from == 1 ? 0 : 1;
		
		// relevant stuffs
 	if (Animate.canTransition) {
   		switch (Animate.whichTransition) {
   			case 'webkit':
   				console.log('WebKit Vendor Prefix');
   				el.style.WebkitTransition = 'opacity 0.5s ease-out';
	   			break;
   			case 'mozilla':
   				console.log('Mozilla Vendor Prefix');
   				el.style.MozTransition = 'opacity 0.5s ease-out';
   				break;
   			case 'opera':
	   			console.log('Opera Vendor Prefix');
   				el.style.OTransition = 'opacity 0.5s ease-out';
   				break;
   			default:
   				console.log('W3C Standard Compliant Browser');
	   			el.style.transition = 'opacity 0.5s ease-out';
   				break;
   		}
   		
		el.style.opacity = to;
   		blocking = false;
   		that.className = (that.className == 'animated') ? '' : 'animated';
 	} else {
   		new Animate(el, 'opacity', {
     			from: from,
	     		to: to,
     			time: 500,
     			callback: function() {
	       			that.className = (that.className == 'animated') ? '' : 'animated';
       				blocking = false;
     			}
	   	}).start();
 	}
 	
 	return false;
}};