foundation-apps
Version:
A responsive, Angular-powered framework for web apps from ZURB.
147 lines (117 loc) • 3.9 kB
JavaScript
(function() {
'use strict';
angular.module('foundation.core.animation', [])
.service('FoundationAnimation', FoundationAnimation)
;
FoundationAnimation.$inject = ['$q'];
function FoundationAnimation($q) {
var animations = [];
var service = {};
var initClasses = ['ng-enter', 'ng-leave'];
var activeClasses = ['ng-enter-active', 'ng-leave-active'];
var activeGenericClass = 'is-active';
var events = [
'webkitAnimationEnd', 'mozAnimationEnd',
'MSAnimationEnd', 'oanimationend',
'animationend', 'webkitTransitionEnd',
'otransitionend', 'transitionend'
];
service.animate = animate;
service.toggleAnimation = toggleAnimation;
return service;
function toggleAnimation(element, futureState) {
if(futureState) {
element.addClass(activeGenericClass);
} else {
element.removeClass(activeGenericClass);
}
}
function animate(element, futureState, animationIn, animationOut) {
var animationTimeout;
var deferred = $q.defer();
var timedOut = true;
var self = this;
self.cancelAnimation = cancelAnimation;
var animationClass = futureState ? animationIn: animationOut;
var activation = futureState;
var initClass = activation ? initClasses[0] : initClasses[1];
var activeClass = activation ? activeClasses[0] : activeClasses[1];
run();
return deferred.promise;
function run() {
//stop animation
registerElement(element);
reset();
element.addClass(animationClass);
element.addClass(initClass);
element.addClass(activeGenericClass);
//force a "tick"
reflow();
//activate
element[0].style.transitionDuration = '';
element.addClass(activeClass);
element.on(events.join(' '), eventHandler);
animationTimeout = setTimeout(function() {
if(timedOut) {
finishAnimation();
}
}, 3000);
}
function eventHandler(e) {
if (element[0] === e.target) {
clearTimeout(animationTimeout);
finishAnimation();
}
}
function finishAnimation() {
deregisterElement(element);
reset(); //reset all classes
element[0].style.transitionDuration = '';
element.removeClass(!activation ? activeGenericClass : ''); //if not active, remove active class
reflow();
timedOut = false;
element.off(events.join(' '), eventHandler);
deferred.resolve({element: element, active: activation});
}
function cancelAnimation(element) {
deregisterElement(element);
angular.element(element).off(events.join(' ')); //kill all animation event handlers
timedOut = false;
deferred.reject();
}
function registerElement(el) {
var elObj = {
el: el,
animation: self
};
//kill in progress animations
var inProgress = animations.filter(function(obj) {
return obj.el === el;
});
if(inProgress.length > 0) {
var target = inProgress[0].el[0];
inProgress[0].animation.cancelAnimation(target);
}
animations.push(elObj);
}
function deregisterElement(el) {
var index;
var currentAnimation = animations.filter(function(obj, ind) {
if(obj.el === el) {
index = ind;
}
});
if(index >= 0) {
animations.splice(index, 1);
}
}
function reflow() {
return element[0].offsetWidth;
}
function reset() {
element[0].style.transitionDuration = 0;
element.removeClass(initClasses.join(' ') + ' ' + activeClasses.join(' ') + ' ' + animationIn + ' ' + animationOut);
}
}
}
})();