├── .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 | 
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 |
--------------------------------------------------------------------------------