Skip to content

Commit

Permalink
progress/spinners support Android
Browse files Browse the repository at this point in the history
  • Loading branch information
xinthink committed Oct 5, 2015
1 parent 207fcbf commit 0ff6b03
Show file tree
Hide file tree
Showing 7 changed files with 133 additions and 59 deletions.
16 changes: 11 additions & 5 deletions lib/mdl/Progress.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ const {
} = React;

const MKColor = require('../MKColor');
const utils = require('../utils');

//
// ## <section id='Progress'>Progress</section>
Expand Down Expand Up @@ -54,6 +53,10 @@ class Progress extends Component {

componentDidMount() {
requestAnimationFrame(() => {
if (!this.refs.bg) {
return;
}

this.refs.bg.measure((left, top, width, height) => {
this._totalLength = width;
this._height.setValue(height);
Expand Down Expand Up @@ -170,7 +173,7 @@ Progress.defaultProps = {
bufferColor: `rgba(${MKColor.RGBIndigo},0.3)`,
style: {
backgroundColor: `rgba(${MKColor.RGBIndigo},0.3)`,
height: utils.toPixels(2),
height: 4,
},
};

Expand All @@ -191,10 +194,15 @@ class IndeterminateProgress extends Component {
left: new Animated.Value(0),
right: new Animated.Value(0),
};
this._aniUpdateProgress = this._aniUpdateProgress.bind(this);
}

componentDidMount() {
requestAnimationFrame(() => {
if (!this.refs.bg) {
return;
}

this.refs.bg.measure((left, top, width, height) => {
this._totalLength = width;
this._height.setValue(height);
Expand Down Expand Up @@ -230,9 +238,7 @@ class IndeterminateProgress extends Component {
}),
this._getBlock2Ani(),
]),
]).start(() => {
this._aniUpdateProgress();
});
]).start(() => setImmediate(this._aniUpdateProgress));
}

_getBlock2Ani() {
Expand Down
149 changes: 116 additions & 33 deletions lib/mdl/Spinner.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ const {
View,
PropTypes,
Animated,
Platform,
} = React;

const MKColor = require('../MKColor');
const {getTheme} = require('../theme');
const utils = require('../utils');

// controlling speed of rotation: percent to degree
const spinnerRotationInterpolate = {
const SPINNER_ROTATE_INTERP = {
inputRange: [
0, .125, .25, .375, .5, .625, .75, .875, 1,
],
Expand All @@ -32,12 +32,28 @@ const spinnerRotationInterpolate = {
],
};

const L_ARC_ROTATE_INTERP = Platform.OS === 'android' ? {
inputRange: [0, 0.5, 1],
outputRange: ['170deg', '-5deg', '170deg'],
} : {
inputRange: [0, 0.5, 1],
outputRange: ['130deg', '-5deg', '130deg'],
};

const R_ARC_ROTATE_INTERP = Platform.OS === 'android' ? {
inputRange: [0, 0.5, 1],
outputRange: ['-170deg', '5deg', '-170deg'],
} : {
inputRange: [0, 0.5, 1],
outputRange: ['-130deg', '5deg', '-130deg'],
};

// default stroke colors
const defaultStrokeColors = [
'rgb(66,165,245)', //palette-blue-400
'rgb(244,67,54)', //palette-red-500
'rgb(253,216,53)', //palette-yellow-600
'rgb(76,175,80)', //palette-green-500
const DEF_STROKE_COLORS = [
'rgb(66,165,245)', // mdl palette-blue-400
'rgb(244,67,54)', // mdl palette-red-500
'rgb(253,216,53)', // mdl palette-yellow-600
'rgb(76,175,80)', // mdl palette-green-500
];


Expand All @@ -49,19 +65,21 @@ class Spinner extends Component {
constructor(props) {
super(props);
this.state = {
strokeColor: defaultStrokeColors[0],
strokeColor: DEF_STROKE_COLORS[0],
dimen: {},
};
this._strokeColorIndex = 0;
this._animatedContainerAngle = new Animated.Value(0);
this._animatedArcAngle = new Animated.Value(0);
this._aniUpdateSpinner = this._aniUpdateSpinner.bind(this);
}

componentDidMount() {
requestAnimationFrame(() => {
if (this.refs.container == null) {
if (!this.refs.container) {
return;
}

const container = this.refs.container.refs.node; // un-box animated view
container.measure((left, top, width, height) => {
this.setState({dimen: {width, height}}, () => this._aniUpdateSpinner());
Expand All @@ -71,58 +89,117 @@ class Spinner extends Component {

// rotation & arc animation
_aniUpdateSpinner() {
const duration = this.props.spinnerAniDuration || 1500;
this._animatedContainerAngle.setValue(0);
this._animatedArcAngle.setValue(0);

this._updateStrokeColor(() => {
this._animatedContainerAngle.setValue(0);
this._animatedArcAngle.setValue(0);
Animated.parallel([
Animated.timing(this._animatedContainerAngle, {
duration,
toValue: 1,
duration: this.props.spinnerAniDuration || 1500,
}),
Animated.timing(this._animatedArcAngle, {
duration,
toValue: 1,
duration: this.props.spinnerAniDuration || 1500,
}),
]).start(() => this._aniUpdateSpinner());
]).start(() => setImmediate(this._aniUpdateSpinner));
});
}

// render the specified part (left or right) of the arc
_renderSpinnerLayer(left) {
_renderSpinnerLayerAndroid(left) {
const {width, height} = this.state.dimen;
const radii = this.state.dimen.width / 2;
const animBlockStyle = {
width,
height,
position: 'absolute',
};
const clipperStyle = {
height,
width: radii,
position: 'absolute',
left: left ? 0 : radii,
overflow: 'hidden',
};
const arcStyle = {
width,
height,
position: 'absolute',
borderRadius: radii,
borderColor: this.state.strokeColor,
borderWidth: this.props.strokeWidth || 3,
};

let arcInterpolate;
if (left) {
arcInterpolate = L_ARC_ROTATE_INTERP;
animBlockStyle.transform = [
{rotate: this._animatedArcAngle.interpolate(arcInterpolate)},
];
} else {
arcStyle.right = 0;
animBlockStyle.right = 0;
arcInterpolate = R_ARC_ROTATE_INTERP;
animBlockStyle.transform = [
{rotate: this._animatedArcAngle.interpolate(arcInterpolate)},
];
}

// FIXME Android doesn't respect borderXxxColor for circles,
// so I have to clip it to semi-circle using view hierarchies
// @see https://github.com/facebook/react-native/issues/3235
return (
<View style={{ // the container
height,
width: radii,
overflow: 'hidden',
position: 'absolute',
left: left ? 0 : radii,
}}
>
<Animated.View style={animBlockStyle}>
<View style={clipperStyle}>
<View style={arcStyle} />
</View>
</Animated.View>
</View>
);
}

_renderSpinnerLayerIOS(left) {
const {width, height} = this.state.dimen;
const radii = this.state.dimen.width / 2;
const arcStyle = {
width,
height,
position: 'absolute',
width: this.state.dimen.width,
height: this.state.dimen.height,
borderColor: this.state.strokeColor,
borderBottomColor: MKColor.Transparent,
borderRadius: radii,
borderWidth: this.props.strokeWidth || 3,
};

let arcInterpolate;
if (left) {
arcInterpolate = L_ARC_ROTATE_INTERP;
arcStyle.borderRightColor = MKColor.Transparent;
arcStyle.transform = [
{rotate: this._animatedArcAngle.interpolate({
inputRange: [0, 0.5, 1],
outputRange: ['130deg', '-5deg', '130deg'],
})},
{rotate: this._animatedArcAngle.interpolate(arcInterpolate)},
];
} else {
arcInterpolate = R_ARC_ROTATE_INTERP;
arcStyle.right = 0;
arcStyle.borderLeftColor = MKColor.Transparent;
arcStyle.transform = [
{rotate: this._animatedArcAngle.interpolate({
inputRange: [0, 0.5, 1],
outputRange: ['-130deg', '5deg', '-130deg'],
})},
{rotate: this._animatedArcAngle.interpolate(arcInterpolate)},
];
}

return (
<View style={{ // the clipper layer
height,
width: radii,
height: this.state.dimen.height,
overflow: 'hidden',
position: 'absolute',
left: left ? 0 : radii,
Expand All @@ -135,6 +212,12 @@ class Spinner extends Component {
);
}

// render the specified part (left or right) of the arc
_renderSpinnerLayer(left) {
return Platform.OS === 'android' ? this._renderSpinnerLayerAndroid(left) :
this._renderSpinnerLayerIOS(left);
}

_updateStrokeColor(cb) {
const colors = this.props.strokeColor;
let nextColor;
Expand All @@ -147,7 +230,7 @@ class Spinner extends Component {
nextColor = colors;
}

this.setState({strokeColor: nextColor || defaultStrokeColors[0]}, cb);
this.setState({strokeColor: nextColor || DEF_STROKE_COLORS[0]}, cb);
}

render() {
Expand All @@ -156,7 +239,7 @@ class Spinner extends Component {
ref="container"
style={[{
transform: [
{rotate: this._animatedContainerAngle.interpolate(spinnerRotationInterpolate)},
{rotate: this._animatedContainerAngle.interpolate(SPINNER_ROTATE_INTERP)},
],
},
Spinner.defaultProps.style,
Expand Down Expand Up @@ -189,12 +272,12 @@ Spinner.propTypes = {

// ## <section id='defaults'>Defaults</section>
Spinner.defaultProps = {
strokeWidth: utils.toPixels(1.5),
strokeColor: defaultStrokeColors,
strokeWidth: 3,
strokeColor: DEF_STROKE_COLORS,
spinnerAniDuration: 1333,
style: {
width: utils.toPixels(14),
height: utils.toPixels(14),
width: 28,
height: 28,
},
};

Expand Down
17 changes: 0 additions & 17 deletions lib/mdl/index.android.js

This file was deleted.

File renamed without changes.
5 changes: 2 additions & 3 deletions lib/rnmk.android.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,14 @@ exports.MKColor = require('./MKColor');
// exports.MKIconToggle = require('./MKIconToggle');

// Shortcuts, and also compatibility for legacy native components like MKButton
// exports.MKButton = require('./MKButton');
exports.MKButton = exports.mdl.Button;
exports.MKTextField = exports.mdl.Textfield;
exports.MKSwitch = exports.mdl.Switch;

exports.MKRipple = exports.mdl.Ripple;
// exports.MKProgress = exports.mdl.Progress;
exports.MKProgress = exports.mdl.Progress;
exports.MKSlider = exports.mdl.Slider;
// exports.MKSpinner = exports.mdl.Spinner;
exports.MKSpinner = exports.mdl.Spinner;

exports.MKCard = exports.mdl.Card;
exports.MKCardTitle = exports.mdl.Title;
Expand Down
1 change: 0 additions & 1 deletion lib/rnmk.ios.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ exports.MKColor = require('./MKColor');
exports.MKIconToggle = require('./MKIconToggle');

// Shortcuts, and also compatibility for legacy native components like MKButton
// exports.MKButton = require('./MKButton');
exports.MKButton = exports.mdl.Button;
exports.MKTextField = exports.mdl.Textfield;
exports.MKSwitch = exports.mdl.Switch;
Expand Down
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
"description": "Bringing Material Design to React Native",
"main": "lib/index.js",
"author": "Yingxin Wu <yingxinwu.g@gmail.com>",
"contributors": [
"All Contributors (https://github.com/xinthink/react-native-material-kit/graphs/contributors)"
],
"license": "MIT",
"keywords": [
"react",
Expand All @@ -12,6 +15,7 @@
"react-native",
"react-native-component",
"ios",
"android",
"material design",
"MaterialKit"
],
Expand Down

0 comments on commit 0ff6b03

Please sign in to comment.