├── .gitignore ├── .babelrc ├── src ├── reducers │ ├── index.js │ └── circle.js ├── actions │ ├── index.js │ ├── ActionTypes.js │ └── CircleActions.js ├── components │ ├── index.js │ ├── CirclesHandler.jsx │ └── Circles.jsx ├── index.js └── App.js ├── screenshot.png ├── dist └── index.html ├── index.html ├── server.js ├── README.md ├── webpack.config.js ├── package.json └── .eslintrc /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "stage": 0 3 | } 4 | -------------------------------------------------------------------------------- /src/reducers/index.js: -------------------------------------------------------------------------------- 1 | export * from './circle'; 2 | -------------------------------------------------------------------------------- /src/actions/index.js: -------------------------------------------------------------------------------- 1 | export * from './CircleActions'; 2 | -------------------------------------------------------------------------------- /src/actions/ActionTypes.js: -------------------------------------------------------------------------------- 1 | export const CIRCLE_ADD = 'CIRCLE_ADD'; 2 | -------------------------------------------------------------------------------- /src/components/index.js: -------------------------------------------------------------------------------- 1 | export * from './Circles'; 2 | export * from './CirclesHandler'; 3 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartgryszko/react-motion-example/HEAD/screenshot.png -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import App from './App'; 3 | 4 | React.render(, document.getElementById('root')); 5 | -------------------------------------------------------------------------------- /src/actions/CircleActions.js: -------------------------------------------------------------------------------- 1 | import { CIRCLE_ADD } from './ActionTypes'; 2 | 3 | export function addCircle() { 4 | return { 5 | type: CIRCLE_ADD, 6 | }; 7 | } 8 | -------------------------------------------------------------------------------- /dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | React Motion – TransitionSpring Demo 4 | 5 | 6 |
7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | React Motion – TransitionSpring Demo 4 | 5 | 6 |
7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | var WebpackDevServer = require('webpack-dev-server'); 3 | var config = require('./webpack.config'); 4 | var port = process.env.PORT || 3000; 5 | 6 | new WebpackDevServer(webpack(config), { 7 | publicPath: config.output.publicPath, 8 | hot: true, 9 | historyApiFallback: true 10 | }).listen(port, 'localhost', function (err, result) { 11 | if (err) { 12 | console.log(err); 13 | } 14 | 15 | console.log('Listening at localhost:' + port); 16 | }); 17 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { combineReducers, createStore } from 'redux'; 3 | import { Provider } from 'react-redux'; 4 | import * as reducers from './reducers'; 5 | import { CirclesHandler } from './components'; 6 | 7 | const reducer = combineReducers(reducers); 8 | const store = createStore(reducer); 9 | 10 | export default class App extends Component { 11 | render() { 12 | return ( 13 | 14 | {() => } 15 | 16 | ); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/reducers/circle.js: -------------------------------------------------------------------------------- 1 | import { Map } from 'immutable'; 2 | import { CIRCLE_ADD } from '../actions/ActionTypes'; 3 | 4 | 5 | const initialState = Map({ 6 | circles: Map({ 0: true }), 7 | lastCircleId: 0, 8 | }); 9 | 10 | export function circle(state = initialState, action = {}) { 11 | switch (action.type) { 12 | case CIRCLE_ADD: 13 | return state.withMutations(map => { 14 | const lastCircleId = map.get('lastCircleId'); 15 | const circles = map.get('circles').set(lastCircleId + 1, true); 16 | return map.set('circles', circles).set('lastCircleId', lastCircleId + 1); 17 | }); 18 | default: 19 | return state; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | react-motion-example 2 | ===================== 3 | 4 | Example of how to use `TransitionSpring` from [react-motion](https://github.com/chenglou/react-motion) library, 5 | based on its demo. Also uses [redux](https://github.com/gaearon/redux) and [immutable-js](https://github.com/facebook/immutable-js). 6 | Hot-reloadable. 7 | 8 | [ONLINE DEMO](https://cdn.rawgit.com/bgryszko/react-motion-example/master/dist/index.html) 9 | 10 | ### Usage 11 | 12 | ``` 13 | npm install 14 | npm start 15 | open http://localhost:3000 16 | ``` 17 | 18 | Now you can click to add more and more circles. 19 | 20 | ### Screenshot 21 | ![Screenshot](https://cdn.rawgit.com/bgryszko/react-motion-example/master/screenshot.png) 22 | -------------------------------------------------------------------------------- /src/components/CirclesHandler.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { bindActionCreators } from 'redux'; 4 | import { Circles } from '.'; 5 | import * as circleActions from '../actions/CircleActions'; 6 | 7 | @connect(state => ({ 8 | circles: state.circle.get('circles'), 9 | })) 10 | export class CirclesHandler extends Component { 11 | static propTypes = { 12 | circles: PropTypes.object, 13 | dispatch: PropTypes.func, 14 | } 15 | 16 | render() { 17 | const { dispatch, ...other} = this.props; 18 | 19 | return ( 20 |
21 | 22 |
23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var webpack = require('webpack'); 3 | 4 | var isProd = process.env.NODE_ENV === 'production'; 5 | var port = process.env.PORT || 3000; 6 | var webpackHost = process.env.WEBPACK_HOST || 'localhost'; 7 | var devtool; 8 | var entry = ['./src/index']; 9 | var loaders = ['babel']; 10 | var plugins = []; 11 | 12 | if (!isProd) { 13 | entry.concat([ 14 | 'webpack-dev-server/client?http://' + webpackHost + ':' + port, 15 | 'webpack/hot/only-dev-server' 16 | ]); 17 | loaders.concat([ 18 | 'react-hot' 19 | ]); 20 | plugins.concat([ 21 | new webpack.HotModuleReplacementPlugin(), 22 | new webpack.NoErrorsPlugin() 23 | ]); 24 | devtool = 'eval'; 25 | } else { 26 | plugins.concat([ 27 | new webpack.optimize.OccurenceOrderPlugin() 28 | ]); 29 | } 30 | 31 | module.exports = { 32 | devtool: devtool, 33 | entry: entry, 34 | output: { 35 | path: path.join(__dirname, 'dist'), 36 | filename: 'bundle.js', 37 | publicPath: '/static/' 38 | }, 39 | plugins: plugins, 40 | resolve: { 41 | extensions: ['', '.js', '.jsx'] 42 | }, 43 | module: { 44 | loaders: [{ 45 | test: /\.jsx?$/, 46 | loaders: loaders, 47 | include: path.join(__dirname, 'src') 48 | }] 49 | } 50 | }; 51 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-motion-example", 3 | "version": "1.0.0", 4 | "description": "Example of how to use TransitionSpring from react-motion", 5 | "scripts": { 6 | "start": "node server.js", 7 | "build": "NODE_ENV=production webpack -p", 8 | "lint": "eslint src" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/bgryszko/react-motion-example.git" 13 | }, 14 | "keywords": [ 15 | "react", 16 | "reactjs", 17 | "react-motion", 18 | "hot", 19 | "redux", 20 | "animation" 21 | ], 22 | "author": "Bart Gryszko (http://github.com/bgryszko)", 23 | "license": "MIT", 24 | "bugs": { 25 | "url": "https://github.com/bgryszko/react-motion-example/issues" 26 | }, 27 | "homepage": "https://github.com/bgryszko/react-motion-example", 28 | "devDependencies": { 29 | "babel": "^5.6.14", 30 | "babel-core": "^5.6.20", 31 | "babel-eslint": "^3.1.23", 32 | "babel-loader": "^5.3.1", 33 | "eslint-config-airbnb": "0.0.6", 34 | "eslint-plugin-react": "^2.3.0", 35 | "immutable": "^3.7.4", 36 | "react-hot-loader": "^1.2.7", 37 | "react-motion": "^0.1.0", 38 | "react-redux": "^1.0.0-alpha", 39 | "redux": "^1.0.0-rc", 40 | "webpack": "^1.9.6", 41 | "webpack-dev-server": "^1.8.2" 42 | }, 43 | "dependencies": { 44 | "react": "^0.13.0" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "eslint-config-airbnb", 3 | "env": { 4 | "es6": true, 5 | "mocha": true 6 | }, 7 | "plugins": [ 8 | "react" 9 | ], 10 | "rules": { 11 | "comma-dangle": [2, "always-multiline"], 12 | "no-use-before-define": 0, 13 | "no-eq-null": 0, 14 | "new-cap": 0, 15 | "block-scoped-var": 0, // until fixed https://github.com/eslint/eslint/issues/2253 16 | 17 | "react/jsx-no-undef": 2, 18 | "react/jsx-quotes": [2, "double", "avoid-escape"], 19 | "react/jsx-uses-react": 2, 20 | "react/jsx-uses-vars": 2, 21 | "react/no-did-mount-set-state": 2, 22 | "react/no-did-update-set-state": 2, 23 | "react/no-unknown-property": 2, 24 | "react/prop-types": 2, 25 | "react/react-in-jsx-scope": 2, 26 | "react/self-closing-comp": 2, 27 | "react/wrap-multilines": 2, 28 | "react/sort-comp": [ 29 | 2, { 30 | "order": [ 31 | "displayName", 32 | "propTypes", 33 | "contextTypes", 34 | "childContextTypes", 35 | "mixins", 36 | "statics", 37 | "defaultProps", 38 | "getDefaultProps", 39 | "getInitialState", 40 | "getChildContext", 41 | "componentWillMount", 42 | "componentDidMount", 43 | "componentWillReceiveProps", 44 | "shouldComponentUpdate", 45 | "componentWillUpdate", 46 | "componentDidUpdate", 47 | "componentWillUnmount", 48 | "/^on.+$/", 49 | "/^get.+$/", 50 | "/^render.+$/", 51 | "/^.+$/", // All other methods go here 52 | "render" 53 | ] 54 | } 55 | ] 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/components/Circles.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | import { TransitionSpring } from 'react-motion'; 3 | 4 | 5 | export class Circles extends Component { 6 | static propTypes = { 7 | circles: PropTypes.object, 8 | addCircle: PropTypes.func, 9 | } 10 | 11 | state = { 12 | mouse: [300, 300], 13 | } 14 | 15 | handleMouseMove({pageX, pageY}) { 16 | this.setState({mouse: [pageX, pageY]}); 17 | } 18 | 19 | handleTouchMove({touches}) { 20 | this.handleMouseMove(touches[0]); 21 | } 22 | 23 | handleClick() { 24 | const { addCircle } = this.props; 25 | addCircle(); 26 | } 27 | 28 | getValues(currentPositions) { 29 | const { circles } = this.props; 30 | let newPositions; 31 | 32 | if (currentPositions == null) { 33 | newPositions = circles.map(() => ({ val: [0, 0] })); 34 | } else { 35 | newPositions = circles.map((circle, rawKey) => { 36 | const key = parseInt(rawKey, 10); 37 | 38 | return key === 0 39 | ? { val: this.state.mouse, config: [120, 17] } 40 | : { val: currentPositions[key - 1].val, config: [120, 17] }; 41 | }); 42 | } 43 | 44 | return newPositions.toObject(); 45 | } 46 | 47 | renderCircle(currentPositions) { 48 | if (!currentPositions) { 49 | return null; 50 | } 51 | 52 | const currentPositionsKeys = Object.keys(currentPositions); 53 | return currentPositionsKeys.map(key => { 54 | const [x, y] = currentPositions[key].val; 55 | return ( 56 |
65 | {key} 66 |
67 | ); 68 | }); 69 | } 70 | 71 | render() { 72 | const { circles } = this.props; 73 | 74 | return ( 75 | 76 | {currentPositions => 77 |
82 | {this.renderCircle(currentPositions)} 83 |
84 | { circles.size } 85 |
86 |
87 | } 88 |
89 | ); 90 | } 91 | } 92 | 93 | const styles = { 94 | scene: { 95 | width: '100%', 96 | height: '100%', 97 | position: 'absolute', 98 | background: 'rgb(230, 230, 230)', 99 | userSelect: 'none', 100 | WebkitUserSelect: 'none', 101 | }, 102 | 103 | counter: { 104 | fontSize: 120, 105 | color: 'rgba(255, 255, 255, 0.5)', 106 | position: 'absolute', 107 | WebkitTransform: `translate3d(-50%, -50%, 0)`, 108 | transform: `translate3d(-50%, -50%, 0)`, 109 | left: '50%', 110 | top: '50%', 111 | }, 112 | 113 | circle: { 114 | borderRadius: 99, 115 | width: 50, 116 | height: 50, 117 | position: 'absolute', 118 | backgroundSize: 50, 119 | textAlign: 'center', 120 | color: 'rgba(255, 255, 255, 0.5)', 121 | lineHeight: 3, 122 | }, 123 | }; 124 | --------------------------------------------------------------------------------