Skip to content

Instantly share code, notes, and snippets.

@vitalyrotari
Last active February 22, 2021 19:06
Show Gist options
  • Save vitalyrotari/4973754 to your computer and use it in GitHub Desktop.
Save vitalyrotari/4973754 to your computer and use it in GitHub Desktop.
Vanilla JS - Fx | CSS Transition animation
(function (window, Element, undefined) {
'use strict';
var prefix = '',
eventPrefix,
vendors = { Webkit: 'webkit', Moz: '', O: 'o', ms: 'MS' },
document = window.document,
testEl = document.createElement('div'),
supportedTransforms = /^((translate|rotate|scale)(X|Y|Z|3d)?|matrix(3d)?|perspective|skew(X|Y)?)$/i,
transform,
transitionProperty, transitionDuration, transitionTiming,
animationName,
animationDuration,
animationTiming,
cssReset = {};
/**
* @param {string} str
* @returns {string}
*/
function dasherize (str) {
return downcase(str.replace(/([a-z])([A-Z])/, '$1-$2'));
}
/**
* @param {string} str
* @returns {string}
*/
function downcase (str) {
return str.toLowerCase();
}
/**
* @param {string} name
* @returns {string}
*/
function normalizeEvent (name) {
return eventPrefix ? eventPrefix + name : downcase(name);
}
/**
* @param {Element} element
* @param {object} props
*/
function css (element, props) {
var style = element['style'];
if (props) {
for (var prop in props) {
if (props.hasOwnProperty(prop)) {
style[prop] = props[prop];
}
}
}
}
for (var vendor in vendors) {
if (vendors.hasOwnProperty(vendor)) {
if (testEl.style[vendor + 'TransitionProperty'] !== undefined) {
prefix = "-" + downcase(vendor) + "-";
eventPrefix = vendors[vendor];
break;
}
}
}
transform = prefix + "transform";
cssReset[transitionProperty = prefix + "transition-property"] =
cssReset[transitionDuration = prefix + "transition-duration"] =
cssReset[transitionTiming = prefix + "transition-timing-function"] =
cssReset[animationName = prefix + "animation-name"] =
cssReset[animationDuration = prefix + "animation-duration"] =
cssReset[animationTiming = prefix + "animation-timing-function"] = "";
window.fx = {
off: (eventPrefix === undefined && testEl.style[transitionProperty] === undefined),
speeds: { _default: 400, fast: 200, slow: 600 },
cssPrefix: prefix,
transitionEnd: normalizeEvent('TransitionEnd'),
animationEnd: normalizeEvent('AnimationEnd')
};
function animate (element, properties, duration, ease, callback) {
var key,
cssValues = {},
cssProperties,
transforms = '',
wrappedCallback,
endEvent = window.fx.transitionEnd;
if (duration === undefined) {
duration = 0.4;
}
if (window.fx.off) {
duration = 0;
}
if (typeof properties == 'string') {
// keyframe animation
cssValues[animationName] = properties;
cssValues[animationDuration] = duration + "s";
cssValues[animationTiming] = (ease || "linear");
endEvent = window.fx.animationEnd;
} else {
cssProperties = [];
// CSS transitions
for (key in properties) {
if (properties.hasOwnProperty(key)) {
if (supportedTransforms.test(key)) {
transforms += key + "(" + properties[key] + ") ";
} else {
cssValues[key] = properties[key];
cssProperties.push(dasherize(key));
}
}
}
}
if (transforms) {
cssValues[transform] = transforms;
cssProperties.push(transform);
}
if (duration > 0 && typeof properties === "object") {
cssValues[transitionProperty] = cssProperties.join(", ");
cssValues[transitionDuration] = duration + "s";
cssValues[transitionTiming] = (ease || "linear");
}
wrappedCallback = function (event) {
if (typeof event !== "undefined") {
if (event.target !== event.currentTarget) {
return; // makes sure the event didn't bubble from "below"
}
event = new CustomEvent('fxEnd', {
detail: {},
bubbles: true,
cancelable: true
});
element.dispatchEvent(event);
}
css(element, cssReset);
callback && callback.call(element)
};
if (duration > 0 && !element.__fxEndWrapper) {
element.addEventListener(endEvent, wrappedCallback, false);
element.__fxEndWrapper = true;
}
// trigger page reflow so new elements can animate
element.clientLeft = element.clientLeft;
css(element, cssValues);
if (duration <= 0) {
setTimeout(function () {
wrappedCallback.call(element);
}, 0);
}
return element;
}
Element.prototype.fx = function (properties, duration, ease, callback) {
if (duration !== null && typeof duration === 'object') {
ease = duration.easing;
callback = duration.complete;
duration = duration.duration;
}
if (duration) {
duration = (typeof duration == "number" ? duration : (window.fx.speeds[duration] || window.fx.speeds._default)) / 1000;
}
return animate(this, properties, duration, ease, callback);
};
testEl = null;
}(window, Element));
var foo = document.querySelector('.foo');
foo.addEventListener('fxEnd', function() {
console.log('Animation finished!');
}, false);
foo.fx({ translateY: '100px' }, 500);
foo.fx({ translateY: '50px' }, 500, 'ease-out');
foo.fx({ translateY: '0px' }, 500, 'ease-out', function() {
console.log('Animation finished!');
});
@stoikerty
Copy link

Finally, something simple and short for using transforms cross-browser, without jquery!
You are my saviour!

Thanks :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment