├── LICENSE.txt ├── animate.min.js ├── README.md └── animate.js /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Benjamin De Cock 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /animate.min.js: -------------------------------------------------------------------------------- 1 | var animate=function(){var d={delay:0,duration:400,easing:"ease"},g=function(){return Array.prototype.includes?function(b,a){return b.includes(a)}:function(b,a){return b.some(function(b){return b===a})}}(),h=function(){var b=function(a){return/\D$/.test(a)?a:a+"ms"};return function(a,c){var f={property:"opacity,"+e(),duration:b(c.duration||d.duration),"timing-function":c.easing||d.easing,delay:b(c.delay||d.delay)};Object.keys(f).forEach(function(b){a.style["transition-"+b]=f[b]})}}(),k=function(b){var a= 2 | function(c){c.target.removeEventListener("transitionend",a);b.complete&&b.complete.call(c.target)};return a},l=function(b,a){void 0!=a.opacity&&(b.style.opacity=a.opacity)},e=function(){var b;return function(){b||(b="transform"in document.body.style?"transform":"-webkit-transform");return b}}(),m=function(){var b=["complete","el","opacity"].concat(Object.keys(d));return function(a){return!g(b,a)}}(),n=function(b){return Object.keys(b).filter(function(a){return m(a)})},p=function(){return function(b, 3 | a){var c=n(a);c.length&&(b.style[e()]=c.map(function(b){var c;c=a[b];c=/\D$/.test(c)||/scale/.test(b)?c:/rotate|skew/.test(b)?c+"deg":c+"px";return b+"("+c+")"}).join(" "))}}(),q=function(b){return function(a){requestAnimationFrame(function(){[h,l,p].forEach(function(c){c(a,b)})});a.addEventListener("transitionend",k(b))}};return function(b){return function(a){"object"==typeof a&&b(a)}}(function(b){var a=b.el;if("string"==typeof a)var c=document,a=/^[\#.]?[\w-]+$/.test(a)?"."==a[0]?c.getElementsByClassName(a.slice(1)): 4 | "#"==a[0]?c.getElementById(a.slice(1)):c.getElementsByTagName(a):c.querySelectorAll(a);(a.nodeType?[a]:Array.isArray(a)?a:[].slice.call(a)).forEach(q(b))})}(); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Animate 2 | 3 | `animate.js` is a tiny library that helps you write smooth CSS-powered animations in JavaScript. See 4 | a quick demo of 500 elements animating at 5 | [playground.deaxon.com/js/animate](http://playground.deaxon.com/js/animate/). 6 | 7 | # Usage 8 | 9 | Include `animate.min.js` in your page: 10 | ```html 11 | 12 | ``` 13 | 14 | Start animating things: 15 | ```javascript 16 | animate({ 17 | el: "div", 18 | translateX: 100, 19 | opacity: .5, 20 | duration: 500, 21 | complete: function() { alert("Done!") } 22 | }); 23 | ``` 24 | 25 | # API 26 | 27 | `animate` requires a config object accepting the following options: 28 | 29 | ## el 30 | The elements you want to animate. `el` can be a: 31 | 32 | * CSS selector 33 | * DOM element 34 | * array of DOM elements 35 | * NodeList or HTMLCollection 36 | 37 | ## duration 38 | [Optional] The duration of your animation in milliseconds. Default: 400. `ms` will be added if you don't specify a 39 | unit. 40 | 41 | ## delay 42 | [Optional] The delay before your animation starts in milliseconds. Default: 0. `ms` will be added if you don't specify a 43 | unit. 44 | 45 | ## easing 46 | [Optional] The animation curve (CSS keyword or cubic-bezier). Default: ease. 47 | 48 | ## complete 49 | [Optional] A function called at the end of the animation. Inside the function, `this` refers to the 50 | DOM element that has finished animating. 51 | 52 | ## animatable properties 53 | * opacity 54 | * translateX 55 | * translateY 56 | * translateZ 57 | * scaleX 58 | * scaleY 59 | * scaleZ 60 | * rotateX 61 | * rotateY 62 | * rotateZ 63 | * skewX 64 | * skewY 65 | * perspective 66 | 67 | You can also use the shortcut transform functions `translate`, `scale` and `rotate` when the same 68 | value should be applied to X and Y: 69 | 70 | ```javascript 71 | translate: 100 // same as translateX: 100, translateY: 100 72 | ``` 73 | 74 | If you don't specify a unit, `px` will be added to `translate`-related and `perspective` functions and `deg` 75 | to `rotate`-related functions. 76 | -------------------------------------------------------------------------------- /animate.js: -------------------------------------------------------------------------------- 1 | const animate = (() => { 2 | 3 | 4 | "use strict"; 5 | 6 | const defaults = { 7 | delay: 0, 8 | duration: 400, 9 | easing: "ease" 10 | }; 11 | 12 | 13 | // array utils =================================================================================== 14 | 15 | const head = (arr) => { 16 | return arr[0]; 17 | }; 18 | 19 | const tail = (arr) => { 20 | return arr.slice(1); 21 | }; 22 | 23 | const contains = (() => { 24 | return Array.prototype.includes 25 | ? (arr, value) => arr.includes(value) 26 | : (arr, value) => arr.some(el => el === value) 27 | })(); 28 | 29 | const toArray = (obj) => { 30 | if (obj.nodeType) { 31 | return [obj]; 32 | } 33 | if (Array.isArray(obj)) { 34 | return obj; 35 | } 36 | return [...obj]; 37 | }; 38 | 39 | 40 | // params utils ================================================================================== 41 | 42 | const requireParams = (func) => { 43 | return params => { 44 | if (typeof params != "object") return; 45 | func(params); 46 | }; 47 | }; 48 | 49 | const getParamsEl = (el) => { 50 | return toArray(typeof el == "string" ? selectElements(el) : el); 51 | }; 52 | 53 | 54 | // misc utils ==================================================================================== 55 | 56 | const selectElements = (selector, context = document) => { 57 | if (/^[\#.]?[\w-]+$/.test(selector)) { 58 | if (head(selector) == ".") { 59 | return context.getElementsByClassName(tail(selector)); 60 | } 61 | if (head(selector) == "#") { 62 | return context.getElementById(tail(selector)); 63 | } 64 | return context.getElementsByTagName(selector); 65 | } 66 | return context.querySelectorAll(selector); 67 | }; 68 | 69 | const hasUnit = (value) => { 70 | return /\D$/.test(value); 71 | }; 72 | 73 | 74 | // transition ==================================================================================== 75 | 76 | const setTransition = (() => { 77 | const addUnit = (value) => { 78 | if (hasUnit(value)) { 79 | return value; 80 | } 81 | return value + "ms"; 82 | }; 83 | return (el, params) => { 84 | var transition = { 85 | "property": "opacity," + transformProperty(), 86 | "duration": addUnit(params.duration || defaults.duration), 87 | "timing-function": params.easing || defaults.easing, 88 | "delay": addUnit(params.delay || defaults.delay) 89 | }; 90 | Object.keys(transition).forEach((prop) => { 91 | el.style["transition-" + prop] = transition[prop]; 92 | }); 93 | }; 94 | })(); 95 | 96 | const clearTransition = (params) => { 97 | const clearListener = (event) => { 98 | event.target.removeEventListener("transitionend", clearListener); 99 | if (!params.complete) return; 100 | params.complete.call(event.target); 101 | }; 102 | return clearListener; 103 | }; 104 | 105 | 106 | // opacity ======================================================================================= 107 | 108 | const setOpacity = (el, params) => { 109 | if (params.opacity == undefined) return; 110 | el.style.opacity = params.opacity; 111 | }; 112 | 113 | 114 | // transform ===================================================================================== 115 | 116 | const transformProperty = (() => { 117 | var transform; 118 | return () => { 119 | if (!transform) { 120 | transform = "transform" in document.body.style ? "transform" : "-webkit-transform"; 121 | } 122 | return transform; 123 | }; 124 | })(); 125 | 126 | const isTransform = (() => { 127 | const ignore = ["complete", "el", "opacity"].concat(Object.keys(defaults)); 128 | return (key) => !contains(ignore, key); 129 | })(); 130 | 131 | const getTransformFunctions = (params) => { 132 | return Object.keys(params).filter((key) => { 133 | return isTransform(key); 134 | }); 135 | }; 136 | 137 | const setTransform = (() => { 138 | const addUnit = (transformFunction, value) => { 139 | if (hasUnit(value) || /scale/.test(transformFunction)) { 140 | return value; 141 | } 142 | if (/rotate|skew/.test(transformFunction)) { 143 | return value + "deg"; 144 | } 145 | return value + "px"; 146 | }; 147 | return (el, params) => { 148 | const transforms = getTransformFunctions(params); 149 | if (!transforms.length) return; 150 | el.style[transformProperty()] = transforms.map((func) => { 151 | return func + "(" + addUnit(func, params[func]) + ")"; 152 | }).join(" "); 153 | }; 154 | })(); 155 | 156 | 157 | // init ========================================================================================== 158 | 159 | const setStyle = (params) => { 160 | return (el) => { 161 | // wait for the next frame 162 | requestAnimationFrame(() => { 163 | [setTransition, setOpacity, setTransform].forEach((func) => { 164 | func(el, params); 165 | }); 166 | }); 167 | el.addEventListener("transitionend", clearTransition(params)); 168 | }; 169 | }; 170 | 171 | return requireParams((params) => { 172 | getParamsEl(params.el).forEach(setStyle(params)); 173 | }); 174 | 175 | 176 | })(); 177 | --------------------------------------------------------------------------------