├── .babelrc ├── src ├── index.js ├── native.js └── components.js ├── examples └── demos │ ├── index.js │ ├── index.html │ ├── app.js │ └── reset.css ├── .eslintrc ├── .gitignore ├── server.js ├── lib ├── index.js ├── native.js └── components.js ├── package.json ├── webpack.config.js └── README.md /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "stage": 0 3 | } -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import components from './components'; 3 | 4 | module.exports = components(React); 5 | -------------------------------------------------------------------------------- /src/native.js: -------------------------------------------------------------------------------- 1 | import React from 'react-native'; 2 | import components from './components'; 3 | 4 | module.exports = components(React); 5 | -------------------------------------------------------------------------------- /examples/demos/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {App} from './app'; 3 | React.render(, document.getElementById('container')); 4 | window.React = React; 5 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /examples/demos/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | react-springs 5 | 8 | 9 | 10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /server.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 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 4 | 5 | var _react = require('react'); 6 | 7 | var _react2 = _interopRequireDefault(_react); 8 | 9 | var _components = require('./components'); 10 | 11 | var _components2 = _interopRequireDefault(_components); 12 | 13 | module.exports = (0, _components2['default'])(_react2['default']); -------------------------------------------------------------------------------- /lib/native.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 4 | 5 | var _reactNative = require('react-native'); 6 | 7 | var _reactNative2 = _interopRequireDefault(_reactNative); 8 | 9 | var _components = require('./components'); 10 | 11 | var _components2 = _interopRequireDefault(_components); 12 | 13 | module.exports = (0, _components2['default'])(_reactNative2['default']); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-springs", 3 | "version": "1.1.0", 4 | "description": "ooh lookit the wow", 5 | "main": "./lib/index.js", 6 | "scripts": { 7 | "test": "mocha", 8 | "build": "rm -rf lib && babel src -d lib", 9 | "start": "HOT=1 babel-node server.js", 10 | "dev": "babel-node server.js" 11 | }, 12 | "keywords": [ 13 | "animation", 14 | "react", 15 | "rebound", 16 | "spring" 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 | "rebound": "0.0.13", 31 | "webpack": "^1.9.10", 32 | "webpack-dev-server": "^1.9.0" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | import webpack from 'webpack'; 2 | // import path from 'path'; 3 | 4 | let config = { 5 | devtool: 'source-map', 6 | target: 'web', 7 | entry: { 8 | demos: ['./examples/demos/index.js'] 9 | }, 10 | output: { 11 | path: __dirname, 12 | filename: 'examples/[name]/bundle.js', 13 | publicPath: '/' 14 | }, 15 | module: { 16 | loaders: [{ 17 | test: /\.js$/, 18 | exclude: /node_modules/, 19 | loaders: ['babel-loader'] 20 | }] 21 | }, 22 | resolve: { 23 | extensions: ['', '.js', '.jsx'] 24 | }, 25 | plugins: [new webpack.DefinePlugin({ 26 | 'process.env': { 27 | 'NODE_ENV': `"${process.env.NODE_ENV || 'development'}"` 28 | } 29 | })], 30 | externals: ['react-native'] 31 | }; 32 | 33 | if(process.env.HOT){ 34 | config = { 35 | ...config, 36 | devtool: 'eval-source-map', 37 | entry: Object.keys(config.entry).reduce((o, key) => ({...o, [key]: [ 38 | 'webpack-dev-server/client?http://localhost:3000', // WebpackDevServer host and port 39 | 'webpack/hot/only-dev-server' 40 | ].concat(config.entry[key])}), {}), 41 | module: {...config.module, 42 | loaders: [{ 43 | ...config.module.loaders[0], 44 | loaders: [ 45 | 'react-hot' 46 | ].concat(config.module.loaders[0].loaders) 47 | }] 48 | }, 49 | plugins: [ 50 | new webpack.HotModuleReplacementPlugin(), 51 | new webpack.NoErrorsPlugin() 52 | ].concat(config.plugins) 53 | }; 54 | } 55 | 56 | module.exports = config; 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | react-springs 2 | --- 3 | 4 | (bring your family and friends) 5 | 6 | `npm install react-springs react rebound --save` 7 | 8 | Dynamic animations for [react](https://facebook.github.io/react/)/[react-native](https://facebook.github.io/react-native/), powered by [rebound](https://github.com/facebook/rebound-js). 9 | 10 | (if you're looking for something more conventional, consider [react-ease](https://github.com/threepointone/react-ease)) 11 | 12 | ```js 13 | // output floats gradually from 0 to 10 14 | 15 | {val =>
{val}
} // yes, children is a function. 16 |
17 | 18 | // you can compose multiple springs 19 | 20 | {val =>
} 21 |
22 | 23 | // alternately, if you want control on each spring 24 | {x => 25 | {y => 26 |
} 27 | } 28 | 29 | 30 | // and if you'd rather not use the child-function pattern 31 |
32 | this.setState({ value: spring.getCurrentValue() })} /> 33 |
moving box
34 |
35 | ``` 36 | 37 | props 38 | --- 39 | 40 | - to (Spring): *number* : sets end value for the spring. if `atRest` is `true`, then the spring immediately snaps to `to`. 41 | - to (Springs): *obj* : similar to `Spring`, but accepts a map of key-values. 42 | - from: *number* : sets start value for a spring. changing it while a spring is in motion will set its current value to `from`, and continue to `to` 43 | - atRest: *boolean* 44 | - friction: *number* 45 | - tension: *number* 46 | - overshootClamping: *boolean* 47 | - onSpringUpdate (Spring): *function* : optional callback to be notified on every spring movement. 'returns' the spring. 48 | - onSpringUpdate (Springs): *function* : like `Spring`, but 'returns' `key, spring` 49 | - springSystem: *SpringSystem* : optional instance of rebound's SpringSystem. useful for perf/custom loopers/etc. 50 | 51 | dev 52 | --- 53 | 54 | `npm install` 55 | 56 | `npm start` 57 | 58 | notes 59 | --- 60 | 61 | - don't forget about the ridiculous performance boost you get from React when NODE_ENV=production 62 | - open to ideas on how to set 'velocity' for flings, etc -------------------------------------------------------------------------------- /src/components.js: -------------------------------------------------------------------------------- 1 | // springs, all that 2 | import rebound from 'rebound'; 3 | 4 | const noop = () => {}; // default onSpringUpdate 5 | 6 | const setters = { 7 | friction(spring, props){ 8 | spring.getSpringConfig().friction = 9 | rebound.OrigamiValueConverter.frictionFromOrigamiValue(props.friction); 10 | }, 11 | tension(spring, props){ 12 | spring.getSpringConfig().tension = 13 | rebound.OrigamiValueConverter.frictionFromOrigamiValue(props.tension); 14 | }, 15 | // from(spring, props){ 16 | // spring.setCurrentValue(props.from, true).setEndValue(spring.getEndValue()); 17 | // }, 18 | overShootClamping(spring, props){ 19 | spring.setOvershootClampingEnabled(props.overShootClamping); 20 | }, 21 | to(spring, props){ 22 | if(props.atRest){ 23 | spring.setCurrentValue(props.to).setAtRest(); 24 | } 25 | else{ 26 | spring.setEndValue(props.to); 27 | } 28 | } 29 | }; 30 | 31 | export default function components(React){ 32 | class Spring extends React.Component{ 33 | static defaultProps = { 34 | // we use a common 'global' springSystem for perf, but you can pass in your own 35 | springSystem: new rebound.SpringSystem(), 36 | 37 | // from and to are analogous to setCurrentValue() and setEndValue() 38 | from: 0, 39 | to: 0, 40 | 41 | // more rebound options 42 | tension: 50, 43 | friction: 3, 44 | overShootClamping: false, 45 | atRest: false, 46 | onSpringUpdate: noop, 47 | children: () => null 48 | 49 | // todo - velocity? 50 | } 51 | 52 | static propTypes = { 53 | from: React.PropTypes.number, 54 | friction: React.PropTypes.number, 55 | tension: React.PropTypes.number, 56 | to: React.PropTypes.number, 57 | atRest: React.PropTypes.bool, 58 | overShootClamping: React.PropTypes.bool, 59 | children: React.PropTypes.func, 60 | onSpringUpdate: React.PropTypes.func 61 | } 62 | 63 | accept(props, initial){ 64 | Object.keys(props).forEach(k => { 65 | if(setters[k] && (initial || (props[k] !== this.props[k]))){ 66 | setters[k](this.spring, props); 67 | } 68 | }); 69 | } 70 | 71 | state = { 72 | value: this.props.from 73 | } 74 | 75 | shouldComponentUpdate(){ 76 | return true; 77 | // components with 'render callbacks' can/should render 'through'. 78 | } 79 | 80 | componentWillMount() { 81 | // create the spring on mounting. 82 | this.spring = this.props.springSystem.createSpring(this.props.tension, this.props.friction); 83 | 84 | this.spring.addListener({ 85 | onSpringUpdate: () => { 86 | this.setState({ value: this.spring.getCurrentValue() }); 87 | this.props.onSpringUpdate(this.spring); 88 | } 89 | }); 90 | 91 | this.spring.setCurrentValue(this.props.from, true); 92 | 93 | this.accept(this.props, true); 94 | } 95 | 96 | componentWillReceiveProps(nextProps) { 97 | this.accept(nextProps, false); 98 | } 99 | 100 | componentWillUnmount() { 101 | // ...and destroy on onmounting 102 | this.spring.destroy(); 103 | delete this.spring; 104 | } 105 | 106 | render(){ 107 | return this.props.children(this.state.value, this.spring); 108 | } 109 | } 110 | 111 | ///////////////////////////////////////// 112 | 113 | class Springs extends React.Component{ 114 | static defaultProps = { 115 | onSpringUpdate: noop, 116 | children: () => null 117 | } 118 | 119 | static propTypes: { 120 | onSpringUpdate: React.PropTypes.func, 121 | children: React.PropTypes.func 122 | } 123 | 124 | shouldComponentUpdate(){ 125 | return true; 126 | // like above 127 | } 128 | onSpringUpdate(key, spring){ 129 | this.setState({ 130 | [key]: spring.getCurrentValue() 131 | }); 132 | this.props.onSpringUpdate(key, spring); 133 | } 134 | 135 | to(pos, keys, index, value, springs){ 136 | if(index === -1){ 137 | return this.props.children(value, springs); 138 | } 139 | const key = keys[index]; 140 | return this.onSpringUpdate(key, spring)}> 141 | {(val, spring) => this.to(pos, keys, index - 1, (value[key] = val, value), (springs[key] = spring, springs))} 142 | ; 143 | } 144 | 145 | // todo - sort keys alphabetically? 146 | render() { 147 | // what we do here, is break `to` into key value pairs, and then return a nest of s 148 | // React takes care of the boring bits (caching, state, etc) 149 | const {to} = this.props, keys = Object.keys(to); 150 | return this.to(to, keys, keys.length - 1, {}, {}); 151 | } 152 | } 153 | 154 | return {Spring, Springs}; 155 | } 156 | 157 | -------------------------------------------------------------------------------- /examples/demos/app.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {Spring, Springs} from '../../src'; 3 | import rebound from 'rebound'; 4 | 5 | function times (n, fn){ 6 | var arr = []; 7 | for(var i = 0; i < n; i++){ arr.push(fn(i)); } 8 | return arr; 9 | } 10 | 11 | let styles = {}; 12 | 13 | styles.slideshow = { 14 | wrap: { width: 400, height: 400, outline: '1px solid #ccc' }, 15 | bar: { flexDirection: 'row', height: 60, position: 'absolute' }, 16 | slide: { flex: 1, fontSize: 200, alignItems: 'center', justifyContent: 'center' }, 17 | thumb: { justifyContent: 'center', alignItems: 'center', flex: 1, cursor: 'pointer' } 18 | }; 19 | 20 | 21 | // ICK 22 | var win; let winset = () => (win = { width: document.body.offsetWidth }); window.addEventListener('resize', winset); window.addEventListener('load', winset); winset(); 23 | // END ICK 24 | 25 | export const Slideshow = React.createClass({ 26 | getInitialState() { 27 | return { 28 | x: 0, y: 0, hover: -1, active: 0, opacity: 0, selected: 1 29 | }; 30 | }, 31 | getDefaultProps(){ 32 | return {n: 100}; 33 | }, 34 | convert(x, y){ 35 | return Slideshow.convert(this.props.n * 60, 400, 60, 400, x, y); 36 | }, 37 | 38 | render() { 39 | return ( 40 |
this.setState({x: e.pageX - ((win.width - 400) / 2), y: e.pageY - ((this.props.pos * 420) + 20)})} 42 | onMouseEnter={() => this.setState({opacity: 1})} 43 | onMouseLeave={() => this.setState({opacity: 0})}> 44 | {bg => 45 |
46 | {this.state.active} 47 |
}
48 | 49 | {val => 50 |
this.setState({hover: -1})}> 51 | {times(this.props.n, i => 52 | { bg => 53 |
this.setState({hover: i})} 55 | onClick={()=> this.setState({active: i, selected: (Math.random() / 100)})}> 56 | {i} 57 |
58 | }
59 | )} 60 |
61 | }
62 |
63 | ); 64 | }, 65 | statics: { 66 | convert(W, w, H, h, x, y){ 67 | return { 68 | left: -1 * ((W - w) / w) * x, // < - memoization target 69 | top: (h - H) + (((H - h) / h) * y) // < - memoization target 70 | }; 71 | } 72 | } 73 | }); 74 | 75 | styles.followers = { 76 | wrap: { width: 400, height: 400, outline: '1px solid #ccc', marginTop: 20 }, 77 | box: { width: 20, height: 20, position: 'absolute', borderRadius: 10 }, 78 | blue: { backgroundColor: 'blue' }, 79 | red: { backgroundColor: 'red' } 80 | }; 81 | 82 | export const Followers = React.createClass({ 83 | getInitialState() { 84 | return { 85 | x: 0, y: 0 86 | }; 87 | }, 88 | onMouseMove(e){ 89 | this.setState({x: Math.min(400, Math.max(0, e.pageX - ((win.width - 400) / 2))), y: Math.min(400, Math.max(0, e.pageY - ((this.props.pos * 420) + 20)))}); 90 | }, 91 | render() { 92 | return
93 | 94 | {vals =>
} 95 |
96 | 97 | {x => 98 | {y => 99 |
} 100 |
} 101 |
102 |
; 103 | } 104 | }); 105 | 106 | styles.unlock = { 107 | wrap: { width: 400, height: 400, outline: '1px solid #ccc', marginTop: 20, justifyContent: 'center' }, 108 | lockscreen: { width: 200, height: 50, backgroundColor: '#ccc', position: 'absolute', top: '50%', marginTop: -25, left: '50%', 109 | marginLeft: -100, justifyContent: 'center', alignItems: 'center', borderRadius: 30 }, 110 | key: { width: 50, height: 50, position: 'absolute', top: 0, backgroundColor: '#fff', cursor: 'pointer', alignItems: 'center', 111 | justifyContent: 'center', borderRadius: 30, boxShadow: '2px 2px 10px #aaa' }, 112 | main: { backgroundColor: 'black', justifyContent: 'center', alignItems: 'center', color: 'white', flex: 1 }, 113 | lock: { cursor: 'pointer' }, 114 | noSelect: { 'WebkitTouchCallout': 'none', 'WebkitUserSelect': 'none', 'KhtmlUserSelect': 'none', 115 | 'MozUserSelect': 'none', 'MsUserSelect': 'none', 'UserSelect': 'none' } 116 | }; 117 | 118 | export const SlideToUnlock = React.createClass({ 119 | getInitialState() { 120 | return { 121 | x: 0, 122 | unlocking: false, 123 | opacity: 0, 124 | delta: 0 125 | 126 | }; 127 | }, 128 | onMouseMove(e){ 129 | this.setState({x: this.state.unlocking ? Math.min(200, Math.max(0, e.pageX - ((win.width - 200) / 2))) : this.state.x}); 130 | }, 131 | onKeyMouseDown(e){ 132 | let x = Math.min(200, Math.max(0, e.pageX - ((win.width - 200) / 2))) - document.getElementById('keyElement').offsetLeft; 133 | this.setState({ 134 | unlocking: true, 135 | x: x, 136 | delta: x 137 | }); 138 | }, 139 | onMouseUp(){ 140 | this.setState({ 141 | unlocking: false, 142 | opacity: this.state.x > 190 ? 1 : 0, 143 | ...(this.state.x > 190 ? {} : {x: 0, delta: 0}) 144 | }); 145 | }, 146 | render() { 147 | return { val => 148 |
149 |
150 | slide 151 |
>
152 |
153 |
154 |
this.setState({opacity: 0, x: 0, delta: 0})}> 155 | click 156 |
157 |
158 |
159 | }
; 160 | 161 | } 162 | }); 163 | 164 | 165 | styles.root = { alignItems: 'center', paddingTop: 20, paddingBottom: 20 }; 166 | 167 | export const App = React.createClass({ 168 | render() { 169 | return
170 | 171 | 172 | 173 |
; 174 | } 175 | }); 176 | -------------------------------------------------------------------------------- /lib/components.js: -------------------------------------------------------------------------------- 1 | // springs, all that 2 | 'use strict'; 3 | 4 | Object.defineProperty(exports, '__esModule', { 5 | value: true 6 | }); 7 | 8 | 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; }; 9 | 10 | 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; }; })(); 11 | 12 | exports['default'] = components; 13 | 14 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 15 | 16 | function _defineProperty(obj, key, value) { return Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } 17 | 18 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } 19 | 20 | function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } 21 | 22 | var _rebound = require('rebound'); 23 | 24 | var _rebound2 = _interopRequireDefault(_rebound); 25 | 26 | var noop = function noop() {}; // default onSpringUpdate 27 | 28 | var setters = { 29 | friction: function friction(spring, props) { 30 | spring.getSpringConfig().friction = _rebound2['default'].OrigamiValueConverter.frictionFromOrigamiValue(props.friction); 31 | }, 32 | tension: function tension(spring, props) { 33 | spring.getSpringConfig().tension = _rebound2['default'].OrigamiValueConverter.frictionFromOrigamiValue(props.tension); 34 | }, 35 | // from(spring, props){ 36 | // spring.setCurrentValue(props.from, true).setEndValue(spring.getEndValue()); 37 | // }, 38 | overShootClamping: function overShootClamping(spring, props) { 39 | spring.setOvershootClampingEnabled(props.overShootClamping); 40 | }, 41 | to: function to(spring, props) { 42 | if (props.atRest) { 43 | spring.setCurrentValue(props.to).setAtRest(); 44 | } else { 45 | spring.setEndValue(props.to); 46 | } 47 | } 48 | }; 49 | 50 | function components(React) { 51 | var Spring = (function (_React$Component) { 52 | function Spring() { 53 | _classCallCheck(this, Spring); 54 | 55 | if (_React$Component != null) { 56 | _React$Component.apply(this, arguments); 57 | } 58 | 59 | this.state = { 60 | value: this.props.from 61 | }; 62 | } 63 | 64 | _inherits(Spring, _React$Component); 65 | 66 | _createClass(Spring, [{ 67 | key: 'accept', 68 | value: function accept(props, initial) { 69 | var _this = this; 70 | 71 | Object.keys(props).forEach(function (k) { 72 | if (setters[k] && (initial || props[k] !== _this.props[k])) { 73 | setters[k](_this.spring, props); 74 | } 75 | }); 76 | } 77 | }, { 78 | key: 'shouldComponentUpdate', 79 | value: function shouldComponentUpdate() { 80 | return true; 81 | // components with 'render callbacks' can/should render 'through'. 82 | } 83 | }, { 84 | key: 'componentWillMount', 85 | value: function componentWillMount() { 86 | var _this2 = this; 87 | 88 | // create the spring on mounting. 89 | this.spring = this.props.springSystem.createSpring(this.props.tension, this.props.friction).addListener({ 90 | onSpringUpdate: function onSpringUpdate() { 91 | _this2.setState({ value: _this2.spring.getCurrentValue() }); 92 | _this2.props.onSpringUpdate(_this2.spring); 93 | } 94 | }).setCurrentValue(this.props.from, true); 95 | 96 | this.accept(this.props, true); 97 | } 98 | }, { 99 | key: 'componentWillReceiveProps', 100 | value: function componentWillReceiveProps(nextProps) { 101 | this.accept(nextProps, false); 102 | } 103 | }, { 104 | key: 'componentWillUnmount', 105 | value: function componentWillUnmount() { 106 | // ...and destroy on onmounting 107 | this.spring.destroy(); 108 | delete this.spring; 109 | } 110 | }, { 111 | key: 'render', 112 | value: function render() { 113 | return this.props.children(this.state.value, this.spring); 114 | } 115 | }], [{ 116 | key: 'defaultProps', 117 | value: { 118 | // we use a common 'global' springSystem for perf, but you can pass in your own 119 | springSystem: new _rebound2['default'].SpringSystem(), 120 | 121 | // from and to are analogous to setCurrentValue() and setEndValue() 122 | from: 0, 123 | to: 0, 124 | 125 | // more rebound options 126 | tension: 50, 127 | friction: 3, 128 | overShootClamping: false, 129 | atRest: false, 130 | onSpringUpdate: noop, 131 | children: function children() { 132 | return null 133 | 134 | // todo - velocity? 135 | ; 136 | } }, 137 | enumerable: true 138 | }, { 139 | key: 'propTypes', 140 | value: { 141 | from: React.PropTypes.number, 142 | friction: React.PropTypes.number, 143 | tension: React.PropTypes.number, 144 | to: React.PropTypes.number, 145 | atRest: React.PropTypes.bool, 146 | overShootClamping: React.PropTypes.bool, 147 | children: React.PropTypes.func, 148 | onSpringUpdate: React.PropTypes.func 149 | }, 150 | enumerable: true 151 | }]); 152 | 153 | return Spring; 154 | })(React.Component); 155 | 156 | var Springs = (function (_React$Component2) { 157 | function Springs() { 158 | _classCallCheck(this, Springs); 159 | 160 | if (_React$Component2 != null) { 161 | _React$Component2.apply(this, arguments); 162 | } 163 | } 164 | 165 | _inherits(Springs, _React$Component2); 166 | 167 | _createClass(Springs, [{ 168 | key: 'shouldComponentUpdate', 169 | value: function shouldComponentUpdate() { 170 | return true; 171 | // like above 172 | } 173 | }, { 174 | key: 'onSpringUpdate', 175 | value: function onSpringUpdate(key, spring) { 176 | this.setState(_defineProperty({}, key, spring.getCurrentValue())); 177 | this.props.onSpringUpdate(key, spring); 178 | } 179 | }, { 180 | key: 'to', 181 | value: function to(pos, keys, index, value, springs) { 182 | var _this3 = this; 183 | 184 | if (index === -1) { 185 | return this.props.children(value, springs); 186 | } 187 | var key = keys[index]; 188 | return React.createElement( 189 | Spring, 190 | _extends({}, this.props, { key: key, to: pos[key], onSpringUpdate: function (spring) { 191 | return _this3.onSpringUpdate(key, spring); 192 | } }), 193 | function (val, spring) { 194 | return _this3.to(pos, keys, index - 1, (value[key] = val, value), (springs[key] = spring, springs)); 195 | } 196 | ); 197 | } 198 | }, { 199 | key: 'render', 200 | 201 | // todo - sort keys alphabetically? 202 | value: function render() { 203 | // what we do here, is break `to` into key value pairs, and then return a nest of s 204 | // React takes care of the boring bits (caching, state, etc) 205 | var to = this.props.to;var keys = Object.keys(to); 206 | return this.to(to, keys, keys.length - 1, {}, {}); 207 | } 208 | }], [{ 209 | key: 'defaultProps', 210 | value: { 211 | onSpringUpdate: noop, 212 | children: function children() { 213 | return null; 214 | } 215 | }, 216 | enumerable: true 217 | }]); 218 | 219 | return Springs; 220 | })(React.Component); 221 | 222 | return { Spring: Spring, Springs: Springs }; 223 | } 224 | 225 | module.exports = exports['default']; -------------------------------------------------------------------------------- /examples/demos/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 | } --------------------------------------------------------------------------------