├── .gitignore ├── .npmignore ├── README.md ├── bower.json ├── build ├── canvas.js ├── demo.js └── fps.js ├── examples ├── common │ └── examples.css ├── demo │ ├── Demo.js │ ├── DemoCanvas.js │ ├── DemoCanvasNoState.js │ ├── app.js │ ├── canvas.html │ ├── canvas.js │ └── index.html └── fps │ ├── app.js │ └── index.html ├── gulpfile.js ├── lib ├── Animate.js ├── Easing.js ├── Loop.js └── ReactStateAnimation.js ├── package.json ├── src ├── Animate.js ├── Loop.js └── ReactStateAnimation.js └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .idea -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | build 2 | src 3 | node_modules 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-state-animation 2 | 3 | react-state-animation provides a capability to update React component's state value by requestAnimationFrame with a simple APIs that builds on d3-ease. The file size is just **4KB** (minified). 4 | 5 | This works with regular [React](http://facebook.github.io/react/) component and [React Canvas](https://github.com/Flipboard/react-canvas) 6 | 7 | ## Installation 8 | `npm install react-state-animation --save` 9 | Include the module by CommonJS way 10 | `import ReactStateAnimation from 'react-state-animation'` or `var ReactStateAnimation = require('react-state-animation');` 11 | 12 | This will require ES5 modules converted by babel. ES6 sources are in /src and converted ES5 modules are located in /lib. 13 | 14 | ##Demo 15 | [http://tejitak.github.io/react-state-animation/examples/demo/](http://tejitak.github.io/react-state-animation/examples/demo/) 16 | 17 | ## API 18 | 19 | - linearIn(*stateProp*, *endStateValue*, *duration*) 20 | - linearOut(*stateProp*, *endStateValue*, *duration*) 21 | - linearInOut(*stateProp*, *endStateValue*, *duration*) 22 | - quadIn(*stateProp*, *endStateValue*, *duration*) 23 | - quadOut(*stateProp*, *endStateValue*, *duration*) 24 | - quadInOut(*stateProp*, *endStateValue*, *duration*) 25 | - cubicIn(*stateProp*, *endStateValue*, *duration*) 26 | - cubicOut(*stateProp*, *endStateValue*, *duration*) 27 | - cubicInOut(*stateProp*, *endStateValue*, *duration*) 28 | - polyIn(*stateProp*, *endStateValue*, *duration*) 29 | - polyOut(*stateProp*, *endStateValue*, *duration*) 30 | - polyInOut(*stateProp*, *endStateValue*, *duration*) 31 | - sinIn(*stateProp*, *endStateValue*, *duration*) 32 | - sinOut(*stateProp*, *endStateValue*, *duration*) 33 | - sinInOut(*stateProp*, *endStateValue*, *duration*) 34 | - expIn(*stateProp*, *endStateValue*, *duration*) 35 | - expOut(*stateProp*, *endStateValue*, *duration*) 36 | - expInOut(*stateProp*, *endStateValue*, *duration*) 37 | - circleIn(*stateProp*, *endStateValue*, *duration*) 38 | - circleOut(*stateProp*, *endStateValue*, *duration*) 39 | - circleInOut(*stateProp*, *endStateValue*, *duration*) 40 | - bounceIn(*stateProp*, *endStateValue*, *duration*) 41 | - bounceOut(*stateProp*, *endStateValue*, *duration*) 42 | - bounceInOut(*stateProp*, *endStateValue*, *duration*) 43 | - backIn(*stateProp*, *endStateValue*, *duration*) 44 | - backOut(*stateProp*, *endStateValue*, *duration*) 45 | - backInOut(*stateProp*, *endStateValue*, *duration*) 46 | - elasticIn(*stateProp*, *endStateValue*, *duration*) 47 | - elasticOut(*stateProp*, *endStateValue*, *duration*) 48 | - elasticInOut(*stateProp*, *endStateValue*, *duration*) 49 | 50 | The above API returns Promise, so you can chain additinal processes by using then. 51 | 52 | ##Usage 53 | ### Example 1. Use outside of component 54 | ```js:app.js 55 | var yourComponent = React.render( 56 | , 57 | document.getElementById('demo') 58 | ) 59 | var reactStateAnimation = new ReactStateAnimation(yourComponent) 60 | // your component's state 'x' will be updated to 350 with linear order in 1 sec, then alpha will be 0 on end of moving 61 | reactStateAnimation.linearInOut('x', 350/*end value*/, 1000/*duration(ms)*/).then(() => reactStateAnimation.linearInOut('alpha', 0, 400)) 62 | ``` 63 | 64 | ### Example 2. Linear Move in React Component 65 | Set any state (e.g. 'x') associated with position left style 66 | ```js:Demo.js 67 | import React from 'react' 68 | import ReactStateAnimation from 'react-state-animation' 69 | 70 | export default class Demo extends React.Component { 71 | constructor(props) { 72 | super(props) 73 | this.state = { 74 | x: 0 75 | } 76 | // react state animation wrapper 77 | this._animate = new ReactStateAnimation(this) 78 | } 79 | 80 | start() { 81 | // start animation 82 | this._animate.linearInOut('x', 350/*end value*/, 1000/*duration(ms)*/) 83 | } 84 | 85 | stop() { 86 | this._animate.stop() 87 | } 88 | 89 | getStyle() { 90 | return { 91 | position: 'absolute', 92 | backgroundColor: "#009688", 93 | top: 0, 94 | left: this.state.x + "px", 95 | width: this.props.width, 96 | height: this.props.height 97 | } 98 | } 99 | 100 | render() { 101 | return ( 102 |
103 | ) 104 | } 105 | } 106 | 107 | Demo.defaultProps = { 108 | width: 50, 109 | height: 50 110 | } 111 | ``` 112 | 113 | ### Example 3. Linear Move in React Canvas 114 | Set any state (e.g. 'x') associated with position left style 115 | ```js:DemoCanvas.js 116 | import React from 'react' 117 | import ReactCanvas from 'react-canvas' 118 | import ReactStateAnimation from 'react-state-animation' 119 | 120 | var Surface = ReactCanvas.Surface 121 | var Group = ReactCanvas.Group 122 | var Layer = ReactCanvas.Layer 123 | 124 | export default class DemoCanvas extends React.Component { 125 | constructor(props) { 126 | super(props) 127 | this.state = { 128 | x: 0 129 | } 130 | // react state animation wrapper 131 | this._animate = new ReactStateAnimation(this) 132 | } 133 | 134 | start() { 135 | // start animation 136 | this._animate.linearInOut('x', 350/*end value*/, 1000/*duration*/) 137 | } 138 | 139 | stop() { 140 | this._animate.stop() 141 | } 142 | 143 | getGroupStyle() { 144 | return { 145 | position: 'absolute', 146 | backgroundColor: "#f4f4f4", 147 | top: 0, 148 | left: 0, 149 | width: this.props.canvasWidth, 150 | height: this.props.canvasHeight 151 | } 152 | } 153 | 154 | getStyle() { 155 | return { 156 | position: 'absolute', 157 | backgroundColor: "#009688", 158 | top: 0, 159 | left: this.state.x, 160 | width: this.props.width, 161 | height: this.props.height 162 | } 163 | } 164 | 165 | render() { 166 | return ( 167 | 168 | 169 | 170 | 171 | 172 | ) 173 | } 174 | } 175 | 176 | DemoCanvas.defaultProps = { 177 | canvasWidth: 400, 178 | canvasHeight: 50, 179 | width: 50, 180 | height: 50 181 | } 182 | ``` 183 | 184 | ### Example 4. Multiple states in ReactART 185 | Set any state (e.g. 'x') associated with position left style 186 | ```js:DemoCanvas.js 187 | 188 | import React from 'react' 189 | import ReactART from 'react-art' 190 | import Circle from 'react-art/lib/Circle.art.js' 191 | import ReactStateAnimation from 'react-state-animation' 192 | 193 | var Surface = ReactCanvas.Surface 194 | 195 | export default class DemoCanvas extends React.Component { 196 | constructor(props) { 197 | super(props) 198 | this.state = { 199 | x: props.position.x, 200 | y: props.position.y, 201 | radius: props.radius 202 | } 203 | // react state animation wrapper 204 | this._animate = new ReactStateAnimation(this) 205 | } 206 | 207 | _Floating() { 208 | // pass an array to the manimate method with the states, to be animated 209 | this._animate.manimate([ 210 | /* state: 'theNameOfTheState', target: endValue */ 211 | {state: 'x', target: this.props.position.x-15}, 212 | {state: 'radius', target: this.props.radius+4} 213 | ], 800, 'elasticInOut') 214 | .then(() => this._animate.manimate([ 215 | {state: 'x', target: this.props.position.x}, 216 | {state: 'radius', target: this.props.radius-2} 217 | ], 800, 'elasticInOut')) 218 | .then(() => this._Floating()); 219 | } 220 | 221 | render() { 222 | return ( 223 | 224 | 225 | 226 | ) 227 | } 228 | } 229 | 230 | DemoCanvas.defaultProps = { 231 | canvasWidth: 400, 232 | canvasHeight: 300, 233 | radius: 30, 234 | position: { 235 | x: 50, 236 | y: 50 237 | } 238 | } 239 | ``` 240 | 241 | ## Note 242 | React setState is now asynchronously called as a batch. So, using regular instance properties instaed of state seems faste especially for React Canvas. 243 | 244 | Please check the [demo for canvas performance between React Canvas with setState (asynchronous and batch) and without setStates](http://tejitak.github.io/react-state-animation/examples/demo/canvas.html) 245 | 246 | 247 | ## Development 248 | 249 | 1. Run "npm install" 250 | 2. Run "gulp" 251 | 3. Access to "http://localhost:8080/html/" 252 | 253 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-state-animation", 3 | "main": "index.js", 4 | "version": "0.0.1", 5 | "homepage": "https://github.com/tejitak/react-state-animation", 6 | "authors": [ 7 | "tejitak" 8 | ], 9 | "description": "Simple animations with React state", 10 | "keywords": [ 11 | "react", 12 | "animation", 13 | "tween", 14 | "transition", 15 | "state", 16 | "easing" 17 | ], 18 | "license": "MIT", 19 | "ignore": [ 20 | "**/.*", 21 | "examples", 22 | "index.js", 23 | "node_modules", 24 | "bower_components", 25 | "test", 26 | "tests" 27 | ] 28 | } -------------------------------------------------------------------------------- /examples/common/examples.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | margin: 0; 3 | font-family: 'Roboto', sans-serif; 4 | color: #101010; 5 | } 6 | 7 | h1, h2, h3 { 8 | color: #333; 9 | font-size: 33px; 10 | font-weight: normal; 11 | } 12 | 13 | h2 { 14 | display: none; 15 | font-size: 33px; 16 | } 17 | 18 | h3 { 19 | border-bottom: 3px solid #eee; 20 | font-size: 18px; 21 | padding: 5px 0; 22 | margin: 20px 0 10px; 23 | font-weight: bold; 24 | } 25 | 26 | h4 { 27 | margin: 12px 0; 28 | } 29 | 30 | header { 31 | background-color: #009688; 32 | width: 100%; 33 | box-shadow: 0 1px 5px rgba(0,0,0,.3); 34 | color: #fff; 35 | font-size: 2em; 36 | font-weight: bold; 37 | padding: 20px 0; 38 | text-align: center; 39 | } 40 | 41 | footer { 42 | background-color: #009688; 43 | width: 100%; 44 | box-shadow: 0 1px 5px rgba(0,0,0,.3); 45 | color: #fff; 46 | text-align: center; 47 | padding: 8px 0; 48 | margin-top: 12px; 49 | } 50 | 51 | section { 52 | margin: 8px 0; 53 | } 54 | 55 | .wrap { 56 | padding: 20px 50px; 57 | } 58 | 59 | .description { 60 | text-align: center; 61 | margin: 20px; 62 | } 63 | 64 | .social_container { 65 | width: 300px; 66 | margin: 4px auto; 67 | text-align: center; 68 | } 69 | 70 | .social_container .social_item{ 71 | display: inline-block; 72 | padding: 4px 20px 73 | } 74 | 75 | @media (min-width: 481px) { 76 | .social_container { 77 | width: 100%; 78 | } 79 | } 80 | 81 | .separator { 82 | margin: 30px 0px; 83 | height: 3px; 84 | background: #eaeaea; 85 | border-radius: 2px; 86 | } 87 | 88 | .container { 89 | position: relative; 90 | width: 400px; 91 | height: 50px; 92 | background-color: #f4f4f4; 93 | margin-bottom: 10px; 94 | } 95 | 96 | .floatContainer { 97 | height: 110px; 98 | width: 540px; 99 | margin-top: 10px; 100 | } 101 | 102 | .floatLeftItem { 103 | float: left; 104 | width: 90px; 105 | height: 100px; 106 | padding: 0px 20px 0 0; 107 | } 108 | 109 | .floatRightItem { 110 | float: left; 111 | width: 400px; 112 | height: 100px; 113 | } -------------------------------------------------------------------------------- /examples/demo/Demo.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactStateAnimation from 'react-state-animation' 3 | 4 | export default class Demo extends React.Component { 5 | constructor(props) { 6 | super(props) 7 | // initialize state 8 | this.state = { 9 | x: 0, 10 | alpha: 1 11 | } 12 | // this.x = 0 13 | // this.alpha = 1 14 | // react state animation wrapper 15 | this._animate = new ReactStateAnimation(this) 16 | } 17 | 18 | start() { 19 | this._animate[this.props.easing]('x', 350/*end value*/, 1000/*duration(ms)*/) 20 | .then(() => this._animate[this.props.easing]('alpha', 0, 500)) 21 | } 22 | 23 | stop() { 24 | this._animate.stop() 25 | } 26 | 27 | getStyle() { 28 | return { 29 | position: 'absolute', 30 | backgroundColor: "#009688", 31 | top: 0, 32 | // opacity: this.alpha, 33 | // left: this.x + "px", 34 | opacity: this.state.alpha, 35 | left: this.state.x + "px", 36 | width: this.props.width, 37 | height: this.props.height 38 | } 39 | } 40 | 41 | render() { 42 | return ( 43 |
44 | ) 45 | } 46 | } 47 | 48 | Demo.defaultProps = { 49 | width: 50, 50 | height: 50, 51 | easing: '' 52 | } -------------------------------------------------------------------------------- /examples/demo/DemoCanvas.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactCanvas from 'react-canvas' 3 | import ReactStateAnimation from 'react-state-animation' 4 | 5 | var Surface = ReactCanvas.Surface 6 | var Group = ReactCanvas.Group 7 | var Layer = ReactCanvas.Layer 8 | 9 | export default class DemoCanvas extends React.Component { 10 | constructor(props) { 11 | super(props) 12 | // initialize state 13 | this.state = { 14 | x: 0, 15 | alpha: 1 16 | } 17 | // react state animation wrapper 18 | this._animate = new ReactStateAnimation(this) 19 | } 20 | 21 | start() { 22 | this._animate[this.props.easing]('x', 350/*end value*/, 1000/*duration*/) 23 | .then(() => this._animate[this.props.easing]('alpha', 0, 400)) 24 | } 25 | 26 | stop() { 27 | this._animate.stop() 28 | } 29 | 30 | getGroupStyle() { 31 | return { 32 | position: 'absolute', 33 | backgroundColor: "#f4f4f4", 34 | top: 0, 35 | left: 0, 36 | width: this.props.canvasWidth, 37 | height: this.props.canvasHeight 38 | } 39 | } 40 | 41 | getStyle() { 42 | return { 43 | position: 'absolute', 44 | backgroundColor: "#009688", 45 | top: 0, 46 | alpha: this.state.alpha, 47 | left: this.state.x, 48 | width: this.props.width, 49 | height: this.props.height 50 | } 51 | } 52 | 53 | render() { 54 | return ( 55 | 56 | 57 | 58 | 59 | 60 | ) 61 | } 62 | } 63 | 64 | DemoCanvas.defaultProps = { 65 | canvasWidth: 400, 66 | canvasHeight: 50, 67 | width: 50, 68 | height: 50, 69 | easing: '' 70 | } -------------------------------------------------------------------------------- /examples/demo/DemoCanvasNoState.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import DemoCanvas from './DemoCanvas' 3 | 4 | export default class DemoCanvasNoState extends DemoCanvas { 5 | constructor(props) { 6 | super(props) 7 | // initialize state 8 | this.state = {} 9 | // replace state to instance props 10 | this.x = 0 11 | this.alpha = 1 12 | } 13 | 14 | getStyle() { 15 | return { 16 | position: 'absolute', 17 | backgroundColor: "#009688", 18 | top: 0, 19 | // replace state to instance props 20 | alpha: this.alpha, 21 | left: this.x, 22 | width: this.props.width, 23 | height: this.props.height 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /examples/demo/app.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Demo from './Demo' 3 | import DemoCanvas from './DemoCanvas' 4 | 5 | var d = document, 6 | components = [], 7 | easingTypes = ['linear', 'easeInQuad', 'easeInCubic', 'easeOutCubic', 'easeInOutCubic'] 8 | 9 | // set up DOM for each easing types 10 | easingTypes.forEach((easing, i) => { 11 | var div = d.createElement("DIV"), 12 | html = ['

', easing, "

", 13 | "reactStateAnimation." + easing + "('x', 350/*end value*/, 1000/*duration(ms)*/).then(() => reactStateAnimation." + easing + "('alpha', 0, 400))", 14 | '
', 15 | '

React Component

React Canvas

', 16 | '
', 17 | '
'] 18 | div.innerHTML = html.join("") 19 | // render React component 20 | components.push(React.render( 21 | , 22 | div.querySelector('.container') 23 | )) 24 | // render React Canvas 25 | components.push(React.render( 26 | , 27 | div.querySelector('.canvasContainer') 28 | )) 29 | d.getElementById('demo').appendChild(div) 30 | }) 31 | 32 | // action buttons 33 | var start = () => { 34 | components.forEach(c => { 35 | c.setState({x: 0, alpha: 1}, () => { 36 | c.start() 37 | }) 38 | }) 39 | } 40 | 41 | var resume = () => { 42 | components.forEach(c => { 43 | c.start() 44 | }) 45 | } 46 | 47 | var stop = () => { 48 | components.forEach(c => { 49 | c.stop() 50 | }) 51 | } 52 | 53 | d.getElementById('startBtn1').addEventListener('click', start) 54 | d.getElementById('startBtn2').addEventListener('click', start) 55 | d.getElementById('resumeBtn1').addEventListener('click', resume) 56 | d.getElementById('resumeBtn2').addEventListener('click', resume) 57 | d.getElementById('stopBtn1').addEventListener('click', stop) 58 | d.getElementById('stopBtn2').addEventListener('click', stop) -------------------------------------------------------------------------------- /examples/demo/canvas.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ReactStateAnimation Demo for canvas setState performance 7 | 8 | 9 | 10 | 11 |
react-state-animation for canvas performance
12 |
13 |
14 |
15 | react-state-animation provides a capability to update React component's state by requestAnimationFrame with a simple APIs. 16 |
17 |
18 | This works with regular React component and React Canvas. View on GitHub 19 |
20 |
21 |
22 |
23 | 24 | 25 | 26 | This demo is for performance check between React Canvas with setState (asynchronous and batch) and without setStates. Press start to check animation 27 |
28 |
29 |
30 | 31 | 32 | 33 |
34 |
35 |
36 |
37 |
38 | 61 |
Design and Implemented by tejitak
62 |
63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /examples/demo/canvas.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Demo from './Demo' 3 | import DemoCanvas from './DemoCanvas' 4 | import DemoCanvasNoState from './DemoCanvasNoState' 5 | 6 | var d = document, 7 | components = [], 8 | easingTypes = ['linear', 'easeInQuad', 'easeInCubic', 'easeOutCubic', 'easeInOutCubic'] 9 | 10 | // set up DOM for each easing types 11 | easingTypes.forEach((easing, i) => { 12 | var div = d.createElement("DIV"), 13 | html = ['

', easing, "

", 14 | "reactStateAnimation." + easing + "('x', 350/*end value*/, 1000/*duration(ms)*/).then(() => reactStateAnimation." + easing + "('alpha', 0, 400))", 15 | '
', 16 | '

Canvas w/ setState

Canvas w/o setState

', 17 | '
', 18 | '
'] 19 | div.innerHTML = html.join("") 20 | // render React component 21 | components.push(React.render( 22 | , 23 | div.querySelector('.with') 24 | )) 25 | // render React Canvas 26 | components.push(React.render( 27 | , 28 | div.querySelector('.without') 29 | )) 30 | d.getElementById('demo').appendChild(div) 31 | }) 32 | 33 | // action buttons 34 | d.getElementById('startBtn').addEventListener('click', () => { 35 | components.forEach(c => { 36 | if(c.state.x) { 37 | c.setState({x: 0, alpha: 1}, () => { 38 | c.start() 39 | }) 40 | }else{ 41 | c.x = 0 42 | c.alpha = 1 43 | c.start() 44 | } 45 | }) 46 | }) 47 | 48 | d.getElementById('resumeBtn').addEventListener('click', () => { 49 | components.forEach(c => { 50 | c.start() 51 | }) 52 | }) 53 | 54 | d.getElementById('stopBtn').addEventListener('click', () => { 55 | components.forEach(c => { 56 | c.stop() 57 | }) 58 | }) 59 | 60 | 61 | -------------------------------------------------------------------------------- /examples/demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ReactStateAnimation Demo 7 | 8 | 9 | 10 | 11 |
react-state-animation
12 |
13 |
14 |
15 | react-state-animation provides a capability to update React component's state by requestAnimationFrame with a simple APIs. The file size is just 4KB (minified). 16 |
17 |
18 | This works with regular React component and React Canvas. View on GitHub 19 |
20 |
21 |
22 |
23 | 24 | 25 | 26 | The following React components have state {x: 0} as an initial value. And the state is used in style. Press start to check animation 27 |
28 |
29 |
30 | 31 | 32 | 33 |
34 |
35 |
36 |
37 |
38 | 39 | 40 | 41 |
42 |
43 |
44 |
45 | 68 |
Design and Implemented by tejitak
69 |
70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /examples/fps/app.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactStateAnimation from 'react-state-animation' 3 | import Demo from '../demo/Demo' 4 | import DemoCanvas from '../demo/DemoCanvas' 5 | 6 | var d = document, 7 | demo = React.render( 8 | , 9 | d.getElementById('demo') 10 | ), 11 | anim = new ReactStateAnimation(demo), 12 | demoDuration = 1000 13 | 14 | anim.onProcess = () => { 15 | stats.update(); 16 | } 17 | 18 | d.getElementById('startDemoBtn').addEventListener('click', () => { 19 | // demo.setState({x: 0}, () => { 20 | // anim.linear('x', 350/*end value*/, demoDuration) 21 | // }) 22 | demo.x = 0; 23 | anim.linear('x', 350/*end value*/, demoDuration) 24 | }) 25 | 26 | d.getElementById('durationDemoInput').addEventListener('change', (e) => { 27 | demoDuration = e.target.value - 0 28 | }) 29 | 30 | var demoCanvas = React.render( 31 | , 32 | d.getElementById('demoCanvas') 33 | ), 34 | animCanvas = new ReactStateAnimation(demoCanvas), 35 | demoCanvasDuration = 1000 36 | 37 | animCanvas.onProcess = () => { 38 | stats.update(); 39 | } 40 | 41 | d.getElementById('startDemoCanvasBtn').addEventListener('click', () => { 42 | demoCanvas.x = 0; 43 | animCanvas.linear('x', 350/*end value*/, demoCanvasDuration) 44 | // demoCanvas.setState({x: 0}, () => { 45 | // animCanvas.linear('x', 350/*end value*/, demoCanvasDuration) 46 | // }) 47 | }) 48 | 49 | d.getElementById('durationDemoCanvasInput').addEventListener('change', (e) => { 50 | demoCanvasDuration = e.target.value - 0 51 | }) -------------------------------------------------------------------------------- /examples/fps/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ReactStateAnimation Demo 7 | 8 | 9 | 10 | 11 |
react-state-animation
12 |
13 |
14 |

React Component

15 |
16 |
Duration:
17 | 18 |
19 |
20 |
21 |
22 |
23 |

React Canvas

24 |
25 |
Duration:
26 | 27 |
28 |
29 |
30 |
31 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var del = require('del'); 3 | var argv = require('yargs').argv; 4 | var gulpif = require('gulp-if'); 5 | var connect = require('gulp-connect'); 6 | var uglify = require('gulp-uglify'); 7 | var babel = require('gulp-babel'); 8 | var webpack = require('gulp-webpack'); 9 | var webpackConfig = require('./webpack.config.js'); 10 | 11 | var port = process.env.PORT || 8080; 12 | var reloadPort = process.env.RELOAD_PORT || 35729; 13 | 14 | gulp.task('clean', function () { 15 | return del.sync(['lib', 'build']); 16 | }); 17 | 18 | // convert to es5 from es6 src modules with babel 19 | gulp.task('lib', function (cb) { 20 | return gulp.src('./src/**/*.js') 21 | .pipe(babel()) 22 | .pipe(gulp.dest('lib/')); 23 | }); 24 | 25 | // build for examples with webpack 26 | gulp.task('build', ['lib'], function () { 27 | return gulp.src(webpackConfig.entry.demo[0]) 28 | .pipe(webpack(webpackConfig)) 29 | .pipe(gulpif(argv.production, uglify())) 30 | .pipe(gulp.dest('build/')); 31 | }); 32 | 33 | gulp.task('serve', function () { 34 | connect.server({ 35 | port: port, 36 | livereload: { 37 | port: reloadPort 38 | } 39 | }); 40 | }); 41 | 42 | gulp.task('reload-js', function () { 43 | return gulp.src('./build/*.js') 44 | .pipe(connect.reload()); 45 | }); 46 | 47 | gulp.task('watch', function () { 48 | gulp.watch(['./build/*.js'], ['reload-js']); 49 | }); 50 | 51 | gulp.task('default', ['clean', 'lib', 'build', 'serve', 'watch']); 52 | -------------------------------------------------------------------------------- /lib/Animate.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequire = function (obj) { return obj && obj.__esModule ? obj["default"] : obj; }; 4 | 5 | var _createClass = (function () { function defineProperties(target, props) { for (var key in props) { var prop = props[key]; prop.configurable = true; if (prop.value) prop.writable = true; } Object.defineProperties(target, props); } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 6 | 7 | var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; 8 | 9 | var Loop = _interopRequire(require("./Loop")); 10 | 11 | var ease = require("d3-ease").ease; 12 | 13 | var eases = ["linear-in", "linear-out", "linear-in-out", "quad-in", "quad-out", "quad-in-out", "cubic-in", "cubic-out", "cubic-in-out", "poly-in", "poly-out", "poly-in-out", "sin-in", "sin-out", "sin-in-out", "exp-in", "exp-out", "exp-in-out", "circle-in", "circle-out", "circle-in-out", "bounce-in", "bounce-out", "bounce-in-out", "back-in", "back-out", "back-in-out", "elastic-in", "elastic-out", "elastic-in-out"], 14 | Easing = {}; 15 | 16 | /** 17 | * React state animation wrapper 18 | * - update state value by requestAnimationFrame loop 19 | */ 20 | 21 | var Animate = (function () { 22 | function Animate(component) { 23 | var _this = this; 24 | 25 | _classCallCheck(this, Animate); 26 | 27 | this._component = component; 28 | 29 | // generate an interface function for each ease. 30 | eases.forEach(function (e) { 31 | // convert to camelCase 32 | var easeName = e.replace(/-([a-z])/g, function (g) { 33 | return g[1].toUpperCase(); 34 | }); 35 | 36 | // add instance methods dynamically 37 | _this[easeName] = function (prop, end, duration) { 38 | return this.animate(prop, end, duration, easeName); 39 | }; 40 | 41 | Easing[easeName] = ease(e); 42 | }); 43 | } 44 | 45 | _createClass(Animate, { 46 | _getStateValue: { 47 | 48 | /** 49 | * Get state value 50 | * if the prop is not in state regular property 51 | */ 52 | 53 | value: function _getStateValue(prop) { 54 | var c = this._component, 55 | v = c.state && c.state[prop]; 56 | return v === undefined ? c[prop] : v; 57 | } 58 | }, 59 | _updateStateValue: { 60 | 61 | /** 62 | * Set value to state 63 | * if the prop is not in state, set value to regular property with force update 64 | */ 65 | 66 | value: function _updateStateValue(prop, v) { 67 | var _this = this; 68 | 69 | return new Promise(function (resolve, reject) { 70 | var c = _this._component; 71 | if (c.state && c.state[prop] !== undefined) { 72 | var state = {}; 73 | state[prop] = v; 74 | c.setState(state, resolve); 75 | } else { 76 | c[prop] = v; 77 | c.forceUpdate(); 78 | resolve(); 79 | } 80 | }); 81 | } 82 | }, 83 | _updateStateMap: { 84 | 85 | /** 86 | * Updates multiple properties within 87 | * @param prop {Array} array of targeted states= {state: {string}, target: {number}} 88 | * @param values {Array} array of values to be set 89 | * @returns {Promise} 90 | */ 91 | 92 | value: function _updateStateMap(prop, values) { 93 | var _this = this; 94 | 95 | return new Promise(function (resolve, reject) { 96 | var c = _this._component, 97 | state = {}; 98 | // build up changed state 99 | for (var i = 0; i < prop.length; i++) { 100 | state[prop[i].state] = values[i]; 101 | } 102 | c.setState(state, resolve); 103 | }); 104 | } 105 | }, 106 | _start: { 107 | value: function _start(loopCallback) { 108 | this._loop = new Loop(loopCallback); 109 | this._loop.start(); 110 | } 111 | }, 112 | animate: { 113 | value: function animate(prop, end, duration, easing) { 114 | var _this = this; 115 | 116 | if (!Easing[easing]) { 117 | console.log("Specified easing does not exist: " + easing); 118 | return; 119 | } 120 | return new Promise(function (resolve, reject) { 121 | var begin = _this._getStateValue(prop); 122 | _this._start(function () { 123 | return _this._anim(prop, begin, end, duration, easing, resolve); 124 | }); 125 | }); 126 | } 127 | }, 128 | manimate: { 129 | value: function manimate(prop, duration, easing) { 130 | var _this = this; 131 | 132 | if (!Easing[easing]) { 133 | console.log("Specified easing does not exist: " + easing); 134 | return; 135 | } 136 | return new Promise(function (resolve, reject) { 137 | // gather array begin States 138 | var begins = [], 139 | ends = []; 140 | for (var i = 0; i < prop.length; i++) { 141 | var b = _this._getStateValue(prop[i].state); 142 | var e = prop[i].target; 143 | begins.push(b); 144 | ends.push(e); 145 | } 146 | // start multi-anim 147 | _this._start(function () { 148 | return _this._multianim(prop, begins, ends, duration, easing, resolve); 149 | }); 150 | }); 151 | } 152 | }, 153 | onProcess: { 154 | 155 | // for override on each loop 156 | 157 | value: function onProcess(prop, value, progress) {} 158 | }, 159 | _anim: { 160 | 161 | /** 162 | * Start animation 163 | * - prop is a react state property 164 | * - end is a goal value of the state 165 | */ 166 | 167 | value: function _anim(prop, begin, end, duration, easing, resolve) { 168 | if (!this._loop) { 169 | resolve(); 170 | return false; 171 | } 172 | var progress = Easing[easing](this._loop.timeDiff() / duration), 173 | distance = Math.abs(begin - end), 174 | diff = progress * distance, 175 | operator = begin > end ? -1 : 1, 176 | value = begin + diff * operator; 177 | this.onProcess(prop, value, progress); 178 | if (progress < 1) { 179 | // return promise to keep loop 180 | return this._updateStateValue(prop, value); 181 | } else { 182 | this.stop(); 183 | this._updateStateValue(prop, end).then(function () { 184 | resolve(); 185 | }); 186 | return false; 187 | } 188 | } 189 | }, 190 | _multianim: { 191 | value: function _multianim(prop, begins, ends, duration, easing, resolve) { 192 | if (!this._loop) { 193 | resolve(); 194 | return false; 195 | } 196 | var progress = Easing[easing](this._loop.timeDiff() / duration), 197 | newValues = []; 198 | 199 | // step through all states 200 | for (var i = 0; i < prop.length; i++) { 201 | var begin = begins[i], 202 | end = ends[i], 203 | p = prop[i].state, 204 | distance = Math.abs(begin - end), 205 | diff = progress * distance, 206 | operator = begin > end ? -1 : 1, 207 | value = begin + diff * operator; 208 | 209 | this.onProcess(p, value, progress); 210 | 211 | newValues.push(value); 212 | } 213 | 214 | if (progress < 1) { 215 | return this._updateStateMap(prop, newValues); 216 | } else { 217 | this.stop(); 218 | this._updateStateMap(prop, ends).then(function () { 219 | resolve(); 220 | }); 221 | return false; 222 | } 223 | } 224 | }, 225 | stop: { 226 | value: function stop() { 227 | if (this._loop) { 228 | this._loop.end(); 229 | this._loop = null; 230 | } 231 | return this; 232 | } 233 | } 234 | }); 235 | 236 | return Animate; 237 | })(); 238 | 239 | module.exports = Animate; -------------------------------------------------------------------------------- /lib/Easing.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _createClass = (function () { function defineProperties(target, props) { for (var key in props) { var prop = props[key]; prop.configurable = true; if (prop.value) prop.writable = true; } Object.defineProperties(target, props); } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 4 | 5 | var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; 6 | 7 | var Easing = (function () { 8 | function Easing() { 9 | _classCallCheck(this, Easing); 10 | } 11 | 12 | _createClass(Easing, { 13 | linear: { 14 | value: function linear(t) { 15 | return t; 16 | } 17 | }, 18 | easeInQuad: { 19 | value: function easeInQuad(t) { 20 | return Math.pow(t, 2); 21 | } 22 | }, 23 | easeInCubic: { 24 | value: function easeInCubic(t) { 25 | return t * t * t; 26 | } 27 | }, 28 | easeOutCubic: { 29 | value: function easeOutCubic(t) { 30 | return --t * t * t + 1; 31 | } 32 | }, 33 | easeInOutCubic: { 34 | value: function easeInOutCubic(t) { 35 | return t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1; 36 | } 37 | } 38 | }); 39 | 40 | return Easing; 41 | })(); 42 | 43 | var instance = new Easing(); 44 | 45 | module.exports = instance; -------------------------------------------------------------------------------- /lib/Loop.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _createClass = (function () { function defineProperties(target, props) { for (var key in props) { var prop = props[key]; prop.configurable = true; if (prop.value) prop.writable = true; } Object.defineProperties(target, props); } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 4 | 5 | var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; 6 | 7 | /* 8 | * Loop utility using requestAnimationFrame 9 | */ 10 | var w = global.window || 0, 11 | raf = w.requestAnimationFrame || w.webkitRequestAnimationFrame || w.mozRequestAnimationFrame || w.msRequestAnimationFrame || w.oRequestAnimationFrame || function (c) { 12 | global.setTimeout(c, 1000 / 60); 13 | }, 14 | caf = w.cancelAnimationFrame || w.webkitCancelAnimationFrame || w.mozCancelAnimationFrame || w.msCancelAnimationFrame || w.oCancelAnimationFrame || global.clearTimeout; 15 | 16 | var Loop = (function () { 17 | function Loop(callback) { 18 | _classCallCheck(this, Loop); 19 | 20 | this._callback = callback; 21 | } 22 | 23 | _createClass(Loop, { 24 | start: { 25 | value: function start() { 26 | // keep loop while the callback returns true 27 | this._startTime = Date.now(); 28 | this._loop(); 29 | } 30 | }, 31 | _loop: { 32 | value: function _loop() { 33 | var _this = this; 34 | 35 | if (!this._callback) { 36 | return; 37 | } 38 | var keep = this._callback(); 39 | if (keep) { 40 | var exec = function () { 41 | _this._timer = raf(_this._loop.bind(_this)); 42 | }; 43 | // handle promise 44 | if (keep.then) { 45 | keep.then(exec); 46 | } else { 47 | exec(); 48 | } 49 | } 50 | } 51 | }, 52 | end: { 53 | value: function end() { 54 | if (this._timer) { 55 | caf(this._timer); 56 | this._timer = null; 57 | } 58 | this._startTime = null; 59 | } 60 | }, 61 | timeDiff: { 62 | value: function timeDiff() { 63 | return Date.now() - this._startTime; 64 | } 65 | } 66 | }); 67 | 68 | return Loop; 69 | })(); 70 | 71 | module.exports = Loop; -------------------------------------------------------------------------------- /lib/ReactStateAnimation.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequire = function (obj) { return obj && obj.__esModule ? obj["default"] : obj; }; 4 | 5 | var Animate = _interopRequire(require("./Animate")); 6 | 7 | var Loop = _interopRequire(require("./Loop")); 8 | 9 | var ReactStateAnimation = Animate; 10 | ReactStateAnimation.Loop = Loop; 11 | 12 | module.exports = ReactStateAnimation; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-state-animation", 3 | "version": "0.1.1", 4 | "description": "Simple animations with React state", 5 | "main": "lib/ReactStateAnimation.js", 6 | "scripts": { 7 | "start": "./node_modules/.bin/gulp", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/tejitak/react-state-animation.git" 13 | }, 14 | "keywords": [ 15 | "react", 16 | "react-canvas", 17 | "animation", 18 | "tween", 19 | "transition", 20 | "state", 21 | "easing" 22 | ], 23 | "author": "tejitak", 24 | "license": "MIT", 25 | "bugs": { 26 | "url": "https://github.com/tejitak/react-state-animation/issues" 27 | }, 28 | "homepage": "https://github.com/tejitak/react-state-animation", 29 | "dependencies": { 30 | "d3-ease": "^0.1.3" 31 | }, 32 | "peerDependencies": { 33 | "react": ">=0.13.x" 34 | }, 35 | "devDependencies": { 36 | "babel": "^5.5.6", 37 | "babel-core": "^5.5.6", 38 | "babel-loader": "^5.1.4", 39 | "del": "^1.1.1", 40 | "envify": "^3.2.0", 41 | "gulp": "^3.8.10", 42 | "gulp-babel": "^4.0.0", 43 | "gulp-connect": "^2.2.0", 44 | "gulp-if": "^1.2.5", 45 | "gulp-uglify": "~1.0.2", 46 | "gulp-webpack": "^1.2.0", 47 | "react-canvas": "0.0.1", 48 | "webpack": "^1.7.3", 49 | "yargs": "^3.5.4" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Animate.js: -------------------------------------------------------------------------------- 1 | import Loop from './Loop' 2 | import {ease} from 'd3-ease' 3 | 4 | let eases = [ 5 | 'linear-in', 6 | 'linear-out', 7 | 'linear-in-out', 8 | 'quad-in', 9 | 'quad-out', 10 | 'quad-in-out', 11 | 'cubic-in', 12 | 'cubic-out', 13 | 'cubic-in-out', 14 | 'poly-in', 15 | 'poly-out', 16 | 'poly-in-out', 17 | 'sin-in', 18 | 'sin-out', 19 | 'sin-in-out', 20 | 'exp-in', 21 | 'exp-out', 22 | 'exp-in-out', 23 | 'circle-in', 24 | 'circle-out', 25 | 'circle-in-out', 26 | 'bounce-in', 27 | 'bounce-out', 28 | 'bounce-in-out', 29 | 'back-in', 30 | 'back-out', 31 | 'back-in-out', 32 | 'elastic-in', 33 | 'elastic-out', 34 | 'elastic-in-out' 35 | ], Easing = {}; 36 | 37 | /** 38 | * React state animation wrapper 39 | * - update state value by requestAnimationFrame loop 40 | */ 41 | export default class Animate { 42 | 43 | constructor(component) { 44 | this._component = component; 45 | 46 | // generate an interface function for each ease. 47 | eases.forEach( (e) => { 48 | // convert to camelCase 49 | var easeName = e.replace(/-([a-z])/g, function (g) { return g[1].toUpperCase(); }); 50 | 51 | // add instance methods dynamically 52 | this[easeName] = function(prop, end, duration) { 53 | return this.animate(prop, end, duration, easeName) 54 | } 55 | 56 | Easing[easeName] = ease(e) 57 | 58 | }); 59 | } 60 | 61 | /** 62 | * Get state value 63 | * if the prop is not in state regular property 64 | */ 65 | _getStateValue(prop) { 66 | var c = this._component, 67 | v = c.state && c.state[prop] 68 | return v === undefined ? c[prop] : v 69 | } 70 | 71 | /** 72 | * Set value to state 73 | * if the prop is not in state, set value to regular property with force update 74 | */ 75 | _updateStateValue(prop, v) { 76 | return new Promise((resolve, reject) => { 77 | var c = this._component 78 | if(c.state && c.state[prop] !== undefined){ 79 | var state = {} 80 | state[prop] = v 81 | c.setState(state, resolve) 82 | }else{ 83 | c[prop] = v 84 | c.forceUpdate() 85 | resolve() 86 | } 87 | }) 88 | } 89 | 90 | /** 91 | * Updates multiple properties within 92 | * @param prop {Array} array of targeted states= {state: {string}, target: {number}} 93 | * @param values {Array} array of values to be set 94 | * @returns {Promise} 95 | */ 96 | _updateStateMap(prop, values) { 97 | 98 | return new Promise((resolve, reject) => { 99 | var c = this._component, 100 | state = {}; 101 | // build up changed state 102 | for(var i = 0; i < prop.length; i++) { 103 | state[prop[i].state] = values[i]; 104 | } 105 | c.setState(state, resolve); 106 | }); 107 | } 108 | 109 | _start(loopCallback) { 110 | this._loop = new Loop(loopCallback) 111 | this._loop.start() 112 | } 113 | 114 | animate(prop, end, duration, easing) { 115 | if(!Easing[easing]) { 116 | console.log("Specified easing does not exist: " + easing) 117 | return 118 | } 119 | return new Promise((resolve, reject) => { 120 | var begin = this._getStateValue(prop) 121 | this._start(() => { 122 | return this._anim(prop, begin, end, duration, easing, resolve) 123 | }) 124 | }) 125 | } 126 | 127 | manimate(prop, duration, easing) { 128 | if (!Easing[easing]) { 129 | console.log("Specified easing does not exist: " + easing); 130 | return; 131 | } 132 | return new Promise((resolve, reject) => { 133 | // gather array begin States 134 | var begins = [], 135 | ends = []; 136 | for(var i = 0; i < prop.length; i++) { 137 | var b = this._getStateValue(prop[i].state); 138 | var e = prop[i].target; 139 | begins.push(b); 140 | ends.push(e); 141 | } 142 | // start multi-anim 143 | this._start(() => { 144 | return this._multianim(prop, begins, ends, duration, easing, resolve); 145 | }); 146 | }); 147 | } 148 | 149 | // for override on each loop 150 | onProcess(prop, value, progress) { 151 | } 152 | 153 | /** 154 | * Start animation 155 | * - prop is a react state property 156 | * - end is a goal value of the state 157 | */ 158 | _anim(prop, begin, end, duration, easing, resolve) { 159 | if(!this._loop){ 160 | resolve() 161 | return false 162 | } 163 | var progress = Easing[easing](this._loop.timeDiff() / duration), 164 | distance = Math.abs(begin - end), 165 | diff = progress * distance, 166 | operator = begin > end ? -1 : 1, 167 | value = begin + diff * operator 168 | this.onProcess(prop, value, progress) 169 | if(progress < 1) { 170 | // return promise to keep loop 171 | return this._updateStateValue(prop, value) 172 | }else{ 173 | this.stop() 174 | this._updateStateValue(prop, end).then(() => { 175 | resolve() 176 | }) 177 | return false 178 | } 179 | } 180 | 181 | _multianim(prop, begins, ends, duration, easing, resolve) { 182 | if(!this._loop) { 183 | resolve(); 184 | return false; 185 | } 186 | var progress = Easing[easing](this._loop.timeDiff() / duration), 187 | newValues = []; 188 | 189 | // step through all states 190 | for(var i = 0; i < prop.length; i++) { 191 | var begin = begins[i], 192 | end = ends[i], 193 | p = prop[i].state, 194 | distance = Math.abs(begin - end), 195 | diff = progress * distance, 196 | operator = begin > end ? -1 : 1, 197 | value = begin + diff * operator; 198 | 199 | this.onProcess(p, value, progress); 200 | 201 | newValues.push(value); 202 | } 203 | 204 | if(progress < 1) { 205 | return this._updateStateMap(prop, newValues); 206 | } else { 207 | this.stop(); 208 | this._updateStateMap(prop, ends).then(function () { 209 | resolve(); 210 | }); 211 | return false; 212 | } 213 | } 214 | 215 | stop() { 216 | if(this._loop) { 217 | this._loop.end() 218 | this._loop = null 219 | } 220 | return this 221 | } 222 | 223 | } 224 | -------------------------------------------------------------------------------- /src/Loop.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Loop utility using requestAnimationFrame 3 | */ 4 | var w = (global.window || 0), 5 | raf = w['requestAnimationFrame'] || w['webkitRequestAnimationFrame'] || w['mozRequestAnimationFrame'] || w['msRequestAnimationFrame'] || w['oRequestAnimationFrame'] || function(c){ global.setTimeout(c, 1000 / 60) }, 6 | caf = w['cancelAnimationFrame'] || w['webkitCancelAnimationFrame'] || w['mozCancelAnimationFrame'] || w['msCancelAnimationFrame'] || w['oCancelAnimationFrame'] || global.clearTimeout 7 | 8 | export default class Loop { 9 | 10 | constructor(callback) { 11 | this._callback = callback; 12 | } 13 | 14 | start() { 15 | // keep loop while the callback returns true 16 | this._startTime = Date.now() 17 | this._loop() 18 | } 19 | 20 | _loop() { 21 | if(!this._callback){ return } 22 | var keep = this._callback() 23 | if(keep) { 24 | var exec = ()=>{ 25 | this._timer = raf(this._loop.bind(this)); 26 | } 27 | // handle promise 28 | if(keep.then){ 29 | keep.then(exec) 30 | }else{ 31 | exec() 32 | } 33 | } 34 | } 35 | 36 | end() { 37 | if(this._timer) { 38 | caf(this._timer) 39 | this._timer = null 40 | } 41 | this._startTime = null 42 | } 43 | 44 | timeDiff() { 45 | return Date.now() - this._startTime 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/ReactStateAnimation.js: -------------------------------------------------------------------------------- 1 | import Animate from './Animate' 2 | import Loop from './Loop' 3 | 4 | var ReactStateAnimation = Animate 5 | ReactStateAnimation.Loop = Loop 6 | 7 | export default ReactStateAnimation -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | cache: true, 3 | 4 | watch: true, 5 | 6 | entry: { 7 | 'demo': ['./examples/demo/app.js'], 8 | 'canvas': ['./examples/demo/canvas.js'], 9 | 'fps': ['./examples/fps/app.js'] 10 | }, 11 | 12 | output: { 13 | filename: '[name].js' 14 | }, 15 | 16 | devtool: 'inline-source-map', 17 | 18 | module: { 19 | loaders: [ 20 | { test: /\.js$|\.jsx$/, exclude: /node_modules|build/, loader: 'babel-loader'} 21 | ] 22 | }, 23 | 24 | resolve: { 25 | root: __dirname, 26 | alias: { 27 | 'react-state-animation': 'src/ReactStateAnimation.js' 28 | }, 29 | extensions: ['', '.js', '.jsx'] 30 | } 31 | }; 32 | --------------------------------------------------------------------------------