skylark-utils
Version:
An Elegant HTML5 JavaScript Library.
287 lines (242 loc) • 8.25 kB
JavaScript
define([
"./skylark",
"./langx",
"./browser",
"./styler",
"./eventer"
], function(skylark, langx, browser, styler, eventer) {
var animationName,
animationDuration,
animationTiming,
animationDelay,
transitionProperty,
transitionDuration,
transitionTiming,
transitionDelay,
animationEnd = browser.normalizeCssEvent('AnimationEnd'),
transitionEnd = browser.normalizeCssEvent('TransitionEnd'),
supportedTransforms = /^((translate|rotate|scale)(X|Y|Z|3d)?|matrix(3d)?|perspective|skew(X|Y)?)$/i,
transform = browser.css3PropPrefix + "transform",
cssReset = {};
cssReset[animationName = browser.normalizeCssProperty("animation-name")] =
cssReset[animationDuration = browser.normalizeCssProperty("animation-duration")] =
cssReset[animationDelay = browser.normalizeCssProperty("animation-delay")] =
cssReset[animationTiming = browser.normalizeCssProperty("animation-timing-function")] = "";
cssReset[transitionProperty = browser.normalizeCssProperty("transition-property")] =
cssReset[transitionDuration = browser.normalizeCssProperty("transition-duration")] =
cssReset[transitionDelay = browser.normalizeCssProperty("transition-delay")] =
cssReset[transitionTiming = browser.normalizeCssProperty("transition-timing-function")] = "";
function animate(elm, properties, duration, ease, callback, delay) {
var key,
cssValues = {},
cssProperties = [],
transforms = "",
that = this,
endEvent,
wrappedCallback,
fired = false,
hasScrollTop = false;
if (langx.isPlainObject(duration)) {
ease = duration.easing;
callback = duration.complete;
delay = duration.delay;
duration = duration.duration;
}
if (langx.isString(duration)) {
duration = fx.speeds[duration];
}
if (duration === undefined) {
duration = fx.speeds.normal;
}
duration = duration / 1000;
if (fx.off) {
duration = 0;
}
if (langx.isFunction(ease)) {
callback = ease;
eace = "swing";
} else {
ease = ease || "swing";
}
if (delay) {
delay = delay / 1000;
} else {
delay = 0;
}
if (langx.isString(properties)) {
// keyframe animation
cssValues[animationName] = properties;
cssValues[animationDuration] = duration + "s";
cssValues[animationTiming] = ease;
endEvent = animationEnd;
} else {
// CSS transitions
for (key in properties) {
if (supportedTransforms.test(key)) {
transforms += key + "(" + properties[key] + ") ";
} else {
if (key === "scrollTop") {
hasScrollTop = true;
}
cssValues[key] = properties[key];
cssProperties.push(langx.dasherize(key));
}
}
endEvent = transitionEnd;
}
if (transforms) {
cssValues[transform] = transforms;
cssProperties.push(transform);
}
if (duration > 0 && langx.isPlainObject(properties)) {
cssValues[transitionProperty] = cssProperties.join(", ");
cssValues[transitionDuration] = duration + "s";
cssValues[transitionDelay] = delay + "s";
cssValues[transitionTiming] = ease;
}
wrappedCallback = function(event) {
fired = true;
if (event) {
if (event.target !== event.currentTarget) {
return // makes sure the event didn't bubble from "below"
}
eventer.off(event.target, endEvent, wrappedCallback)
} else {
eventer.off(elm, animationEnd, wrappedCallback) // triggered by setTimeout
}
styler.css(elm, cssReset);
callback && callback.call(this);
};
if (duration > 0) {
eventer.on(elm, endEvent, wrappedCallback);
// transitionEnd is not always firing on older Android phones
// so make sure it gets fired
langx.debounce(function() {
if (fired) {
return;
}
wrappedCallback.call(that);
}, ((duration + delay) * 1000) + 25)();
}
// trigger page reflow so new elements can animate
elm.clientLeft;
styler.css(elm, cssValues);
if (duration <= 0) {
langx.debounce(function() {
if (fired) {
return;
}
wrappedCallback.call(that);
}, 0)();
}
if (hasScrollTop) {
scrollToTop(elm, properties["scrollTop"], duration, callback);
}
return this;
}
function show(elm, speed, callback) {
styler.show(elm);
if (speed) {
if (!callback && langx.isFunction(speed)) {
callback = speed;
speed = "normal";
}
styler.css(elm, "opacity", 0)
animate(elm, { opacity: 1, scale: "1,1" }, speed, callback);
}
return this;
}
function hide(elm, speed, callback) {
if (speed) {
if (!callback && langx.isFunction(speed)) {
callback = speed;
speed = "normal";
}
animate(elm, { opacity: 0, scale: "0,0" }, speed, function() {
styler.hide(elm);
if (callback) {
callback.call(elm);
}
});
} else {
styler.hide(elm);
}
return this;
}
function scrollToTop(elm, pos, speed, callback) {
var scrollFrom = parseInt(elm.scrollTop),
i = 0,
runEvery = 5, // run every 5ms
freq = speed * 1000 / runEvery,
scrollTo = parseInt(pos);
var interval = setInterval(function() {
i++;
if(i<=freq) elm.scrollTop = (scrollTo - scrollFrom) / freq * i + scrollFrom;
if (i >= freq + 1) {
clearInterval(interval);
if (callback) langx.debounce(callback, 1000)();
}
}, runEvery);
}
function toggle(elm, speed, callback) {
if (styler.isInvisible(elm)) {
show(elm, speed, callback);
} else {
hide(elm, speed, callback);
}
return this;
}
function fadeTo(elm, speed, opacity, callback) {
animate(elm, { opacity: opacity }, speed, callback);
return this;
}
function fadeIn(elm, speed, callback) {
var target = styler.css(elm, "opacity");
if (target > 0) {
styler.css(elm, "opacity", 0);
} else {
target = 1;
}
styler.show(elm);
fadeTo(elm, speed, target, callback);
return this;
}
function fadeOut(elm, speed, callback) {
fadeTo(elm, speed, 0, function() {
styler.hide(elm);
if (callback) {
callback.call(elm);
}
});
return this;
}
function fadeToggle(elm, speed, callback) {
if (styler.isInvisible(elm)) {
fadeIn(elm, speed, callback);
} else {
fadeOut(elm, speed, callback);
}
return this;
}
function fx() {
return fx;
}
langx.mixin(fx, {
off: false,
speeds: {
normal: 400,
fast: 200,
slow: 600
},
animate: animate,
fadeIn: fadeIn,
fadeOut: fadeOut,
fadeTo: fadeTo,
fadeToggle: fadeToggle,
hide: hide,
scrollToTop: scrollToTop,
show: show,
toggle: toggle
});
return skylark.fx = fx;
});