├── .editorconfig ├── .gitignore ├── .jshintrc ├── .travis.yml ├── README.md ├── bower.json ├── demo-animation.gif ├── dist └── react-transition.js ├── index.html ├── karma.conf.js ├── lib ├── index.js └── react-transition.js ├── package.json ├── spec ├── react-transition.spec.jsx └── spec-helper.js ├── webpack.build.config.js └── webpack.config.js /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | indent_style = space 3 | indent_size = 2 4 | charset = utf-8 5 | trim_trailing_whitespace = true 6 | insert_final_newline = true 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | bower_components 3 | .DS_Store 4 | *.log 5 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "undef": true, 3 | "unused": true, 4 | "indent": 2, 5 | "noempty": true, 6 | "browser": true, 7 | "globals": { 8 | "module": false, 9 | "require": true, 10 | "jasmine": false, 11 | "spyOn": false, 12 | "describe": false, 13 | "beforeEach": false, 14 | "afterEach": false, 15 | "expect": false, 16 | "it": false, 17 | "xit": false 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.11" 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Transition Component 2 | 3 | [![Build Status](https://secure.travis-ci.org/pirelenito/react-transition.png)](https://travis-ci.org/pirelenito/react-transition) 4 | 5 | [React.js](http://reactjs.com/) component that will apply transitions on any prop change. Was created to be used on drawing SVG charts with [D3](http://d3js.org/), but can be used for anything. 6 | 7 | ![Demo](https://rawgit.com/pirelenito/react-transition/master/demo-animation.gif) 8 | 9 | Check the [live demo](https://rawgit.com/pirelenito/react-transition/master/index.html). 10 | 11 | ## Usage 12 | 13 | Here is an example usage: 14 | 15 | ```js 16 | 17 | ``` 18 | 19 | The `ReactTransition` component will actualy render a `div`, and it will apply the `bounce` ease function with a duration of `500` milisseconds on any [componentWillReceiveProps](http://facebook.github.io/react/docs/component-specs.html#updating-componentwillreceiveprops) of the `style` property. 20 | 21 | ## Install 22 | 23 | Install with [NPM](http://npmjs.org/): 24 | 25 | ```bash 26 | npm install react-transition 27 | ``` 28 | 29 | It also is available as an UMD. Having support for [AMD](https://github.com/amdjs/amdjs-api/wiki/AMD), [CommonJS](http://wiki.commonjs.org/wiki/CommonJS) and globals, which can be installed through [Bower](http://bower.io/): 30 | 31 | ```bash 32 | bower install react-transition 33 | ``` 34 | 35 | Or simply dowloading the [dist/react-transition.js](https://raw.githubusercontent.com/pirelenito/react-transition/master/dist/react-transition.js) distribution file and add as a script tag in your HTML (it will expose a `ReactTransition` global variable) allongsite with its depencencies: 36 | 37 | ```html 38 | 39 | 40 | 41 | ``` 42 | 43 | ## Easing functions 44 | 45 | [Check D3's documentation](https://github.com/mbostock/d3/wiki/Transitions#d3_ease) to see the available *easing* function. But some examples are: 46 | 47 | * linear - the identity function, t. 48 | * poly(k) - raises t to the specified power k (e.g., 3). 49 | * quad - equivalent to poly(2). 50 | * cubic - equivalent to poly(3). 51 | * sin - applies the trigonometric function sin. 52 | * exp - raises 2 to a power based on t. 53 | * circle - the quarter circle. 54 | * elastic(a, p) - simulates an elastic band; 55 | * back(s) - simulates backing into a parking space. 56 | * bounce - simulates a bouncy collision. 57 | 58 | ## Author 59 | 60 | [Paulo Ragonha](http://paulo.ragonha.me/) 61 | 62 | ## Similar work 63 | 64 | You can check [@chenglou](https://github.com/chenglou)'s approach to animation using mixins: [react-tween-state](https://github.com/chenglou/react-tween-state). 65 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-transition", 3 | "version": "1.0.3", 4 | "authors": [ 5 | "Paulo Ragonha " 6 | ], 7 | "description": "React Component to perform transitions on prop changes", 8 | "main": "dist/react-transition.js", 9 | "moduleType": [ 10 | "amd", 11 | "globals", 12 | "node" 13 | ], 14 | "keywords": [ 15 | "react", 16 | "transition", 17 | "reactjs", 18 | "react-component", 19 | "animation", 20 | "tween" 21 | ], 22 | "license": "MIT", 23 | "ignore": [ 24 | "**/.*", 25 | "node_modules", 26 | "bower_components", 27 | "test", 28 | "tests" 29 | ], 30 | "dependencies": { 31 | "react": "~0.12.0", 32 | "d3": "~3.4.11" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /demo-animation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pirelenito/react-transition/f33c64628bcd0f69765218c3d3497b12cceb6a26/demo-animation.gif -------------------------------------------------------------------------------- /dist/react-transition.js: -------------------------------------------------------------------------------- 1 | !function(t,n){"object"==typeof exports&&"object"==typeof module?module.exports=n(require("React"),require("d3")):"function"==typeof define&&define.amd?define(["React","d3"],n):"object"==typeof exports?exports.ReactTransition=n(require("React"),require("d3")):t.ReactTransition=n(t.React,t.d3)}(this,function(){return function(t){function n(e){if(r[e])return r[e].exports;var o=r[e]={exports:{},id:e,loaded:!1};return t[e].call(o.exports,o,o.exports,n),o.loaded=!0,o.exports}var r={};return n.m=t,n.c=r,n.p="",n(0)}([function(t,n,r){t.exports=r(2)},function(t){function n(t){return"string"==typeof t?t:null==t?"":t+""}function r(t){return t&&"object"==typeof t||!1}function e(t){return"number"==typeof t&&t>-1&&t%1==0&&m>=t}function o(t){return null==t?!1:y.call(t)==c?g.test(l.call(t)):r(t)&&f.test(t)||!1}function u(t){return t=n(t),t&&p.test(t)?t.replace(a,"\\$&"):t}var i="[object Array]",c="[object Function]",f=/^\[object .+?Constructor\]$/,a=/[.*+?^${}()|[\]\/\\]/g,p=RegExp(a.source),s=Object.prototype,l=Function.prototype.toString,y=s.toString,g=RegExp("^"+u(y).replace(/toString|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$"),h=o(h=Array.isArray)&&h,m=Math.pow(2,53)-1,v=h||function(t){return r(t)&&e(t.length)&&y.call(t)==i||!1};t.exports=v},function(t,n,r){var e=r(10),o=r(11),u=r(3);t.exports=e.createClass({getDefaultProps:function(){return{ease:"cubic-in-out",duration:400}},startAnimation:function(){function t(){if(!o&&!u.stopAnimation){var r=n();r>1&&(r=1,o=!0),u.animate(r),window.requestAnimationFrame(t)}}function n(){var t=(new Date).getTime();return(t-r)/(e-r)||0}var r=(new Date).getTime(),e=r+parseInt(this.props.duration,10),o=!1,u=this;t()},componentWillUnmount:function(){this.stopAnimation=!0},getInitialState:function(){return this.props},componentWillReceiveProps:function(t){var n=this,r={};u(t,function(e,u){"component"===u||"children"===u||"ease"===u||"duration"===u||u.match(/^on(.+)/)||(r[u]=o.interpolate(n.state[u],t[u]))}),this.interpolators=r,this.startAnimation()},animate:function(t){var n={},r=o.ease(this.props.ease);u(this.interpolators,function(e,o){n[o]=e(r(t))}),this.setState(n)},render:function(){return e.createElement(this.props.component,this.state,this.props.children)}})},function(t,n,r){function e(t,n,r){return"function"==typeof n&&"undefined"==typeof r&&c(t)?o(t,n):u(t,i(n,r,3))}var o=r(4),u=r(5),i=r(9),c=r(1);t.exports=e},function(t){function n(t,n){for(var r=-1,e=t.length;++r-1&&t%1==0&&p>=t}function c(t){return f(t)?t:Object(t)}function f(t){var n=typeof t;return"function"==n||t&&"object"==n||!1}var a=r(6),p=Math.pow(2,53)-1;t.exports=e},function(t,n,r){function e(t,n){return t=+t,n=null==n?h:n,t>-1&&t%1==0&&n>t}function o(t){return"number"==typeof t&&t>-1&&t%1==0&&h>=t}function u(t){for(var n=c(t),r=n.length,u=r&&t.length,i=u&&o(u)&&(a(t)||m.nonEnumArgs&&f(t)),p=-1,s=[];++p0;++u-1&&t%1==0&&c>=t}function e(t){var e=n(t)?t.length:void 0;return r(e)&&i.call(t)==o||!1}var o="[object Arguments]",u=Object.prototype,i=u.toString,c=Math.pow(2,53)-1;t.exports=e},function(t){function n(t){return"string"==typeof t?t:null==t?"":t+""}function r(t){return t&&"object"==typeof t||!1}function e(t){return null==t?!1:s.call(t)==u?l.test(p.call(t)):r(t)&&i.test(t)||!1}function o(t){return t=n(t),t&&f.test(t)?t.replace(c,"\\$&"):t}var u="[object Function]",i=/^\[object .+?Constructor\]$/,c=/[.*+?^${}()|[\]\/\\]/g,f=RegExp(c.source),a=Object.prototype,p=Function.prototype.toString,s=a.toString,l=RegExp("^"+o(s).replace(/toString|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$");t.exports=e},function(t){function n(t,n,e){if("function"!=typeof t)return r;if("undefined"==typeof n)return t;switch(e){case 1:return function(r){return t.call(n,r)};case 3:return function(r,e,o){return t.call(n,r,e,o)};case 4:return function(r,e,o,u){return t.call(n,r,e,o,u)};case 5:return function(r,e,o,u,i){return t.call(n,r,e,o,u,i)}}return function(){return t.apply(n,arguments)}}function r(t){return t}t.exports=n},function(t){t.exports=React},function(t){t.exports=d3}])}); -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | React Transition example 5 | 6 | 7 | 8 | 9 | 10 | 37 | 38 | 39 |
40 | 41 | 71 | 72 |

Example based on react-tween-state

73 | 74 | 75 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | /* jshint node: true */ 2 | module.exports = function(config) { 3 | config.set({ 4 | basePath: '.', 5 | 6 | frameworks: ['jasmine'], 7 | browsers: ['PhantomJS'], 8 | 9 | files: [ 10 | // shim to workaroud PhantomJS 1.x lack of `bind` support 11 | // see: https://github.com/ariya/phantomjs/issues/10522 12 | 'node_modules/es5-shim/es5-shim.js', 13 | 14 | // React is an external dependency of the component 15 | 'node_modules/react/dist/react-with-addons.js', 16 | 17 | 'spec/spec-helper.js', 18 | 'spec/**/*.spec.*', 19 | { pattern: 'lib/**/*', watched: true, included: false } 20 | ], 21 | 22 | preprocessors: { 23 | // add webpack as preprocessor 24 | 'spec/**/*.spec.*': ['webpack'] 25 | }, 26 | 27 | webpack: require('./webpack.config.js'), 28 | 29 | webpackServer: { 30 | noInfo: true 31 | }, 32 | 33 | singleRun: true 34 | }); 35 | }; 36 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./react-transition'); 2 | -------------------------------------------------------------------------------- /lib/react-transition.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var d3 = require('d3'); 3 | var forEach = require('lodash.foreach'); 4 | 5 | 6 | /** 7 | Transition Component. It applies transitions on any prop change. Uses D3 interpolator functions underneath. 8 | 9 | @param props.component underlying component that will be initialized and animated 10 | @param [props.ease='linear'] easying function between transition states (available options: https://github.com/mbostock/d3/wiki/Transitions#d3_ease) 11 | @param [props.duration='500'] duration of the transition 12 | 13 | Usage: 14 | 15 | */ 16 | module.exports = React.createClass({ 17 | getDefaultProps: function () { 18 | return { 19 | ease: 'cubic-in-out', 20 | duration: 400 21 | }; 22 | }, 23 | 24 | startAnimation: function () { 25 | var start = new Date().getTime(), 26 | end = start + parseInt(this.props.duration, 10), 27 | finished = false, 28 | that = this; 29 | 30 | function animate () { 31 | if (finished || that.stopAnimation) { return; } 32 | var now = t(); 33 | 34 | if (now > 1) { now = 1; finished = true; } 35 | 36 | that.animate(now); 37 | window.requestAnimationFrame(animate); 38 | } 39 | 40 | function t () { 41 | var now = new Date().getTime(); 42 | 43 | return (now - start) / (end - start) || 0; 44 | } 45 | 46 | animate(); 47 | }, 48 | 49 | componentWillUnmount: function () { 50 | // stops the animation in progress 51 | this.stopAnimation = true; 52 | }, 53 | 54 | getInitialState: function () { 55 | return this.props; 56 | }, 57 | 58 | componentWillReceiveProps: function (newProps) { 59 | var that = this; 60 | 61 | var interpolators = {}; 62 | 63 | forEach(newProps, function (value, propName) { 64 | if (propName === 'component' || propName === 'children' || propName === 'ease' || propName === 'duration' || propName.match(/^on(.+)/)) { return; } 65 | interpolators[propName] = d3.interpolate(that.state[propName], newProps[propName]); 66 | }); 67 | 68 | this.interpolators = interpolators; 69 | this.startAnimation(); 70 | }, 71 | 72 | animate: function (t) { 73 | var newState = {}, 74 | ease = d3.ease(this.props.ease); 75 | 76 | forEach(this.interpolators, function (interpolator, propName) { 77 | newState[propName] = interpolator(ease(t)); 78 | }); 79 | 80 | this.setState(newState); 81 | }, 82 | 83 | render: function () { 84 | return React.createElement(this.props.component, this.state, this.props.children); 85 | } 86 | }); 87 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-transition", 3 | "version": "1.0.3", 4 | "description": "React Component to perform transitions on prop changes", 5 | "main": "lib/index.js", 6 | "scripts": { 7 | "test": "./node_modules/.bin/jsxhint lib/ spec/ && ./node_modules/.bin/jshint lib/ spec/ && ./node_modules/karma/bin/karma start karma.conf.js", 8 | "watch-test": "./node_modules/karma/bin/karma start karma.conf.js --auto-watch --no-single-run", 9 | "build": "./node_modules/.bin/webpack --config webpack.build.config.js -p", 10 | "dev": "./node_modules/.bin/webpack-dev-server --config webpack.build.config.js --inline --hot" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git@github.com:pirelenito/react-transition.git" 15 | }, 16 | "keywords": [ 17 | "react", 18 | "reactjs", 19 | "react-component", 20 | "transition", 21 | "animation", 22 | "tween" 23 | ], 24 | "author": "Paulo Ragonha", 25 | "license": "MIT", 26 | "bugs": { 27 | "url": "https://github.com/pirelenito/react-transition/issues" 28 | }, 29 | "homepage": "https://github.com/pirelenito/react-transition", 30 | "devDependencies": { 31 | "css-loader": "^0.9.1", 32 | "es5-shim": "^4.1.0", 33 | "jasmine": "^2.2.1", 34 | "jasmine-core": "^2.2.0", 35 | "jshint": "^2.6.0", 36 | "jsx-loader": "^0.12.2", 37 | "jsxhint": "^0.10.0", 38 | "karma": "^0.12.31", 39 | "karma-jasmine": "^0.3.5", 40 | "karma-phantomjs-launcher": "^0.1.4", 41 | "karma-webpack": "^1.5.0", 42 | "sass-loader": "^0.4.0-beta", 43 | "style-loader": "^0.8.3", 44 | "webpack": "^1.5.3", 45 | "webpack-dev-server": "^1.7.0" 46 | }, 47 | "dependencies": { 48 | "d3": "^3.5.5", 49 | "lodash.foreach": "^3.0.1", 50 | "react": "^0.12.0", 51 | "underscore": "^1.6.0" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /spec/react-transition.spec.jsx: -------------------------------------------------------------------------------- 1 | var React = require('react/addons'); 2 | var TestUtils = React.addons.TestUtils; 3 | var ReactTransition = require('../lib/react-transition'); 4 | 5 | 6 | describe("ReactTransition", function() { 7 | var component; 8 | 9 | beforeEach(function() { 10 | component = TestUtils.renderIntoDocument( 11 | 12 | ); 13 | }); 14 | 15 | it("should render", function() { 16 | expect(component.getDOMNode().className).toEqual('react-transition'); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /spec/spec-helper.js: -------------------------------------------------------------------------------- 1 | beforeEach(function() { 2 | 3 | }); 4 | -------------------------------------------------------------------------------- /webpack.build.config.js: -------------------------------------------------------------------------------- 1 | /* jshint node: true */ 2 | var config = require('./webpack.config'); 3 | 4 | 5 | config.externals = { 6 | 'react': 'var React', 7 | 'd3': 'var d3' 8 | }; 9 | 10 | 11 | module.exports = config; 12 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | /* jshint node: true */ 2 | var path = require('path'); 3 | 4 | 5 | module.exports = { 6 | context: path.join(__dirname), 7 | entry: './lib/index.js', 8 | 9 | output: { 10 | path: path.join(__dirname), 11 | filename: 'dist/react-transition.js', 12 | libraryTarget: 'umd', 13 | library: 'ReactTransition' 14 | }, 15 | 16 | module: { 17 | loaders: [ 18 | { 19 | test: /\.jsx$/, 20 | loader: 'jsx-loader?harmony' 21 | } 22 | ] 23 | } 24 | }; 25 | --------------------------------------------------------------------------------