├── .babelrc.js ├── .editorconfig ├── .gitignore ├── .prettierrc ├── .storybook └── config.js ├── HISTORY.md ├── LICENSE.md ├── README.md ├── package.json ├── src ├── Event.js ├── classes.js └── index.js └── stories ├── collapse.js ├── events.js ├── simple.js └── style.js /.babelrc.js: -------------------------------------------------------------------------------- 1 | console.log('Load babel config'); 2 | 3 | module.exports = { 4 | presets: [ 5 | [ 6 | '@babel/preset-env', 7 | { 8 | modules: false, 9 | }, 10 | ], 11 | ], 12 | }; 13 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # top-most EditorConfig file 2 | root = true 3 | 4 | # Unix-style newlines with a newline ending every file 5 | [*.{js,css}] 6 | end_of_line = lf 7 | insert_final_newline = true 8 | indent_style = space 9 | indent_size = 2 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | *.log 3 | .idea/ 4 | .ipr 5 | .iws 6 | *~ 7 | ~* 8 | *.diff 9 | *.patch 10 | *.bak 11 | .DS_Store 12 | Thumbs.db 13 | .project 14 | .*proj 15 | .svn/ 16 | *.swp 17 | *.swo 18 | *.pyc 19 | *.pyo 20 | .build 21 | node_modules 22 | .cache 23 | dist 24 | assets/**/*.css 25 | build 26 | lib 27 | coverage 28 | yarn.lock 29 | /pkg/ 30 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all" 4 | } -------------------------------------------------------------------------------- /.storybook/config.js: -------------------------------------------------------------------------------- 1 | import { addParameters, configure } from '@storybook/react'; 2 | 3 | addParameters({ 4 | options: { 5 | showAddonsPanel: false, 6 | theme: { 7 | brandTitle: 'css-animation', 8 | brandUrl: 'https://github.com/yiminghe/css-animation/', 9 | } 10 | } 11 | }); 12 | 13 | // automatically import all files ending in *.stories.js 14 | const req = require.context('../stories', true, /.js$/); 15 | 16 | function loadStories() { 17 | req.keys().forEach(filename => req(filename)); 18 | } 19 | 20 | configure(loadStories, module); 21 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | # History 2 | ---- 3 | 4 | ## 2.0.0 / 2019-07-28 5 | 6 | - use pack 7 | 8 | ## 1.5.0 / 2018-11-12 9 | 10 | - support startEventListenter 11 | 12 | ## 1.4.0 / 2017-08-16 13 | 14 | - add es version 15 | 16 | ## 1.3.0 / 2016-08-01 17 | 18 | - support animationName as object 19 | 20 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-present yiminghe 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # css-animation 2 | --- 3 | 4 | make css animation easier 5 | 6 | [![NPM version][npm-image]][npm-url] 7 | 8 | [npm-image]: http://img.shields.io/npm/v/css-animation.svg?style=flat-square 9 | [npm-url]: http://npmjs.org/package/css-animation 10 | 11 | ## Development 12 | 13 | ``` 14 | npm install 15 | npm start 16 | ``` 17 | 18 | ## Example 19 | 20 | http://localhost:6006/ 21 | 22 | online example: http://yiminghe.github.io/css-animation/ 23 | 24 | 25 | ## Feature 26 | 27 | * support ie8,ie8+,chrome,firefox,safari 28 | 29 | ## install 30 | 31 | [![css-animation](https://nodei.co/npm/css-animation.png)](https://npmjs.org/package/css-animation) 32 | 33 | ## Usage 34 | 35 | ```js 36 | import anim from 'css-animation'; 37 | anim(el,animationName,function(){}); 38 | ``` 39 | 40 | ## API 41 | 42 | ### void anim(el:DOMElement, animationName:String, callback:Function) 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 |
nametypedefaultdescription
elDOMElementdom element to be animated
animationNameString|Objectwill add animationName (if string) or animationName.name (if object) as class to el, then setTimeout 0 to add ${animationName}-active (if string) or animationName.active (if object) to el
callbackFunctiontriggered when anim caused by animationName is done
74 | 75 | ## License 76 | 77 | css-animation is released under the MIT license. 78 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "css-animation", 3 | "version": "2.0.4", 4 | "description": "css-animation", 5 | "keywords": [ 6 | "css-animation" 7 | ], 8 | "homepage": "http://github.com/yiminghe/css-animation", 9 | "author": "", 10 | "repository": { 11 | "type": "git", 12 | "url": "git@github.com:yiminghe/css-animation.git" 13 | }, 14 | "bugs": { 15 | "url": "http://github.com/yiminghe/css-animation/issues" 16 | }, 17 | "license": "MIT", 18 | "@pika/pack": { 19 | "pipeline": [ 20 | [ 21 | "@pika/plugin-standard-pkg", 22 | { 23 | "exclude": [ 24 | "__tests__/**/*" 25 | ] 26 | } 27 | ], 28 | [ 29 | "pika-plugin-build-web-babel" 30 | ], 31 | [ 32 | "@pika/plugin-build-node" 33 | ], 34 | [ 35 | "pika-plugin-clean-dist-src" 36 | ] 37 | ] 38 | }, 39 | "scripts": { 40 | "prettier": "prettier --write \"{src,stories}/**/*.{js,tsx}\"", 41 | "start": "start-storybook -p 6006", 42 | "pub": "npm run build && npm publish pkg && git push", 43 | "build": "pack build", 44 | "deploy": "storybook-to-ghpages", 45 | "lint-staged": "lint-staged" 46 | }, 47 | "devDependencies": { 48 | "@pika/plugin-build-node": "0.6.x", 49 | "@pika/plugin-build-types": "0.6.x", 50 | "@pika/plugin-standard-pkg": "0.6.x", 51 | "@pika/types": "0.6.x", 52 | "@storybook/react": "^5.1.9", 53 | "@storybook/storybook-deployer": "^2.8.1", 54 | "babel-loader": "^8.0.6", 55 | "lint-staged": "^9.2.1", 56 | "pika-plugin-build-web-babel": "0.6.x", 57 | "pika-plugin-clean-dist-src": "^0.1.1", 58 | "pre-commit": "1.x", 59 | "prettier": "^1.18.2", 60 | "react": "16.x", 61 | "react-dom": "16.x" 62 | }, 63 | "lint-staged": { 64 | "*.{tsx,js,jsx,ts}": [ 65 | "prettier --write", 66 | "git add" 67 | ] 68 | }, 69 | "pre-commit": [ 70 | "lint-staged" 71 | ] 72 | } 73 | -------------------------------------------------------------------------------- /src/Event.js: -------------------------------------------------------------------------------- 1 | const START_EVENT_NAME_MAP = { 2 | transitionstart: { 3 | transition: 'transitionstart', 4 | WebkitTransition: 'webkitTransitionStart', 5 | MozTransition: 'mozTransitionStart', 6 | OTransition: 'oTransitionStart', 7 | msTransition: 'MSTransitionStart', 8 | }, 9 | 10 | animationstart: { 11 | animation: 'animationstart', 12 | WebkitAnimation: 'webkitAnimationStart', 13 | MozAnimation: 'mozAnimationStart', 14 | OAnimation: 'oAnimationStart', 15 | msAnimation: 'MSAnimationStart', 16 | }, 17 | }; 18 | 19 | const END_EVENT_NAME_MAP = { 20 | transitionend: { 21 | transition: 'transitionend', 22 | WebkitTransition: 'webkitTransitionEnd', 23 | MozTransition: 'mozTransitionEnd', 24 | OTransition: 'oTransitionEnd', 25 | msTransition: 'MSTransitionEnd', 26 | }, 27 | 28 | animationend: { 29 | animation: 'animationend', 30 | WebkitAnimation: 'webkitAnimationEnd', 31 | MozAnimation: 'mozAnimationEnd', 32 | OAnimation: 'oAnimationEnd', 33 | msAnimation: 'MSAnimationEnd', 34 | }, 35 | }; 36 | 37 | const startEvents = []; 38 | const endEvents = []; 39 | 40 | function detectEvents() { 41 | const testEl = document.createElement('div'); 42 | const style = testEl.style; 43 | 44 | if (!('AnimationEvent' in window)) { 45 | delete START_EVENT_NAME_MAP.animationstart.animation; 46 | delete END_EVENT_NAME_MAP.animationend.animation; 47 | } 48 | 49 | if (!('TransitionEvent' in window)) { 50 | delete START_EVENT_NAME_MAP.transitionstart.transition; 51 | delete END_EVENT_NAME_MAP.transitionend.transition; 52 | } 53 | 54 | function process(EVENT_NAME_MAP, events) { 55 | for (const baseEventName in EVENT_NAME_MAP) { 56 | if (EVENT_NAME_MAP.hasOwnProperty(baseEventName)) { 57 | const baseEvents = EVENT_NAME_MAP[baseEventName]; 58 | for (const styleName in baseEvents) { 59 | if (styleName in style) { 60 | events.push(baseEvents[styleName]); 61 | break; 62 | } 63 | } 64 | } 65 | } 66 | } 67 | 68 | process(START_EVENT_NAME_MAP, startEvents); 69 | process(END_EVENT_NAME_MAP, endEvents); 70 | } 71 | 72 | if (typeof window !== 'undefined' && typeof document !== 'undefined') { 73 | detectEvents(); 74 | } 75 | 76 | function addEventListener(node, eventName, eventListener) { 77 | node.addEventListener(eventName, eventListener, false); 78 | } 79 | 80 | function removeEventListener(node, eventName, eventListener) { 81 | node.removeEventListener(eventName, eventListener, false); 82 | } 83 | 84 | const TransitionEvents = { 85 | // Start events 86 | startEvents, 87 | 88 | addStartEventListener(node, eventListener) { 89 | if (startEvents.length === 0) { 90 | window.setTimeout(eventListener, 0); 91 | return; 92 | } 93 | startEvents.forEach(startEvent => { 94 | addEventListener(node, startEvent, eventListener); 95 | }); 96 | }, 97 | 98 | removeStartEventListener(node, eventListener) { 99 | if (startEvents.length === 0) { 100 | return; 101 | } 102 | startEvents.forEach(startEvent => { 103 | removeEventListener(node, startEvent, eventListener); 104 | }); 105 | }, 106 | 107 | // End events 108 | endEvents, 109 | 110 | addEndEventListener(node, eventListener) { 111 | if (endEvents.length === 0) { 112 | window.setTimeout(eventListener, 0); 113 | return; 114 | } 115 | endEvents.forEach(endEvent => { 116 | addEventListener(node, endEvent, eventListener); 117 | }); 118 | }, 119 | 120 | removeEndEventListener(node, eventListener) { 121 | if (endEvents.length === 0) { 122 | return; 123 | } 124 | endEvents.forEach(endEvent => { 125 | removeEventListener(node, endEvent, eventListener); 126 | }); 127 | }, 128 | }; 129 | 130 | export default TransitionEvents; 131 | -------------------------------------------------------------------------------- /src/classes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | function index(arr, obj) { 6 | if (arr.indexOf) return arr.indexOf(obj); 7 | for (var i = 0; i < arr.length; ++i) { 8 | if (arr[i] === obj) return i; 9 | } 10 | return -1; 11 | } 12 | 13 | /** 14 | * Whitespace regexp. 15 | */ 16 | 17 | var re = /\s+/; 18 | 19 | /** 20 | * toString reference. 21 | */ 22 | 23 | var toString = Object.prototype.toString; 24 | 25 | /** 26 | * Wrap `el` in a `ClassList`. 27 | * 28 | * @param {Element} el 29 | * @return {ClassList} 30 | * @api public 31 | */ 32 | 33 | export default function(el) { 34 | return new ClassList(el); 35 | } 36 | 37 | /** 38 | * Initialize a new ClassList for `el`. 39 | * 40 | * @param {Element} el 41 | * @api private 42 | */ 43 | 44 | function ClassList(el) { 45 | if (!el || !el.nodeType) { 46 | throw new Error('A DOM element reference is required'); 47 | } 48 | this.el = el; 49 | this.list = el.classList; 50 | } 51 | 52 | /** 53 | * Add class `name` if not already present. 54 | * 55 | * @param {String} name 56 | * @return {ClassList} 57 | * @api public 58 | */ 59 | 60 | ClassList.prototype.add = function(name) { 61 | // classList 62 | if (this.list) { 63 | this.list.add(name); 64 | return this; 65 | } 66 | 67 | // fallback 68 | var arr = this.array(); 69 | var i = index(arr, name); 70 | if (!~i) arr.push(name); 71 | this.el.className = arr.join(' '); 72 | return this; 73 | }; 74 | 75 | /** 76 | * Remove class `name` when present, or 77 | * pass a regular expression to remove 78 | * any which match. 79 | * 80 | * @param {String|RegExp} name 81 | * @return {ClassList} 82 | * @api public 83 | */ 84 | 85 | ClassList.prototype.remove = function(name) { 86 | if ('[object RegExp]' == toString.call(name)) { 87 | return this.removeMatching(name); 88 | } 89 | 90 | // classList 91 | if (this.list) { 92 | this.list.remove(name); 93 | return this; 94 | } 95 | 96 | // fallback 97 | var arr = this.array(); 98 | var i = index(arr, name); 99 | if (~i) arr.splice(i, 1); 100 | this.el.className = arr.join(' '); 101 | return this; 102 | }; 103 | 104 | /** 105 | * Remove all classes matching `re`. 106 | * 107 | * @param {RegExp} re 108 | * @return {ClassList} 109 | * @api private 110 | */ 111 | 112 | ClassList.prototype.removeMatching = function(re) { 113 | var arr = this.array(); 114 | for (var i = 0; i < arr.length; i++) { 115 | if (re.test(arr[i])) { 116 | this.remove(arr[i]); 117 | } 118 | } 119 | return this; 120 | }; 121 | 122 | /** 123 | * Toggle class `name`, can force state via `force`. 124 | * 125 | * For browsers that support classList, but do not support `force` yet, 126 | * the mistake will be detected and corrected. 127 | * 128 | * @param {String} name 129 | * @param {Boolean} force 130 | * @return {ClassList} 131 | * @api public 132 | */ 133 | 134 | ClassList.prototype.toggle = function(name, force) { 135 | // classList 136 | if (this.list) { 137 | if ('undefined' !== typeof force) { 138 | if (force !== this.list.toggle(name, force)) { 139 | this.list.toggle(name); // toggle again to correct 140 | } 141 | } else { 142 | this.list.toggle(name); 143 | } 144 | return this; 145 | } 146 | 147 | // fallback 148 | if ('undefined' !== typeof force) { 149 | if (!force) { 150 | this.remove(name); 151 | } else { 152 | this.add(name); 153 | } 154 | } else { 155 | if (this.has(name)) { 156 | this.remove(name); 157 | } else { 158 | this.add(name); 159 | } 160 | } 161 | 162 | return this; 163 | }; 164 | 165 | /** 166 | * Return an array of classes. 167 | * 168 | * @return {Array} 169 | * @api public 170 | */ 171 | 172 | ClassList.prototype.array = function() { 173 | var className = this.el.getAttribute('class') || ''; 174 | var str = className.replace(/^\s+|\s+$/g, ''); 175 | var arr = str.split(re); 176 | if ('' === arr[0]) arr.shift(); 177 | return arr; 178 | }; 179 | 180 | /** 181 | * Check if class `name` is present. 182 | * 183 | * @param {String} name 184 | * @return {ClassList} 185 | * @api public 186 | */ 187 | 188 | ClassList.prototype.has = ClassList.prototype.contains = function(name) { 189 | return this.list ? this.list.contains(name) : !!~index(this.array(), name); 190 | }; 191 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import Event from './Event'; 2 | import classes from './classes'; 3 | 4 | const isCssAnimationSupported = Event.endEvents.length !== 0; 5 | const capitalPrefixes = [ 6 | 'Webkit', 7 | 'Moz', 8 | 'O', 9 | // ms is special .... ! 10 | 'ms', 11 | ]; 12 | const prefixes = [ '-webkit-', '-moz-', '-o-', 'ms-', '' ]; 13 | 14 | function getStyleProperty(node, name) { 15 | // old ff need null, https://developer.mozilla.org/en-US/docs/Web/API/Window/getComputedStyle 16 | const style = window.getComputedStyle(node, null); 17 | let ret = ''; 18 | for (let i = 0; i < prefixes.length; i++) { 19 | ret = style.getPropertyValue(prefixes[i] + name); 20 | if (ret) { 21 | break; 22 | } 23 | } 24 | return ret; 25 | } 26 | 27 | function fixBrowserByTimeout(node) { 28 | if (isCssAnimationSupported) { 29 | const transitionDelay = 30 | parseFloat(getStyleProperty(node, 'transition-delay')) || 0; 31 | const transitionDuration = 32 | parseFloat(getStyleProperty(node, 'transition-duration')) || 0; 33 | const animationDelay = 34 | parseFloat(getStyleProperty(node, 'animation-delay')) || 0; 35 | const animationDuration = 36 | parseFloat(getStyleProperty(node, 'animation-duration')) || 0; 37 | const time = Math.max( 38 | transitionDuration + transitionDelay, 39 | animationDuration + animationDelay, 40 | ); 41 | // sometimes, browser bug 42 | node.rcEndAnimTimeout = setTimeout(() => { 43 | node.rcEndAnimTimeout = null; 44 | if (node.rcEndListener) { 45 | node.rcEndListener(); 46 | } 47 | }, time * 1000 + 200); 48 | } 49 | } 50 | 51 | function clearBrowserBugTimeout(node) { 52 | if (node.rcEndAnimTimeout) { 53 | clearTimeout(node.rcEndAnimTimeout); 54 | node.rcEndAnimTimeout = null; 55 | } 56 | } 57 | 58 | const cssAnimation = (node, transitionName, endCallback) => { 59 | const nameIsObj = typeof transitionName === 'object'; 60 | const className = nameIsObj ? transitionName.name : transitionName; 61 | const activeClassName = nameIsObj 62 | ? transitionName.active 63 | : `${transitionName}-active`; 64 | let end = endCallback; 65 | let start; 66 | let active; 67 | const nodeClasses = classes(node); 68 | 69 | if ( 70 | endCallback && 71 | Object.prototype.toString.call(endCallback) === '[object Object]' 72 | ) { 73 | end = endCallback.end; 74 | start = endCallback.start; 75 | active = endCallback.active; 76 | } 77 | 78 | if (node.rcEndListener) { 79 | node.rcEndListener(); 80 | } 81 | 82 | node.rcEndListener = e => { 83 | if (e && e.target !== node) { 84 | return; 85 | } 86 | 87 | if (node.rcAnimTimeout) { 88 | clearTimeout(node.rcAnimTimeout); 89 | node.rcAnimTimeout = null; 90 | } 91 | 92 | clearBrowserBugTimeout(node); 93 | 94 | nodeClasses.remove(className); 95 | nodeClasses.remove(activeClassName); 96 | 97 | Event.removeEndEventListener(node, node.rcEndListener); 98 | node.rcEndListener = null; 99 | 100 | // Usually this optional end is used for informing an owner of 101 | // a leave animation and telling it to remove the child. 102 | if (end) { 103 | end(); 104 | } 105 | }; 106 | 107 | Event.addEndEventListener(node, node.rcEndListener); 108 | 109 | if (start) { 110 | start(); 111 | } 112 | nodeClasses.add(className); 113 | 114 | node.rcAnimTimeout = setTimeout(() => { 115 | node.rcAnimTimeout = null; 116 | nodeClasses.add(activeClassName); 117 | if (active) { 118 | setTimeout(active, 0); 119 | } 120 | fixBrowserByTimeout(node); 121 | // 30ms for firefox 122 | }, 30); 123 | 124 | return { 125 | stop() { 126 | if (node.rcEndListener) { 127 | node.rcEndListener(); 128 | } 129 | }, 130 | }; 131 | }; 132 | 133 | cssAnimation.style = (node, style, callback) => { 134 | if (node.rcEndListener) { 135 | node.rcEndListener(); 136 | } 137 | 138 | node.rcEndListener = e => { 139 | if (e && e.target !== node) { 140 | return; 141 | } 142 | 143 | if (node.rcAnimTimeout) { 144 | clearTimeout(node.rcAnimTimeout); 145 | node.rcAnimTimeout = null; 146 | } 147 | 148 | clearBrowserBugTimeout(node); 149 | 150 | Event.removeEndEventListener(node, node.rcEndListener); 151 | node.rcEndListener = null; 152 | 153 | // Usually this optional callback is used for informing an owner of 154 | // a leave animation and telling it to remove the child. 155 | if (callback) { 156 | callback(); 157 | } 158 | }; 159 | 160 | Event.addEndEventListener(node, node.rcEndListener); 161 | 162 | node.rcAnimTimeout = setTimeout(() => { 163 | for (const s in style) { 164 | if (style.hasOwnProperty(s)) { 165 | node.style[s] = style[s]; 166 | } 167 | } 168 | node.rcAnimTimeout = null; 169 | fixBrowserByTimeout(node); 170 | }, 0); 171 | }; 172 | 173 | cssAnimation.setTransition = (node, p, value) => { 174 | let property = p; 175 | let v = value; 176 | if (value === undefined) { 177 | v = property; 178 | property = ''; 179 | } 180 | property = property || ''; 181 | capitalPrefixes.forEach(prefix => { 182 | node.style[`${prefix}Transition${property}`] = v; 183 | }); 184 | }; 185 | 186 | cssAnimation.isCssAnimationSupported = isCssAnimationSupported; 187 | 188 | export { isCssAnimationSupported, Event }; 189 | 190 | export default cssAnimation; 191 | -------------------------------------------------------------------------------- /stories/collapse.js: -------------------------------------------------------------------------------- 1 | import anim from '../src'; 2 | import React from 'react'; 3 | import ReactDOM from 'react-dom'; 4 | import { storiesOf } from '@storybook/react'; 5 | const style = ` 6 | 7 | .box { 8 | background:red; 9 | width:100px; 10 | height:100px; 11 | } 12 | .collapse-active { 13 | transition: height .3s ease-out; 14 | } 15 | 16 | `; 17 | 18 | let show = true; 19 | 20 | function toggle() { 21 | const t = document.getElementById('t'); 22 | const b = document.getElementById('b'); 23 | b.disabled = true; 24 | t.style.display = ''; 25 | let height; 26 | anim(t, `collapse`, { 27 | start() { 28 | if (show) { 29 | t.style.height = `${t.offsetHeight}px`; 30 | } else { 31 | height = t.offsetHeight; 32 | t.style.height = 0; 33 | } 34 | }, 35 | active() { 36 | t.style.height = `${show ? height : 0}px`; 37 | }, 38 | end() { 39 | t.style.display = show ? '' : 'none'; 40 | b.disabled = false; 41 | t.style.height = ''; 42 | }, 43 | }); 44 | show = !show; 45 | } 46 | 47 | const Demo = () => ( 48 |
49 | 50 |
51 | 54 |
55 | ); 56 | 57 | Demo.story = 'collapse'; 58 | 59 | storiesOf(Demo.story, module).add('demo', () => ); 60 | 61 | export default Demo; 62 | -------------------------------------------------------------------------------- /stories/events.js: -------------------------------------------------------------------------------- 1 | import Events from '../src/Event'; 2 | import React from 'react'; 3 | import ReactDOM from 'react-dom'; 4 | import { storiesOf } from '@storybook/react'; 5 | const style = ` 6 | .box { 7 | background:red; 8 | width:100px; 9 | height:100px; 10 | animation-duration: 0.3s; 11 | animation-fill-mode: both; 12 | animation-timing-function: cubic-bezier(0.55, 0, 0.55, 0.2); 13 | animation-play-state: paused; 14 | 15 | animation-name: fadeIn; 16 | animation-play-state: running; 17 | } 18 | 19 | .active { 20 | animation-name: fadeOut; 21 | animation-play-state: running; 22 | } 23 | 24 | @keyframes fadeIn { 25 | 0% { 26 | opacity: 0; 27 | } 28 | 100% { 29 | opacity: 1; 30 | } 31 | } 32 | 33 | @keyframes fadeOut { 34 | 0% { 35 | opacity: 1; 36 | } 37 | 100% { 38 | opacity: 0; 39 | } 40 | } 41 | `; 42 | 43 | setTimeout(() => { 44 | const t = document.getElementById('t'); 45 | Events.addStartEventListener(t, () => { 46 | console.log('transition start...'); 47 | }); 48 | Events.addEndEventListener(t, () => { 49 | console.log('transition end...'); 50 | }); 51 | }, 100); 52 | 53 | let changed = false; 54 | 55 | function toggle() { 56 | const t = document.getElementById('t'); 57 | changed = !changed; 58 | 59 | if (changed) { 60 | t.className = 'box active'; 61 | } else { 62 | t.className = 'box'; 63 | } 64 | } 65 | 66 | const Demo = () => ( 67 |
68 | 69 |
70 | 71 |
72 | ); 73 | 74 | Demo.story = 'events'; 75 | 76 | storiesOf(Demo.story, module).add('demo', () => ); 77 | 78 | export default Demo; 79 | -------------------------------------------------------------------------------- /stories/simple.js: -------------------------------------------------------------------------------- 1 | import anim from '../src'; 2 | import React from 'react'; 3 | import ReactDOM from 'react-dom'; 4 | import { storiesOf } from '@storybook/react'; 5 | const style = ` 6 | 7 | .box { 8 | background:red; 9 | width:100px; 10 | height:100px; 11 | } 12 | .fade-enter { 13 | opacity: 0; 14 | animation-duration: 0.3s; 15 | animation-fill-mode: both; 16 | animation-timing-function: cubic-bezier(0.55, 0, 0.55, 0.2); 17 | animation-play-state: paused; 18 | } 19 | 20 | .fade-leave { 21 | animation-duration: 0.3s; 22 | animation-fill-mode: both; 23 | animation-timing-function: cubic-bezier(0.55, 0, 0.55, 0.2); 24 | animation-play-state: paused; 25 | } 26 | 27 | .fade-enter.fade-enter-active { 28 | animation-name: rcDialogFadeIn; 29 | animation-play-state: running; 30 | } 31 | 32 | .fade-leave.fade-leave-active { 33 | animation-name: rcDialogFadeOut; 34 | animation-play-state: running; 35 | } 36 | 37 | @keyframes rcDialogFadeIn { 38 | 0% { 39 | opacity: 0; 40 | } 41 | 100% { 42 | opacity: 1; 43 | } 44 | } 45 | 46 | @keyframes rcDialogFadeOut { 47 | 0% { 48 | opacity: 1; 49 | } 50 | 100% { 51 | opacity: 0; 52 | } 53 | } 54 | `; 55 | 56 | let show = true; 57 | 58 | function toggle() { 59 | const t = document.getElementById('t'); 60 | const b = document.getElementById('b'); 61 | b.disabled = true; 62 | t.style.visibility = ''; 63 | anim(t, `fade-${show ? 'leave' : 'enter'}`, () => { 64 | t.style.visibility = show ? '' : 'hidden'; 65 | b.disabled = false; 66 | }); 67 | show = !show; 68 | } 69 | 70 | const Demo = () => ( 71 |
72 | 73 |
74 | 77 |
78 | ); 79 | 80 | Demo.story = 'simple'; 81 | 82 | storiesOf(Demo.story, module).add('demo', () => ); 83 | 84 | export default Demo; 85 | -------------------------------------------------------------------------------- /stories/style.js: -------------------------------------------------------------------------------- 1 | import anim from '../src'; 2 | import React from 'react'; 3 | import ReactDOM from 'react-dom'; 4 | import { storiesOf } from '@storybook/react'; 5 | 6 | const style = ` 7 | .box { 8 | background:red; 9 | width:100px; 10 | height:100px; 11 | } 12 | `; 13 | 14 | let show = true; 15 | 16 | function toggle() { 17 | const t = document.getElementById('t'); 18 | const b = document.getElementById('b'); 19 | b.disabled = true; 20 | t.style.visibility = ''; 21 | t.style.opacity = show ? 1 : 0; 22 | anim.setTransition(t, 'opacity 2s ease-in'); 23 | anim.style( 24 | t, 25 | show 26 | ? { 27 | opacity: 0, 28 | } 29 | : { 30 | opacity: 1, 31 | }, 32 | () => { 33 | t.style.visibility = show ? '' : 'hidden'; 34 | b.disabled = false; 35 | anim.setTransition(t, ''); 36 | }, 37 | ); 38 | show = !show; 39 | } 40 | 41 | const Demo = () => ( 42 |
43 | 44 |
45 | 48 |
49 | ); 50 | 51 | Demo.story = 'style'; 52 | 53 | storiesOf(Demo.story, module).add('demo', () => ); 54 | 55 | export default Demo; 56 | --------------------------------------------------------------------------------