From cfc56a1354960260d6e7d40e401e7b1ba43e3a7e Mon Sep 17 00:00:00 2001 From: Nicolas Gallagher Date: Sun, 1 Jan 2017 19:47:01 -0800 Subject: [PATCH] [change] ActivityIndicator using CSS animations Ref #299 --- .../ActivityIndicatorExample.js | 10 +- .../renderApplication-test.js.snap | 1 + src/apis/StyleSheet/initialize.js | 8 + .../__snapshots__/index-test.js.snap | 226 ++++++++++++++++++ .../ActivityIndicator/__tests__/index-test.js | 14 +- src/components/ActivityIndicator/index.js | 71 ++---- src/components/View/ViewStylePropTypes.js | 2 + src/propTypes/AnimationPropTypes.js | 16 ++ 8 files changed, 291 insertions(+), 57 deletions(-) create mode 100644 src/components/ActivityIndicator/__tests__/__snapshots__/index-test.js.snap create mode 100644 src/propTypes/AnimationPropTypes.js diff --git a/examples/components/ActivityIndicator/ActivityIndicatorExample.js b/examples/components/ActivityIndicator/ActivityIndicatorExample.js index c19c12d7..165c3564 100644 --- a/examples/components/ActivityIndicator/ActivityIndicatorExample.js +++ b/examples/components/ActivityIndicator/ActivityIndicatorExample.js @@ -50,7 +50,8 @@ const ToggleAnimatingActivityIndicator = React.createClass({ return ( ); @@ -121,7 +122,12 @@ const examples = [ { title: 'Start/stop', render() { - return ; + return ( + + + + + ); } }, { diff --git a/src/apis/AppRegistry/__tests__/__snapshots__/renderApplication-test.js.snap b/src/apis/AppRegistry/__tests__/__snapshots__/renderApplication-test.js.snap index 60dcdd58..d0f743c9 100644 --- a/src/apis/AppRegistry/__tests__/__snapshots__/renderApplication-test.js.snap +++ b/src/apis/AppRegistry/__tests__/__snapshots__/renderApplication-test.js.snap @@ -5,6 +5,7 @@ body{margin:0} button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0} input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit-search-cancel-button,input::-webkit-search-decoration,input::-webkit-search-results-button,input::-webkit-search-results-decoration{display:none} .rn_pointerEvents\\:auto, .rn_pointerEvents\\:box-only, .rn_pointerEvents\\:box-none * {pointer-events:auto}.rn_pointerEvents\\:none, .rn_pointerEvents\\:box-only *, .rn_pointerEvents\\:box-none {pointer-events:none} +@keyframes rn-ActivityIndicator-animation {0% { -webkit-transform: rotate(0deg); transform: rotate(0deg); }100% { -webkit-transform: rotate(360deg); transform: rotate(360deg); }} .rn-bottom\\:0px{bottom:0px;} .rn-left\\:0px{left:0px;} .rn-position\\:absolute{position:absolute;} diff --git a/src/apis/StyleSheet/initialize.js b/src/apis/StyleSheet/initialize.js index bf42deb0..c9ee162c 100644 --- a/src/apis/StyleSheet/initialize.js +++ b/src/apis/StyleSheet/initialize.js @@ -33,6 +33,14 @@ const initialize = () => { '.rn_pointerEvents\\:none, .rn_pointerEvents\\:box-only *, .rn_pointerEvents\\:box-none {pointer-events:none}' ); + injector.addRule( + 'activity-indicator-animation', + '@keyframes rn-ActivityIndicator-animation {' + + '0% { -webkit-transform: rotate(0deg); transform: rotate(0deg); }' + + '100% { -webkit-transform: rotate(360deg); transform: rotate(360deg); }' + + '}' + ); + StyleRegistry.initialize(); }; diff --git a/src/components/ActivityIndicator/__tests__/__snapshots__/index-test.js.snap b/src/components/ActivityIndicator/__tests__/__snapshots__/index-test.js.snap new file mode 100644 index 00000000..592caa5c --- /dev/null +++ b/src/components/ActivityIndicator/__tests__/__snapshots__/index-test.js.snap @@ -0,0 +1,226 @@ +exports[`components/ActivityIndicator default render 1`] = ` +
+
+ + + + +
+
+`; + +exports[`components/ActivityIndicator other render 1`] = ` +
+
+ + + + +
+
+`; diff --git a/src/components/ActivityIndicator/__tests__/index-test.js b/src/components/ActivityIndicator/__tests__/index-test.js index 34aa66d7..614997c1 100644 --- a/src/components/ActivityIndicator/__tests__/index-test.js +++ b/src/components/ActivityIndicator/__tests__/index-test.js @@ -1,5 +1,17 @@ /* eslint-env jasmine, jest */ +import ActivityIndicator from '..'; +import React from 'react'; +import renderer from 'react-test-renderer'; + describe('components/ActivityIndicator', () => { - test.skip('NO TEST COVERAGE', () => {}); + test('default render', () => { + const component = renderer.create(); + expect(component.toJSON()).toMatchSnapshot(); + }); + + test('other render', () => { + const component = renderer.create(); + expect(component.toJSON()).toMatchSnapshot(); + }); }); diff --git a/src/components/ActivityIndicator/index.js b/src/components/ActivityIndicator/index.js index 1938424a..e3d6d76b 100644 --- a/src/components/ActivityIndicator/index.js +++ b/src/components/ActivityIndicator/index.js @@ -1,12 +1,8 @@ -import Animated from '../../apis/Animated'; import applyNativeMethods from '../../modules/applyNativeMethods'; -import Easing from 'animated/lib/Easing'; import StyleSheet from '../../apis/StyleSheet'; import View from '../View'; import React, { Component, PropTypes } from 'react'; -const rotationInterpolation = { inputRange: [ 0, 1 ], outputRange: [ '0deg', '360deg' ] }; - class ActivityIndicator extends Component { static displayName = 'ActivityIndicator'; @@ -25,21 +21,6 @@ class ActivityIndicator extends Component { size: 'small' }; - constructor(props) { - super(props); - this.state = { - animation: new Animated.Value(0) - }; - } - - componentDidMount() { - this._manageAnimation(); - } - - componentDidUpdate() { - this._manageAnimation(); - } - render() { const { animating, @@ -50,8 +31,6 @@ class ActivityIndicator extends Component { ...other } = this.props; - const { animation } = this.state; - const svg = ( - ); } - - _manageAnimation() { - const { animation } = this.state; - - const cycleAnimation = () => { - animation.setValue(0); - Animated.timing(animation, { - duration: 750, - easing: Easing.inOut(Easing.linear), - toValue: 1 - }).start((event) => { - if (event.finished) { - cycleAnimation(); - } - }); - }; - - if (this.props.animating) { - cycleAnimation(); - } else { - animation.stopAnimation(); - } - } } const styles = StyleSheet.create({ @@ -138,10 +92,19 @@ const styles = StyleSheet.create({ }, hidesWhenStopped: { visibility: 'hidden' + }, + animation: { + animationDuration: '0.75s', + animationName: 'rn-ActivityIndicator-animation', + animationTimingFunction: 'linear', + animationIterationCount: 'infinite' + }, + animationPause: { + animationPlayState: 'paused' } }); -const indicatorStyles = StyleSheet.create({ +const indicatorSizes = StyleSheet.create({ small: { width: 20, height: 20 diff --git a/src/components/View/ViewStylePropTypes.js b/src/components/View/ViewStylePropTypes.js index 0e2ed32d..da580e70 100644 --- a/src/components/View/ViewStylePropTypes.js +++ b/src/components/View/ViewStylePropTypes.js @@ -1,3 +1,4 @@ +import AnimationPropTypes from '../../propTypes/AnimationPropTypes'; import BorderPropTypes from '../../propTypes/BorderPropTypes'; import ColorPropType from '../../propTypes/ColorPropType'; import LayoutPropTypes from '../../propTypes/LayoutPropTypes'; @@ -10,6 +11,7 @@ const autoOrHiddenOrVisible = oneOf([ 'auto', 'hidden', 'visible' ]); const hiddenOrVisible = oneOf([ 'hidden', 'visible' ]); module.exports = process.env.NODE_ENV !== 'production' ? { + ...AnimationPropTypes, ...BorderPropTypes, ...LayoutPropTypes, ...ShadowPropTypes, diff --git a/src/propTypes/AnimationPropTypes.js b/src/propTypes/AnimationPropTypes.js new file mode 100644 index 00000000..b5f75a9f --- /dev/null +++ b/src/propTypes/AnimationPropTypes.js @@ -0,0 +1,16 @@ +import { PropTypes } from 'react'; + +const { number, oneOf, oneOfType, string } = PropTypes; + +const AnimationPropTypes = process.env.NODE_ENV !== 'production' ? { + animationDelay: string, + animationDirection: oneOf([ 'alternate', 'alternate-reverse', 'normal', 'reverse' ]), + animationDuration: string, + animationFillMode: oneOf([ 'none', 'forwards', 'backwards', 'both' ]), + animationIterationCount: oneOfType([ number, oneOf([ 'infinite' ]) ]), + animationName: string, + animationPlayState: oneOf([ 'paused', 'running' ]), + animationTimingFunction: string +} : {}; + +module.exports = AnimationPropTypes;