Skip to content

Commit 671ec1e

Browse files
committed
Refactor Circle & Pie to use animated values directly for better perf
1 parent 3ab5922 commit 671ec1e

File tree

4 files changed

+228
-113
lines changed

4 files changed

+228
-113
lines changed

Circle.js

Lines changed: 125 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,47 @@ import React, {
44
} from 'react';
55

66
import {
7+
Animated,
78
ART,
9+
StyleSheet,
810
Text,
911
View,
1012
} from 'react-native';
1113

1214
import Arc from './Shapes/Arc';
15+
import withAnimation from './withAnimation';
1316

14-
export default class ProgressCircle extends Component {
17+
const CIRCLE = Math.PI * 2;
18+
19+
const AnimatedSurface = Animated.createAnimatedComponent(ART.Surface);
20+
const AnimatedArc = Animated.createAnimatedComponent(Arc);
21+
22+
const styles = StyleSheet.create({
23+
container: {
24+
backgroundColor: 'transparent',
25+
overflow: 'hidden',
26+
},
27+
});
28+
29+
export class ProgressCircle extends Component {
1530
static propTypes = {
31+
animated: PropTypes.bool,
1632
borderColor: PropTypes.string,
1733
borderWidth: PropTypes.number,
1834
color: PropTypes.string,
35+
children: React.PropTypes.node,
1936
direction: PropTypes.oneOf(['clockwise', 'counter-clockwise']),
2037
formatText: PropTypes.func,
2138
indeterminate: PropTypes.bool,
22-
progress: PropTypes.number,
39+
progress: PropTypes.oneOfType([
40+
PropTypes.number,
41+
PropTypes.instanceOf(Animated.Value),
42+
]),
43+
rotation: PropTypes.instanceOf(Animated.Value),
2344
showsText: PropTypes.bool,
2445
size: PropTypes.number,
25-
textStyle: PropTypes.any,
46+
style: View.propTypes.style,
47+
textStyle: Text.propTypes.style,
2648
thickness: PropTypes.number,
2749
unfilledColor: PropTypes.string,
2850
};
@@ -31,15 +53,33 @@ export default class ProgressCircle extends Component {
3153
borderWidth: 1,
3254
color: 'rgba(0, 122, 255, 1)',
3355
direction: 'clockwise',
34-
formatText: progress => Math.round(progress * 100) + '%',
56+
formatText: progress => `${Math.round(progress * 100)}%`,
3557
progress: 0,
3658
showsText: false,
3759
size: 40,
3860
thickness: 3,
3961
};
4062

63+
constructor(props, context) {
64+
super(props, context);
65+
66+
this.progressValue = 0;
67+
}
68+
69+
componentWillMount() {
70+
if (this.props.animated) {
71+
this.props.progress.addListener((event) => {
72+
this.progressValue = event.value;
73+
if (this.props.showsText || this.progressValue === 1) {
74+
this.forceUpdate();
75+
}
76+
});
77+
}
78+
}
79+
4180
render() {
42-
let {
81+
const {
82+
animated,
4383
borderColor,
4484
borderWidth,
4585
color,
@@ -48,73 +88,107 @@ export default class ProgressCircle extends Component {
4888
formatText,
4989
indeterminate,
5090
progress,
91+
rotation,
5192
showsText,
5293
size,
94+
style,
5395
textStyle,
5496
thickness,
5597
unfilledColor,
56-
...restProps,
98+
...restProps
5799
} = this.props;
58100

59-
borderWidth = borderWidth || (indeterminate ? 1 : 0);
101+
const border = borderWidth || (indeterminate ? 1 : 0);
60102

61-
const radius = size / 2 - borderWidth;
103+
const radius = (size / 2) - border;
62104
const offset = {
63-
top: borderWidth,
64-
left: borderWidth,
105+
top: border,
106+
left: border,
65107
};
66-
const textOffset = borderWidth + thickness;
67-
const textSize = size - textOffset * 2;
108+
const textOffset = border + thickness;
109+
const textSize = size - (textOffset * 2);
110+
111+
const Surface = rotation ? AnimatedSurface : ART.Surface;
112+
const Shape = animated ? AnimatedArc : Arc;
113+
const progressValue = animated ? this.progressValue : progress;
114+
const angle = animated ? Animated.multiply(progress, CIRCLE) : progress * CIRCLE;
68115

69116
return (
70-
<View {...restProps}>
71-
<ART.Surface
117+
<View style={[styles.container, style]} {...restProps}>
118+
<Surface
72119
width={size}
73-
height={size}>
74-
{unfilledColor && progress !== 1 ? (<Arc
75-
radius={radius}
76-
offset={offset}
77-
startAngle={progress * 2 * Math.PI}
78-
endAngle={2 * Math.PI}
79-
direction={direction}
80-
stroke={unfilledColor}
81-
strokeWidth={thickness} />) : false}
82-
{!indeterminate && progress ? (<Arc
83-
radius={radius}
84-
offset={offset}
85-
startAngle={0}
86-
endAngle={progress * 2 * Math.PI}
87-
direction={direction}
88-
stroke={color}
89-
strokeWidth={thickness} />) : false}
90-
{borderWidth ?
91-
(<Arc
120+
height={size}
121+
style={{
122+
transform: [{
123+
rotate: indeterminate && rotation
124+
? rotation.interpolate({
125+
inputRange: [0, 1],
126+
outputRange: ['0deg', '360deg'],
127+
})
128+
: '0deg',
129+
}],
130+
}}
131+
>
132+
{unfilledColor && progressValue !== 1 ? (
133+
<Shape
134+
radius={radius}
135+
offset={offset}
136+
startAngle={angle}
137+
endAngle={CIRCLE}
138+
direction={direction}
139+
stroke={unfilledColor}
140+
strokeWidth={thickness}
141+
/>
142+
) : false}
143+
{!indeterminate ? (
144+
<Shape
145+
radius={radius}
146+
offset={offset}
147+
startAngle={0}
148+
endAngle={angle}
149+
direction={direction}
150+
stroke={color}
151+
strokeWidth={thickness}
152+
/>
153+
) : false}
154+
{border ? (
155+
<Arc
92156
radius={size / 2}
93157
startAngle={0}
94158
endAngle={(indeterminate ? 1.8 : 2) * Math.PI}
95159
stroke={borderColor || color}
96-
strokeWidth={borderWidth} />) : false}
97-
</ART.Surface>
98-
{!indeterminate && progress && showsText ? (
99-
<View style={{
100-
position: 'absolute',
101-
left: textOffset,
102-
top: textOffset,
103-
width: textSize,
104-
height: textSize,
105-
borderRadius: textSize / 2,
106-
alignItems: 'center',
107-
justifyContent: 'center',
108-
}}>
109-
<Text style={[{
110-
color: color,
111-
fontSize: textSize / 4.5,
112-
fontWeight: '300',
113-
}, textStyle]}>{formatText(progress)}</Text>
160+
strokeWidth={border}
161+
/>
162+
) : false}
163+
</Surface>
164+
{!indeterminate && showsText ? (
165+
<View
166+
style={{
167+
position: 'absolute',
168+
left: textOffset,
169+
top: textOffset,
170+
width: textSize,
171+
height: textSize,
172+
borderRadius: textSize / 2,
173+
alignItems: 'center',
174+
justifyContent: 'center',
175+
}}
176+
>
177+
<Text
178+
style={[{
179+
color,
180+
fontSize: textSize / 4.5,
181+
fontWeight: '300',
182+
}, textStyle]}
183+
>
184+
{formatText(progressValue)}
185+
</Text>
114186
</View>
115187
) : false}
116188
{children}
117189
</View>
118190
);
119191
}
120192
}
193+
194+
export default withAnimation(ProgressCircle);

Pie.js

Lines changed: 68 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,42 @@ import React, {
44
} from 'react';
55

66
import {
7+
Animated,
78
ART,
9+
StyleSheet,
810
View,
911
} from 'react-native';
1012

1113
import Circle from './Shapes/Circle';
1214
import Sector from './Shapes/Sector';
15+
import withAnimation from './withAnimation';
1316

14-
export default class ProgressPie extends Component {
17+
const CIRCLE = Math.PI * 2;
18+
19+
const AnimatedSurface = Animated.createAnimatedComponent(ART.Surface);
20+
const AnimatedSector = Animated.createAnimatedComponent(Sector);
21+
22+
const styles = StyleSheet.create({
23+
container: {
24+
backgroundColor: 'transparent',
25+
overflow: 'hidden',
26+
},
27+
});
28+
29+
export class ProgressPie extends Component {
1530
static propTypes = {
31+
animated: PropTypes.bool,
1632
borderColor: PropTypes.string,
1733
borderWidth: PropTypes.number,
1834
color: PropTypes.string,
19-
progress: PropTypes.number,
35+
children: PropTypes.node,
36+
progress: PropTypes.oneOfType([
37+
PropTypes.number,
38+
PropTypes.instanceOf(Animated.Value),
39+
]),
40+
rotation: PropTypes.instanceOf(Animated.Value),
2041
size: PropTypes.number,
42+
style: View.propTypes.style,
2143
unfilledColor: PropTypes.string,
2244
};
2345

@@ -30,46 +52,69 @@ export default class ProgressPie extends Component {
3052

3153
render() {
3254
const {
33-
progress,
34-
size,
55+
animated,
56+
borderColor,
3557
borderWidth,
58+
children,
3659
color,
37-
borderColor,
60+
progress,
61+
rotation,
62+
size,
63+
style,
3864
unfilledColor,
39-
indeterminate,
40-
children,
41-
...restProps,
65+
...restProps
4266
} = this.props;
4367

44-
const angle = progress * Math.PI * 2;
45-
const radius = size / 2 - borderWidth;
68+
69+
const Surface = rotation ? AnimatedSurface : ART.Surface;
70+
const Shape = animated ? AnimatedSector : Sector;
71+
72+
const angle = animated ? Animated.multiply(progress, CIRCLE) : progress * CIRCLE;
73+
const radius = (size / 2) - borderWidth;
4674
const offset = {
4775
top: borderWidth,
4876
left: borderWidth,
4977
};
5078

5179
return (
52-
<View {...restProps}>
53-
<ART.Surface
80+
<View style={[styles.container, style]} {...restProps}>
81+
<Surface
5482
width={size}
55-
height={size}>
56-
{unfilledColor ? (<Circle
57-
radius={radius}
58-
offset={offset}
59-
fill={unfilledColor} />) : false}
60-
{angle ? (<Sector
83+
height={size}
84+
style={rotation ? {
85+
transform: [{
86+
rotate: rotation.interpolate({
87+
inputRange: [0, 1],
88+
outputRange: ['0deg', '360deg'],
89+
}),
90+
}],
91+
} : undefined}
92+
>
93+
{unfilledColor ? (
94+
<Circle
95+
radius={radius}
96+
offset={offset}
97+
fill={unfilledColor}
98+
/>
99+
) : false}
100+
<Shape
61101
radius={radius}
62102
angle={angle}
63103
offset={offset}
64-
fill={color} />) : false}
65-
{borderWidth ?
66-
(<Circle
104+
fill={color}
105+
/>
106+
{borderWidth ? (
107+
<Circle
67108
radius={size / 2}
68109
stroke={borderColor || color}
69-
strokeWidth={borderWidth} />) : false}
70-
</ART.Surface>
110+
strokeWidth={borderWidth}
111+
/>
112+
) : false}
113+
</Surface>
71114
{children}
72115
</View>
73116
);
74117
}
75118
}
119+
120+
export default withAnimation(ProgressPie, 0.2);

index.js

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
1-
import ProgressCircle from './Circle';
2-
import ProgressPie from './Pie';
3-
import makeAnimatable from './makeAnimatable';
4-
51
export { default as Bar } from './Bar';
6-
export const Circle = makeAnimatable(ProgressCircle);
2+
export { default as Circle } from './Circle';
73
export { default as CircleSnail } from './CircleSnail';
8-
export const Pie = makeAnimatable(ProgressPie, 0.2);
4+
export { default as Pie } from './Pie';

0 commit comments

Comments
 (0)