├── .babelrc ├── .eslintrc ├── .gitignore ├── README.md ├── dev.js ├── example ├── app.js ├── entry.js ├── index.html └── reset.css ├── lib ├── components.js ├── ease.js ├── index.js └── native.js ├── package.json ├── src ├── components.js ├── ease.js ├── index.js └── native.js └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "stage": 0 3 | } -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "rules" :{ 4 | "quotes": [1, "single"], 5 | "no-unused-vars": [1, {"vars": "all", "args": "all"}], 6 | "strict": true 7 | }, 8 | "env":{ 9 | "browser": true, 10 | "node" : true 11 | } 12 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # osx noise 2 | .DS_Store 3 | profile 4 | 5 | # xcode noise 6 | build/* 7 | *.mode1 8 | *.mode1v3 9 | *.mode2v3 10 | *.perspective 11 | *.perspectivev3 12 | *.pbxuser 13 | *.xcworkspace 14 | xcuserdata 15 | 16 | # svn & cvs 17 | .svn 18 | CVS 19 | node_modules 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | (work in progress, looking for feedback) 2 | 3 | react-ease 4 | --- 5 | 6 | back to first principles 7 | 8 | `npm install react-ease react --save` 9 | 10 | canned animations for [react](https://facebook.github.io/react/)/[react-native](https://facebook.github.io/react-native/) 11 | 12 | (if you're looking for something more dynamic, consider [react-springs](https://github.com/threepointone/react-springs)) 13 | 14 | ```js 15 | // output floats from 0 to 100 over 0.5 seconds 16 | 17 | {val =>
{val}
} // yes, children is a function 18 |
19 | 20 | // you can tell when it's done 21 | 22 | {(val, done) =>
animation {done ? 'over!' : 'running...'}
} 23 |
24 | 25 | // you can ease multiple values at once 26 | 27 | {val =>
move it, move it
} 28 |
29 | 30 | // or if you want more control over each value 31 | {x => 32 | {y => 33 |
not bad!
} 34 |
} 35 |
36 | 37 | // finally, you can chain a bunch of them together 38 | done && console.log('done!')} 43 | repeat={5}> 44 | {(val, done) =>
sweet!
} 45 |
46 | 47 | 48 | ``` 49 | 50 | Ease::props 51 | --- 52 | 53 | - from: *number*/*object* 54 | - to: *number*/*object* 55 | - duration: *number* (ms) 56 | - ease: *string*/*function* `./src.js` has a list of available easing functions, or pass in your own 57 | - delay: *number* (ms) 58 | - onProgress: *function* - optional callback called on every 'movement'. 'returns' the current value, and a `done` flag 59 | - repeat: *number* 60 | 61 | Chain::props 62 | --- 63 | 64 | - sequence: *array* 65 | - repeat: *number* 66 | 67 | thanks 68 | --- 69 | - [ease-sential](https://github.com/WebReflection/ease-sential), of which react-ease contains a tweaked port 70 | 71 | -------------------------------------------------------------------------------- /dev.js: -------------------------------------------------------------------------------- 1 | import webpack from 'webpack'; 2 | import WebpackDevServer from 'webpack-dev-server'; 3 | import config from './webpack.config'; 4 | 5 | const isHot = !!process.env.HOT; 6 | 7 | new WebpackDevServer(webpack(config), { 8 | publicPath: config.output.publicPath, 9 | hot: isHot, 10 | historyApiFallback: true 11 | }).listen(3000, 'localhost', err => console.log(err || 'webpack at localhost:3000')); 12 | -------------------------------------------------------------------------------- /example/app.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Ease, Chain} from '../src'; 3 | 4 | let styles = {}; 5 | 6 | styles.app = { 7 | box: { 8 | width: 100, 9 | height: 100, 10 | left: 0, 11 | top: 0, 12 | position: 'absolute', 13 | backgroundColor: 'red' 14 | } 15 | }; 16 | 17 | export const App = React.createClass({ 18 | render() { 19 | return ( 20 |
21 | 22 | {val =>
} 23 |
24 | 25 |
26 | ); 27 | } 28 | }); 29 | 30 | styles.chained = { 31 | box: { 32 | width: 100, 33 | height: 100, 34 | left: 0, 35 | top: 0, 36 | position: 'absolute', 37 | backgroundColor: 'blue' 38 | } 39 | }; 40 | 41 | 42 | export const Chained = React.createClass({ 43 | render() { 44 | return ( 45 |
46 | 51 | {(val, done) =>
sweet! {done ? 'done!' :
{JSON.stringify(val, null, ' ')}
}
} 52 |
53 |
54 | ); 55 | } 56 | }); 57 | 58 | 59 | -------------------------------------------------------------------------------- /example/entry.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {App} from './app'; 3 | React.render(, document.getElementById('container')); 4 | window.React = React; 5 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | react-ease 5 | 8 | 9 | 10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /example/reset.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v3.0.1 | MIT License | git.io/normalize */ 2 | 3 | // * 1. Set default font family to sans-serif. 4 | // * 2. Prevent iOS text size adjust after orientation change, without disabling user zoom. 5 | html { 6 | font-family: sans-serif; 7 | -ms-text-size-adjust: 100%; 8 | -webkit-text-size-adjust: 100%; 9 | } 10 | 11 | // Remove default margin. 12 | body { margin: 0 } 13 | 14 | // HTML5 display definitions ========================================================================== 15 | // * Correct `block` display not defined for any HTML5 element in IE 8/9. 16 | // * Correct `block` display not defined for `details` or `summary` in IE 10/11 and Firefox. 17 | // * Correct `block` display not defined for `main` in IE 11. 18 | article, 19 | aside, 20 | details, 21 | figcaption, 22 | figure, 23 | footer, 24 | header, 25 | hgroup, 26 | main, 27 | nav, 28 | section, 29 | summary { 30 | display: block; 31 | } 32 | 33 | // * 1. Correct `inline-block` display not defined in IE 8/9. 34 | // * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. 35 | audio, 36 | canvas, 37 | progress, 38 | video { 39 | display: inline-block; 40 | vertical-align: baseline; 41 | } 42 | 43 | // * Prevent modern browsers from displaying `audio` without controls. 44 | // * Remove excess height in iOS 5 devices. 45 | audio:not([controls]) { 46 | display: none; 47 | height: 0; 48 | } 49 | 50 | // * Address `[hidden]` styling not present in IE 8/9/10. 51 | // * Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22. 52 | [hidden], 53 | template { 54 | display: none; 55 | } 56 | 57 | // Links ========================================================================== 58 | // * Remove the gray background color from active links in IE 10. 59 | a { 60 | background: transparent; 61 | // * Improve readability when focused and also mouse hovered in all browsers. 62 | &:active, 63 | &:hover { 64 | outline: 0; 65 | } 66 | } 67 | 68 | // Text-level semantics ========================================================================== 69 | 70 | // * Address styling not present in IE 8/9/10/11, Safari, and Chrome. 71 | abbr[title] { border-bottom: 1px dotted; } 72 | 73 | // * Address style set to `bolder` in Firefox 4+, Safari, and Chrome. 74 | b, 75 | strong { 76 | font-weight: bold; 77 | } 78 | 79 | // * Address styling not present in Safari and Chrome. 80 | dfn { font-style: italic; } 81 | 82 | // * Address variable `h1` font-size and margin within `section` and `article` contexts in Firefox 4+, Safari, and Chrome. 83 | h1 { 84 | font-size: 2em; 85 | margin: 0.67em 0; 86 | } 87 | 88 | // * Address styling not present in IE 8/9. 89 | mark { 90 | background: #ff0; 91 | color: #000; 92 | } 93 | 94 | // * Address inconsistent and variable font size in all browsers. 95 | small { font-size: 80%; } 96 | 97 | // * Prevent `sub` and `sup` affecting `line-height` in all browsers. 98 | sub, 99 | sup { 100 | font-size: 75%; 101 | line-height: 0; 102 | position: relative; 103 | vertical-align: baseline; 104 | } 105 | 106 | sup { top: -0.5em; } 107 | sub { bottom: -0.25em; } 108 | 109 | // Embedded content ========================================================================== 110 | // * Remove border when inside `a` element in IE 8/9/10. 111 | img { border: 0; } 112 | 113 | // * Correct overflow not hidden in IE 9/10/11. 114 | svg:not(:root) { overflow: hidden; } 115 | 116 | // Grouping content ========================================================================== 117 | // * Address margin not present in IE 8/9 and Safari. 118 | figure { margin: 1em 40px; } 119 | 120 | // * Address differences between Firefox and other browsers. 121 | hr { 122 | -moz-box-sizing: content-box; 123 | box-sizing: content-box; 124 | height: 0; 125 | } 126 | 127 | // * Contain overflow in all browsers. 128 | pre { overflow: auto; } 129 | 130 | // * Address odd `em`-unit font size rendering in all browsers. 131 | code, 132 | kbd, 133 | pre, 134 | samp { 135 | font-family: monospace, monospace; 136 | font-size: 1em; 137 | } 138 | 139 | // Forms ========================================================================== 140 | // * Known limitation: by default, Chrome and Safari on OS X allow very limited 141 | // * styling of `select`, unless a `border` property is set. 142 | 143 | // * 1. Correct color not being inherited. 144 | // * Known issue: affects color of disabled elements. 145 | // * 2. Correct font properties not being inherited. 146 | // * 3. Address margins set differently in Firefox 4+, Safari, and Chrome. 147 | button, 148 | input, 149 | optgroup, 150 | select, 151 | textarea { 152 | color: inherit; 153 | font: inherit; 154 | margin: 0; 155 | } 156 | 157 | // * Address `overflow` set to `hidden` in IE 8/9/10/11. 158 | button { overflow: visible;} 159 | 160 | // * Address inconsistent `text-transform` inheritance for `button` and `select`. 161 | // * All other form control elements do not inherit `text-transform` values. 162 | // * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. 163 | // * Correct `select` style inheritance in Firefox. 164 | button, 165 | select { 166 | text-transform: none; 167 | } 168 | 169 | // * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` and `video` controls. 170 | // * 2. Correct inability to style clickable `input` types in iOS. 171 | // * 3. Improve usability and consistency of cursor style between image-type `input` and others. 172 | button, 173 | html input[type="button"] { 174 | -webkit-appearance: button; 175 | cursor: pointer; 176 | } 177 | 178 | // * Re-set default cursor for disabled elements. 179 | button[disabled], 180 | html input[disabled] { 181 | cursor: default; 182 | } 183 | 184 | // * Remove inner padding and border in Firefox 4+. 185 | button 186 | input { 187 | &::-moz-focus-inner { 188 | border: 0; 189 | padding: 0; 190 | } 191 | } 192 | 193 | // * Address Firefox 4+ setting `line-height` on `input` using `!important` in the UA stylesheet. 194 | input { 195 | line-height: normal; 196 | &[type="reset"], 197 | &[type="submit"] { 198 | -webkit-appearance: button; 199 | cursor: pointer; 200 | } 201 | 202 | // * It's recommended that you don't attempt to style these elements. 203 | // * Firefox's implementation doesn't respect box-sizing, padding, or width. 204 | // * 1. Address box sizing set to `content-box` in IE 8/9/10. 205 | // * 2. Remove excess padding in IE 8/9/10. 206 | &[type="checkbox"], 207 | &[type="radio"] { 208 | box-sizing: border-box; 209 | padding: 0; 210 | } 211 | 212 | // * Fix the cursor style for Chrome's increment/decrement buttons. For certain 213 | // * `font-size` values of the `input`, it causes the cursor style of the 214 | // * decrement button to change from `default` to `text`. 215 | &[type="number"] { 216 | &::-webkit-inner-spin-button, 217 | &::-webkit-outer-spin-button { 218 | height: auto; 219 | } 220 | } 221 | 222 | // * 1. Address `appearance` set to `searchfield` in Safari and Chrome. 223 | // * 2. Address `box-sizing` set to `border-box` in Safari and Chrome (include `-moz` to future-proof). 224 | &[type="search"] { 225 | -webkit-appearance: textfield; 226 | -moz-box-sizing: content-box; 227 | -webkit-box-sizing: content-box; 228 | box-sizing: content-box; 229 | 230 | // * Remove inner padding and search cancel button in Safari and Chrome on OS X. 231 | // * Safari (but not Chrome) clips the cancel button when the search input has 232 | // * padding (and `textfield` appearance). 233 | &::-webkit-search-cancel-button, 234 | &::-webkit-search-decoration { 235 | -webkit-appearance: none; 236 | } 237 | } 238 | 239 | } 240 | 241 | // * Define consistent border, margin, and padding. 242 | fieldset { 243 | border: 1px solid #c0c0c0; 244 | margin: 0 2px; 245 | padding: 0.35em 0.625em 0.75em; 246 | } 247 | 248 | // * 1. Correct `color` not being inherited in IE 8/9/10/11. 249 | // * 2. Remove padding so people aren't caught out if they zero out fieldsets. 250 | legend { 251 | border: 0; 252 | padding: 0; 253 | } 254 | 255 | // * Remove default vertical scrollbar in IE 8/9/10/11. 256 | textarea { overflow: auto; } 257 | 258 | // * Don't inherit the `font-weight` (applied by a rule above). 259 | // * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. 260 | optgroup { font-weight: bold; } 261 | 262 | // Tables ========================================================================== 263 | // * Remove most spacing between table cells. 264 | table { 265 | border-collapse: collapse; 266 | border-spacing: 0; 267 | } 268 | 269 | td, 270 | th { 271 | padding: 0; 272 | } 273 | 274 | body{ 275 | font-family:helvetica, sans-serif; 276 | } 277 | 278 | 279 | html, body{ 280 | position:relative; 281 | display:block; 282 | height:100%; 283 | width:100%; 284 | padding:0; 285 | margin:0; 286 | } 287 | 288 | /* css-layout defaults */ 289 | div, span, html, body{ 290 | box-sizing: border-box; 291 | position: relative; 292 | 293 | display: flex; 294 | flex-direction: column; 295 | align-items: stretch; 296 | flex-shrink: 0; 297 | 298 | border: 0 solid black; 299 | margin: 0; 300 | padding: 0; 301 | } 302 | 303 | /* stretch out container */ 304 | #container{ 305 | flex:1; 306 | } -------------------------------------------------------------------------------- /lib/components.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, '__esModule', { 4 | value: true 5 | }); 6 | 7 | var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; 8 | 9 | exports['default'] = components; 10 | 11 | var _ease = require('./ease'); 12 | 13 | var noop = function noop() {}; 14 | 15 | function components(React) { 16 | 17 | var Ease = React.createClass({ 18 | displayName: 'Ease', 19 | 20 | propTypes: { 21 | // from: React.PropTypes.number, 22 | // to: React.PropTypes.number, 23 | duration: React.PropTypes.number, 24 | // ease: easings[easings.default], 25 | repeat: React.PropTypes.number, 26 | onProgress: React.PropTypes.func, 27 | delay: React.PropTypes.number, 28 | children: React.PropTypes.func 29 | }, 30 | getDefaultProps: function getDefaultProps() { 31 | return _ease.defaults; 32 | }, 33 | shouldComponentUpdate: function shouldComponentUpdate() { 34 | return true; 35 | }, 36 | 37 | getInitialState: function getInitialState() { 38 | return { 39 | value: this.props.from, 40 | done: false 41 | }; 42 | }, 43 | componentDidMount: function componentDidMount() { 44 | var _this = this; 45 | 46 | this.ease = new _ease.Easing(_extends({}, this.props, { onProgress: function onProgress(value, done) { 47 | _this.setState({ value: value, done: done }); 48 | _this.props.onProgress(value, done); 49 | } })); 50 | }, 51 | componentWillUnmount: function componentWillUnmount() { 52 | this.ease.cancel(); 53 | delete this.ease; 54 | }, 55 | render: function render() { 56 | return this.props.children(this.state.value, this.state.done); 57 | } 58 | }); 59 | 60 | var Chain = React.createClass({ 61 | displayName: 'Chain', 62 | 63 | getDefaultProps: function getDefaultProps() { 64 | return { 65 | onProgress: noop, 66 | repeat: 1 67 | }; 68 | }, 69 | propTypes: { 70 | sequence: React.PropTypes.array, 71 | children: React.PropTypes.func, 72 | onProgress: React.PropTypes.func 73 | }, 74 | getInitialState: function getInitialState() { 75 | var props = this.props.sequence[0]; 76 | return { 77 | index: 0, 78 | done: false, 79 | value: props.from, 80 | from: props.from, 81 | to: typeof props.to === 'number' ? props.to : _extends({}, props.from, props.to) 82 | }; 83 | }, 84 | 85 | shouldComponentUpdate: function shouldComponentUpdate() { 86 | return true; 87 | }, 88 | onProgress: function onProgress(value, done) { 89 | this.props.onProgress(this.state.index, value, done); 90 | 91 | if (done) { 92 | var _length = this.props.repeat * this.props.sequence.length; 93 | var allDone = this.state.index === _length - 1; 94 | var props = this.props.sequence[(this.state.index + 1) % this.props.sequence.length] || {}; 95 | var mergeable = typeof value !== 'number' && !allDone; 96 | this.setState({ 97 | index: allDone ? this.state.index : this.state.index + 1, 98 | done: allDone, 99 | value: value, 100 | from: mergeable ? _extends({}, value, props.from || {}) : props.from !== undefined ? props.from : value, 101 | to: mergeable ? _extends({}, value, props.to || {}) : props.to 102 | }); 103 | } 104 | }, 105 | render: function render() { 106 | var _this2 = this; 107 | 108 | var props = this.props.sequence[this.state.index % this.props.sequence.length]; 109 | var fn = function fn(val) { 110 | return _this2.props.children(val, _this2.state.done); 111 | }; 112 | return this.state.done ? fn(this.state.value, true) : React.createElement( 113 | Ease, 114 | _extends({}, props, { key: this.state.index, onProgress: this.onProgress, from: this.state.from, to: this.state.to }), 115 | fn 116 | ); 117 | } 118 | }); 119 | 120 | return { Ease: Ease, Chain: Chain }; 121 | } 122 | 123 | module.exports = exports['default']; -------------------------------------------------------------------------------- /lib/ease.js: -------------------------------------------------------------------------------- 1 | 2 | // pick up raf 3 | 'use strict'; 4 | 5 | Object.defineProperty(exports, '__esModule', { 6 | value: true 7 | }); 8 | 9 | var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; 10 | 11 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 12 | 13 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 14 | 15 | var root = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : undefined; 16 | var raf = root.requestAnimationFrame || root.webkitRequestAnimationFrame || root.mozRequestAnimationFrame || root.msRequestAnimationFrame || function (fn) { 17 | return setTimeout(fn, 10); 18 | }; 19 | 20 | exports.raf = raf; 21 | var noop = function noop() {}; 22 | 23 | // penner's easing equations 24 | var easings = { 25 | 'default': 'easeOutQuad', 26 | swing: function swing(t, b, c, d) { 27 | return easings[easings['default']](t, b, c, d); 28 | }, 29 | easeInQuad: function easeInQuad(t, b, c, d) { 30 | return c * (t /= d) * t + b; 31 | }, 32 | easeOutQuad: function easeOutQuad(t, b, c, d) { 33 | return -c * (t /= d) * (t - 2) + b; 34 | }, 35 | easeInOutQuad: function easeInOutQuad(t, b, c, d) { 36 | if ((t /= d / 2) < 1) { 37 | return c / 2 * t * t + b; 38 | } 39 | return -c / 2 * (--t * (t - 2) - 1) + b; 40 | }, 41 | easeInCubic: function easeInCubic(t, b, c, d) { 42 | return c * (t /= d) * t * t + b; 43 | }, 44 | easeOutCubic: function easeOutCubic(t, b, c, d) { 45 | return c * ((t = t / d - 1) * t * t + 1) + b; 46 | }, 47 | easeInOutCubic: function easeInOutCubic(t, b, c, d) { 48 | if ((t /= d / 2) < 1) { 49 | return c / 2 * t * t * t + b; 50 | } 51 | return c / 2 * ((t -= 2) * t * t + 2) + b; 52 | }, 53 | easeInQuart: function easeInQuart(t, b, c, d) { 54 | return c * (t /= d) * t * t * t + b; 55 | }, 56 | easeOutQuart: function easeOutQuart(t, b, c, d) { 57 | return -c * ((t = t / d - 1) * t * t * t - 1) + b; 58 | }, 59 | easeInOutQuart: function easeInOutQuart(t, b, c, d) { 60 | if ((t /= d / 2) < 1) { 61 | return c / 2 * t * t * t * t + b; 62 | } 63 | return -c / 2 * ((t -= 2) * t * t * t - 2) + b; 64 | }, 65 | easeInQuint: function easeInQuint(t, b, c, d) { 66 | return c * (t /= d) * t * t * t * t + b; 67 | }, 68 | easeOutQuint: function easeOutQuint(t, b, c, d) { 69 | return c * ((t = t / d - 1) * t * t * t * t + 1) + b; 70 | }, 71 | easeInOutQuint: function easeInOutQuint(t, b, c, d) { 72 | if ((t /= d / 2) < 1) { 73 | return c / 2 * t * t * t * t * t + b; 74 | } 75 | return c / 2 * ((t -= 2) * t * t * t * t + 2) + b; 76 | }, 77 | easeInSine: function easeInSine(t, b, c, d) { 78 | return -c * Math.cos(t / d * (Math.PI / 2)) + c + b; 79 | }, 80 | easeOutSine: function easeOutSine(t, b, c, d) { 81 | return c * Math.sin(t / d * (Math.PI / 2)) + b; 82 | }, 83 | easeInOutSine: function easeInOutSine(t, b, c, d) { 84 | return -c / 2 * (Math.cos(Math.PI * t / d) - 1) + b; 85 | }, 86 | easeInExpo: function easeInExpo(t, b, c, d) { 87 | return t === 0 ? b : c * Math.pow(2, 10 * (t / d - 1)) + b; 88 | }, 89 | easeOutExpo: function easeOutExpo(t, b, c, d) { 90 | return t === d ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b; 91 | }, 92 | easeInOutExpo: function easeInOutExpo(t, b, c, d) { 93 | if (t === 0) { 94 | return b; 95 | } 96 | if (t === d) { 97 | return b + c; 98 | } 99 | if ((t /= d / 2) < 1) { 100 | return c / 2 * Math.pow(2, 10 * (t - 1)) + b; 101 | } 102 | return c / 2 * (-Math.pow(2, -10 * --t) + 2) + b; 103 | }, 104 | easeInCirc: function easeInCirc(t, b, c, d) { 105 | return -c * (Math.sqrt(1 - (t /= d) * t) - 1) + b; 106 | }, 107 | easeOutCirc: function easeOutCirc(t, b, c, d) { 108 | return c * Math.sqrt(1 - (t = t / d - 1) * t) + b; 109 | }, 110 | easeInOutCirc: function easeInOutCirc(t, b, c, d) { 111 | if ((t /= d / 2) < 1) { 112 | return -c / 2 * (Math.sqrt(1 - t * t) - 1) + b; 113 | } 114 | return c / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1) + b; 115 | }, 116 | easeInElastic: function easeInElastic(t, b, c, d) { 117 | var s = 1.70158; 118 | var p = 0; 119 | var a = c; 120 | if (t === 0) { 121 | return b; 122 | } 123 | if ((t /= d) === 1) { 124 | return b + c; 125 | } 126 | if (!p) { 127 | p = d * 0.3; 128 | } 129 | if (a < Math.abs(c)) { 130 | a = c; 131 | s = p / 4; 132 | } else { 133 | s = p / (2 * Math.PI) * Math.asin(c / a); 134 | } 135 | return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b; 136 | }, 137 | easeOutElastic: function easeOutElastic(t, b, c, d) { 138 | var s = 1.70158; 139 | var p = 0; 140 | var a = c; 141 | if (t === 0) { 142 | return b; 143 | } 144 | if ((t /= d) === 1) { 145 | return b + c; 146 | } 147 | if (!p) { 148 | p = d * 0.3; 149 | } 150 | if (a < Math.abs(c)) { 151 | a = c; 152 | s = p / 4; 153 | } else { 154 | s = p / (2 * Math.PI) * Math.asin(c / a); 155 | } 156 | return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b; 157 | }, 158 | easeInOutElastic: function easeInOutElastic(t, b, c, d) { 159 | var s = 1.70158; 160 | var p = 0; 161 | var a = c; 162 | if (t === 0) { 163 | return b; 164 | } 165 | if ((t /= d / 2) === 2) { 166 | return b + c; 167 | } 168 | if (!p) { 169 | p = d * (0.3 * 1.5); 170 | } 171 | if (a < Math.abs(c)) { 172 | a = c; 173 | s = p / 4; 174 | } else { 175 | s = p / (2 * Math.PI) * Math.asin(c / a); 176 | } 177 | if (t < 1) { 178 | return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b; 179 | } 180 | return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p) * 0.5 + c + b; 181 | }, 182 | easeInBack: function easeInBack(t, b, c, d, s) { 183 | if (s === undefined) { 184 | s = 1.70158; 185 | } 186 | return c * (t /= d) * t * ((s + 1) * t - s) + b; 187 | }, 188 | easeOutBack: function easeOutBack(t, b, c, d, s) { 189 | if (s === undefined) { 190 | s = 1.70158; 191 | } 192 | return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b; 193 | }, 194 | easeInOutBack: function easeInOutBack(t, b, c, d, s) { 195 | if (s === undefined) { 196 | s = 1.70158; 197 | } 198 | if ((t /= d / 2) < 1) { 199 | return c / 2 * (t * t * (((s *= 1.525) + 1) * t - s)) + b; 200 | } 201 | return c / 2 * ((t -= 2) * t * (((s *= 1.525) + 1) * t + s) + 2) + b; 202 | }, 203 | easeInBounce: function easeInBounce(t, b, c, d) { 204 | return c - easings.easeOutBounce(d - t, 0, c, d) + b; 205 | }, 206 | easeOutBounce: function easeOutBounce(t, b, c, d) { 207 | if ((t /= d) < 1 / 2.75) { 208 | return c * (7.5625 * t * t) + b; 209 | } else if (t < 2 / 2.75) { 210 | return c * (7.5625 * (t -= 1.5 / 2.75) * t + 0.75) + b; 211 | } else if (t < 2.5 / 2.75) { 212 | return c * (7.5625 * (t -= 2.25 / 2.75) * t + 0.9375) + b; 213 | } else { 214 | return c * (7.5625 * (t -= 2.625 / 2.75) * t + 0.984375) + b; 215 | } 216 | }, 217 | easeInOutBounce: function easeInOutBounce(t, b, c, d) { 218 | if (t < d / 2) { 219 | return easings.easeInBounce(t * 2, 0, c, d) * 0.5 + b; 220 | } 221 | return easings.easeOutBounce(t * 2 - d, 0, c, d) * 0.5 + c * 0.5 + b; 222 | } 223 | }; 224 | 225 | exports.easings = easings; 226 | var defaults = { 227 | from: 0, 228 | to: 0, 229 | duration: 1000, 230 | ease: easings[easings['default']], 231 | onProgress: noop, 232 | delay: 0 233 | }; 234 | 235 | exports.defaults = defaults; 236 | // to avoid an assign polyfill 237 | function assign(target) { 238 | for (var i = 1; i < arguments.length; i++) { 239 | var source = arguments[i]; 240 | for (var key in source) { 241 | if (Object.prototype.hasOwnProperty.call(source, key)) { 242 | target[key] = source[key]; 243 | } 244 | } 245 | } 246 | return target; 247 | } 248 | 249 | // animation class 250 | // - from: *number*/*object* 251 | // - to: *number*/*object* 252 | // - duration: *number* (ms) 253 | // - ease: *string*/*function* `./src.js` has a list of available easing functions, or pass in your own 254 | // - delay: *number* (ms) 255 | // - onProgress: *function* - optional callback called on every 'movement'. 'returns' the current value, and a `done` flag 256 | 257 | var Easing = (function () { 258 | function Easing(config) { 259 | var _this = this; 260 | 261 | _classCallCheck(this, Easing); 262 | 263 | assign(this, _extends({}, defaults, config)); 264 | this.easing = typeof this.ease === 'function' ? this.ease : easings[this.ease || easings['default']]; 265 | 266 | this.update = typeof this.from === 'object' ? this.loopProperties : this.directUpdate; 267 | 268 | this.start = Date.now() + this.delay; 269 | this.end = this.start + this.duration; 270 | this.canceled = false; 271 | this.progress = this.progress.bind(this); 272 | 273 | if (this.delay) { 274 | setTimeout(function () { 275 | return raf(_this.progress); 276 | }, this.delay); 277 | } else { 278 | raf(this.progress); 279 | } 280 | } 281 | 282 | _createClass(Easing, [{ 283 | key: 'directUpdate', 284 | value: function directUpdate(from, to, easing, current, duration) { 285 | return easing(current, from, to - from, duration); 286 | } 287 | }, { 288 | key: 'loopProperties', 289 | value: function loopProperties(from, to, easing, current, duration) { 290 | var k, 291 | o = {}; 292 | for (k in from) { 293 | o[k] = easing(current, from[k], to[k] - from[k], duration); 294 | } 295 | return o; 296 | } 297 | }, { 298 | key: 'progress', 299 | value: function progress() { 300 | if (this.canceled) { 301 | return; 302 | } 303 | this.time = Date.now(); 304 | if (this.end <= this.time) { 305 | this.onProgress(this.to, true); 306 | } else { 307 | this.onProgress(this.update(this.from, this.to, this.easing, this.time - this.start, this.duration)); 308 | raf(this.progress); 309 | } 310 | } 311 | }, { 312 | key: 'cancel', 313 | value: function cancel() { 314 | this.canceled = true; 315 | } 316 | }]); 317 | 318 | return Easing; 319 | })(); 320 | 321 | exports.Easing = Easing; 322 | 323 | /* 324 | * 325 | * TERMS OF USE - EASING EQUATIONS 326 | * 327 | * Open source under the BSD License. 328 | * 329 | * Copyright © 2001 Robert Penner 330 | * All rights reserved. 331 | * 332 | * Redistribution and use in source and binary forms, with or without modification, 333 | * are permitted provided that the following conditions are met: 334 | * 335 | * Redistributions of source code must retain the above copyright notice, this list of 336 | * conditions and the following disclaimer. 337 | * Redistributions in binary form must reproduce the above copyright notice, this list 338 | * of conditions and the following disclaimer in the documentation and/or other materials 339 | * provided with the distribution. 340 | * 341 | * Neither the name of the author nor the names of contributors may be used to endorse 342 | * or promote products derived from this software without specific prior written permission. 343 | * 344 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 345 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 346 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 347 | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 348 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 349 | * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 350 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 351 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 352 | * OF THE POSSIBILITY OF SUCH DAMAGE. 353 | * 354 | */ -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, '__esModule', { 4 | value: true 5 | }); 6 | 7 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 8 | 9 | var _react = require('react'); 10 | 11 | var _react2 = _interopRequireDefault(_react); 12 | 13 | var _components = require('./components'); 14 | 15 | var _components2 = _interopRequireDefault(_components); 16 | 17 | exports['default'] = (0, _components2['default'])(_react2['default']); 18 | module.exports = exports['default']; -------------------------------------------------------------------------------- /lib/native.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | Object.defineProperty(exports, '__esModule', { 4 | value: true 5 | }); 6 | 7 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 8 | 9 | var _reactNative = require('react-native'); 10 | 11 | var _reactNative2 = _interopRequireDefault(_reactNative); 12 | 13 | var _components = require('./components'); 14 | 15 | var _components2 = _interopRequireDefault(_components); 16 | 17 | exports['default'] = (0, _components2['default'])(_reactNative2['default']); 18 | module.exports = exports['default']; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-ease", 3 | "version": "1.0.7", 4 | "description": "i like to move it move it", 5 | "main": "./lib/index.js", 6 | "scripts": { 7 | "test": "mocha", 8 | "build": "babel src -d lib", 9 | "start": "HOT=1 babel-node dev.js", 10 | "dev": "babel-node dev.js" 11 | }, 12 | "keywords": [ 13 | "animation", 14 | "react", 15 | "easings", 16 | "chain" 17 | ], 18 | "author": "Sunil Pai", 19 | "license": "ISC", 20 | "devDependencies": { 21 | "babel": "^5.5.4", 22 | "babel-core": "^5.5.4", 23 | "babel-eslint": "^3.1.15", 24 | "babel-loader": "^5.1.4", 25 | "eslint": "^0.22.1", 26 | "eslint-plugin-react": "^2.5.0", 27 | "node-libs-browser": "^0.5.2", 28 | "react": "^0.13.3", 29 | "react-hot-loader": "^1.2.7", 30 | "webpack": "^1.9.10", 31 | "webpack-dev-server": "^1.9.0" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/components.js: -------------------------------------------------------------------------------- 1 | import {Easing, defaults} from './ease'; 2 | 3 | let noop = () => {}; 4 | 5 | export default function components(React){ 6 | 7 | const Ease = React.createClass({ 8 | propTypes: { 9 | // from: React.PropTypes.number, 10 | // to: React.PropTypes.number, 11 | duration: React.PropTypes.number, 12 | // ease: easings[easings.default], 13 | repeat: React.PropTypes.number, 14 | onProgress: React.PropTypes.func, 15 | delay: React.PropTypes.number, 16 | children: React.PropTypes.func 17 | }, 18 | getDefaultProps() { 19 | return defaults; 20 | }, 21 | shouldComponentUpdate() { 22 | return true; 23 | }, 24 | 25 | getInitialState() { 26 | return { 27 | value: this.props.from, 28 | done: false 29 | }; 30 | }, 31 | componentDidMount() { 32 | this.ease = new Easing({...this.props, onProgress: (value, done) => { 33 | this.setState({value, done}); 34 | this.props.onProgress(value, done); 35 | }}); 36 | }, 37 | componentWillUnmount() { 38 | this.ease.cancel(); 39 | delete this.ease; 40 | }, 41 | render() { 42 | return this.props.children(this.state.value, this.state.done); 43 | } 44 | }); 45 | 46 | const Chain = React.createClass({ 47 | getDefaultProps(){ 48 | return { 49 | onProgress: noop, 50 | repeat: 1 51 | }; 52 | }, 53 | propTypes: { 54 | sequence: React.PropTypes.array, 55 | children: React.PropTypes.func, 56 | onProgress: React.PropTypes.func 57 | }, 58 | getInitialState() { 59 | let props = this.props.sequence[0]; 60 | return { 61 | index: 0, 62 | done: false, 63 | value: props.from, 64 | from: props.from, 65 | to: typeof props.to === 'number' ? props.to : {...props.from, ...props.to} 66 | }; 67 | }, 68 | 69 | shouldComponentUpdate(){ 70 | return true; 71 | }, 72 | onProgress(value, done){ 73 | this.props.onProgress(this.state.index, value, done); 74 | 75 | if(done){ 76 | let length = this.props.repeat * this.props.sequence.length; 77 | let allDone = this.state.index === (length - 1); 78 | let props = this.props.sequence[((this.state.index + 1) % this.props.sequence.length)] || {}; 79 | let mergeable = ((typeof value !== 'number') && !allDone); 80 | this.setState({ 81 | index: allDone ? this.state.index : this.state.index + 1, 82 | done: allDone, 83 | value: value, 84 | from: mergeable ? {...value, ...(props.from || {})} : (props.from !== undefined ? props.from : value), 85 | to: mergeable ? {...value, ...(props.to || {})} : props.to 86 | }); 87 | } 88 | }, 89 | render(){ 90 | let props = this.props.sequence[this.state.index % this.props.sequence.length]; 91 | let fn = val => this.props.children(val, this.state.done); 92 | return this.state.done ? 93 | fn(this.state.value, true) : 94 | 95 | {fn} 96 | ; 97 | } 98 | }); 99 | 100 | return {Ease, Chain}; 101 | 102 | } 103 | -------------------------------------------------------------------------------- /src/ease.js: -------------------------------------------------------------------------------- 1 | 2 | // pick up raf 3 | let root = (typeof window !== 'undefined') ? window : ((typeof global !== 'undefined') ? global : this); 4 | export const raf = root.requestAnimationFrame || root.webkitRequestAnimationFrame || root.mozRequestAnimationFrame || root.msRequestAnimationFrame || (fn => setTimeout(fn, 10)); 5 | 6 | let noop = () => {}; 7 | 8 | // penner's easing equations 9 | export const easings = { 10 | default: 'easeOutQuad', 11 | swing(t, b, c, d) { 12 | return easings[easings.default](t, b, c, d); 13 | }, 14 | easeInQuad(t, b, c, d) { 15 | return c * (t /= d) * t + b; 16 | }, 17 | easeOutQuad(t, b, c, d) { 18 | return -c * (t /= d) * (t - 2) + b; 19 | }, 20 | easeInOutQuad(t, b, c, d) { 21 | if ((t /= d / 2) < 1) { 22 | return c / 2 * t * t + b; 23 | } 24 | return -c / 2 * ((--t) * (t - 2) - 1) + b; 25 | }, 26 | easeInCubic(t, b, c, d) { 27 | return c * (t /= d) * t * t + b; 28 | }, 29 | easeOutCubic(t, b, c, d) { 30 | return c * ((t = t / d - 1) * t * t + 1) + b; 31 | }, 32 | easeInOutCubic(t, b, c, d) { 33 | if ((t /= d / 2) < 1) { 34 | return c / 2 * t * t * t + b; 35 | } 36 | return c / 2 * ((t -= 2) * t * t + 2) + b; 37 | }, 38 | easeInQuart(t, b, c, d) { 39 | return c * (t /= d) * t * t * t + b; 40 | }, 41 | easeOutQuart(t, b, c, d) { 42 | return -c * ((t = t / d - 1) * t * t * t - 1) + b; 43 | }, 44 | easeInOutQuart(t, b, c, d) { 45 | if ((t /= d / 2) < 1) { 46 | return c / 2 * t * t * t * t + b; 47 | } 48 | return -c / 2 * ((t -= 2) * t * t * t - 2) + b; 49 | }, 50 | easeInQuint(t, b, c, d) { 51 | return c * (t /= d) * t * t * t * t + b; 52 | }, 53 | easeOutQuint(t, b, c, d) { 54 | return c * ((t = t / d - 1) * t * t * t * t + 1) + b; 55 | }, 56 | easeInOutQuint(t, b, c, d) { 57 | if ((t /= d / 2) < 1) { 58 | return c / 2 * t * t * t * t * t + b; 59 | } 60 | return c / 2 * ((t -= 2) * t * t * t * t + 2) + b; 61 | }, 62 | easeInSine(t, b, c, d) { 63 | return -c * Math.cos(t / d * (Math.PI / 2)) + c + b; 64 | }, 65 | easeOutSine(t, b, c, d) { 66 | return c * Math.sin(t / d * (Math.PI / 2)) + b; 67 | }, 68 | easeInOutSine(t, b, c, d) { 69 | return -c / 2 * (Math.cos(Math.PI * t / d) - 1) + b; 70 | }, 71 | easeInExpo(t, b, c, d) { 72 | return (t === 0) ? b : c * Math.pow(2, 10 * (t / d - 1)) + b; 73 | }, 74 | easeOutExpo(t, b, c, d) { 75 | return (t === d) ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b; 76 | }, 77 | easeInOutExpo(t, b, c, d) { 78 | if (t === 0) { 79 | return b; 80 | } 81 | if (t === d) { 82 | return b + c; 83 | } 84 | if ((t /= d / 2) < 1) { 85 | return c / 2 * Math.pow(2, 10 * (t - 1)) + b; 86 | } 87 | return c / 2 * (-Math.pow(2, -10 * --t) + 2) + b; 88 | }, 89 | easeInCirc(t, b, c, d) { 90 | return -c * (Math.sqrt(1 - (t /= d) * t) - 1) + b; 91 | }, 92 | easeOutCirc(t, b, c, d) { 93 | return c * Math.sqrt(1 - (t = t / d - 1) * t) + b; 94 | }, 95 | easeInOutCirc(t, b, c, d) { 96 | if ((t /= d / 2) < 1) { 97 | return -c / 2 * (Math.sqrt(1 - t * t) - 1) + b; 98 | } 99 | return c / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1) + b; 100 | }, 101 | easeInElastic(t, b, c, d) { 102 | var s = 1.70158; 103 | var p = 0; 104 | var a = c; 105 | if (t === 0) { 106 | return b; 107 | } 108 | if ((t /= d) === 1) { 109 | return b + c; 110 | } 111 | if (!p) { 112 | p = d * .3; 113 | } 114 | if (a < Math.abs(c)) { 115 | a = c; 116 | s = p / 4; 117 | } else { 118 | s = p / (2 * Math.PI) * Math.asin(c / a); 119 | } 120 | return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b; 121 | }, 122 | easeOutElastic(t, b, c, d) { 123 | var s = 1.70158; 124 | var p = 0; 125 | var a = c; 126 | if (t === 0) { 127 | return b; 128 | } 129 | if ((t /= d) === 1) { 130 | return b + c; 131 | } 132 | if (!p) { 133 | p = d * .3; 134 | } 135 | if (a < Math.abs(c)) { 136 | a = c; 137 | s = p / 4; 138 | } else { 139 | s = p / (2 * Math.PI) * Math.asin(c / a); 140 | } 141 | return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b; 142 | }, 143 | easeInOutElastic(t, b, c, d) { 144 | var s = 1.70158; 145 | var p = 0; 146 | var a = c; 147 | if (t === 0) { 148 | return b; 149 | } 150 | if ((t /= d / 2) === 2) { 151 | return b + c; 152 | } 153 | if (!p) { 154 | p = d * (.3 * 1.5); 155 | } 156 | if (a < Math.abs(c)) { 157 | a = c; 158 | s = p / 4; 159 | } else { 160 | s = p / (2 * Math.PI) * Math.asin(c / a); 161 | } 162 | if (t < 1) { 163 | return -.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b; 164 | } 165 | return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b; 166 | }, 167 | easeInBack(t, b, c, d, s) { 168 | if (s === undefined) { 169 | s = 1.70158; 170 | } 171 | return c * (t /= d) * t * ((s + 1) * t - s) + b; 172 | }, 173 | easeOutBack(t, b, c, d, s) { 174 | if (s === undefined) { 175 | s = 1.70158; 176 | } 177 | return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b; 178 | }, 179 | easeInOutBack(t, b, c, d, s) { 180 | if (s === undefined) { 181 | s = 1.70158; 182 | } 183 | if ((t /= d / 2) < 1) { 184 | return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b; 185 | } 186 | return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b; 187 | }, 188 | easeInBounce(t, b, c, d) { 189 | return c - easings.easeOutBounce(d - t, 0, c, d) + b; 190 | }, 191 | easeOutBounce(t, b, c, d) { 192 | if ((t /= d) < (1 / 2.75)) { 193 | return c * (7.5625 * t * t) + b; 194 | } else if (t < (2 / 2.75)) { 195 | return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b; 196 | } else if (t < (2.5 / 2.75)) { 197 | return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b; 198 | } else { 199 | return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b; 200 | } 201 | }, 202 | easeInOutBounce(t, b, c, d) { 203 | if (t < d / 2) { 204 | return easings.easeInBounce(t * 2, 0, c, d) * .5 + b; 205 | } 206 | return easings.easeOutBounce(t * 2 - d, 0, c, d) * .5 + c * .5 + b; 207 | } 208 | }; 209 | 210 | export const defaults = { 211 | from: 0, 212 | to: 0, 213 | duration: 1000, 214 | ease: easings[easings.default], 215 | onProgress: noop, 216 | delay: 0 217 | }; 218 | 219 | // to avoid an assign polyfill 220 | function assign(target) { 221 | for (var i = 1; i < arguments.length; i++) { 222 | var source = arguments[i]; 223 | for (var key in source) { 224 | if (Object.prototype.hasOwnProperty.call(source, key)) { 225 | target[key] = source[key]; 226 | } 227 | } 228 | } 229 | return target; 230 | } 231 | 232 | // animation class 233 | // - from: *number*/*object* 234 | // - to: *number*/*object* 235 | // - duration: *number* (ms) 236 | // - ease: *string*/*function* `./src.js` has a list of available easing functions, or pass in your own 237 | // - delay: *number* (ms) 238 | // - onProgress: *function* - optional callback called on every 'movement'. 'returns' the current value, and a `done` flag 239 | 240 | export class Easing{ 241 | constructor(config){ 242 | assign(this, {...defaults, ...config}); 243 | this.easing = typeof this.ease === 'function' ? 244 | this.ease : 245 | easings[this.ease || easings.default]; 246 | 247 | this.update = typeof this.from === 'object' ? this.loopProperties : this.directUpdate; 248 | 249 | this.start = Date.now() + this.delay; 250 | this.end = this.start + this.duration; 251 | this.canceled = false; 252 | this.progress = ::this.progress; 253 | 254 | if(this.delay){ 255 | setTimeout(()=> raf(this.progress), this.delay); 256 | } 257 | else{ 258 | raf(this.progress); 259 | } 260 | } 261 | 262 | directUpdate(from, to, easing, current, duration) { 263 | return easing(current, from, to - from, duration); 264 | } 265 | 266 | loopProperties(from, to, easing, current, duration) { 267 | var k, o = {}; 268 | for (k in from) { 269 | o[k] = easing(current, from[k], to[k] - from[k], duration); 270 | } 271 | return o; 272 | } 273 | 274 | progress(){ 275 | if (this.canceled) { 276 | return; 277 | } 278 | this.time = Date.now(); 279 | if (this.end <= this.time) { 280 | this.onProgress(this.to, true); 281 | } else { 282 | this.onProgress(this.update(this.from, this.to, this.easing, this.time - this.start, this.duration)); 283 | raf(this.progress); 284 | } 285 | } 286 | 287 | cancel(){ 288 | this.canceled = true; 289 | } 290 | } 291 | 292 | 293 | 294 | 295 | /* 296 | * 297 | * TERMS OF USE - EASING EQUATIONS 298 | * 299 | * Open source under the BSD License. 300 | * 301 | * Copyright © 2001 Robert Penner 302 | * All rights reserved. 303 | * 304 | * Redistribution and use in source and binary forms, with or without modification, 305 | * are permitted provided that the following conditions are met: 306 | * 307 | * Redistributions of source code must retain the above copyright notice, this list of 308 | * conditions and the following disclaimer. 309 | * Redistributions in binary form must reproduce the above copyright notice, this list 310 | * of conditions and the following disclaimer in the documentation and/or other materials 311 | * provided with the distribution. 312 | * 313 | * Neither the name of the author nor the names of contributors may be used to endorse 314 | * or promote products derived from this software without specific prior written permission. 315 | * 316 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 317 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 318 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 319 | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 320 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 321 | * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 322 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 323 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 324 | * OF THE POSSIBILITY OF SUCH DAMAGE. 325 | * 326 | */ 327 | 328 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import components from './components'; 3 | 4 | export default components(React); 5 | 6 | -------------------------------------------------------------------------------- /src/native.js: -------------------------------------------------------------------------------- 1 | import React from 'react-native'; 2 | import components from './components'; 3 | 4 | export default components(React); 5 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | import webpack from 'webpack'; 2 | 3 | let config = { 4 | devtool: 'source-map', 5 | target: 'web', 6 | entry: { 7 | entry: ['./example/entry.js'] 8 | }, 9 | output: { 10 | path: __dirname, 11 | filename: '/example/bundle.js', 12 | publicPath: '/' 13 | }, 14 | module: { 15 | loaders: [{ 16 | test: /\.js$/, 17 | exclude: /node_modules/, 18 | loaders: ['babel-loader'] 19 | }] 20 | }, 21 | resolve: { 22 | extensions: ['', '.js', '.jsx'] 23 | }, 24 | plugins: [new webpack.DefinePlugin({ 25 | 'process.env': { 26 | 'NODE_ENV': `"${process.env.NODE_ENV || 'development'}"` 27 | } 28 | })], 29 | externals: ['react-native'] 30 | }; 31 | 32 | if(process.env.HOT){ 33 | config = { 34 | ...config, 35 | devtool: 'eval-source-map', 36 | entry: Object.keys(config.entry).reduce((o, key) => ({...o, [key]: [ 37 | 'webpack-dev-server/client?http://localhost:3000', // WebpackDevServer host and port 38 | 'webpack/hot/only-dev-server' 39 | ].concat(config.entry[key])}), {}), 40 | module: {...config.module, 41 | loaders: [{ 42 | ...config.module.loaders[0], 43 | loaders: [ 44 | 'react-hot' 45 | ].concat(config.module.loaders[0].loaders) 46 | }] 47 | }, 48 | plugins: [ 49 | new webpack.HotModuleReplacementPlugin(), 50 | new webpack.NoErrorsPlugin() 51 | ].concat(config.plugins) 52 | }; 53 | } 54 | 55 | module.exports = config; 56 | --------------------------------------------------------------------------------