1 | function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
2 |
|
3 | import * as React from 'react';
|
4 | import { Animated, Easing, Platform, StyleSheet, View } from 'react-native';
|
5 | import { withTheme } from '../core/theming';
|
6 | const DURATION = 2400;
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 | const ActivityIndicator = _ref => {
|
29 | let {
|
30 | animating = true,
|
31 | color: indicatorColor,
|
32 | hidesWhenStopped = true,
|
33 | size: indicatorSize = 'small',
|
34 | style,
|
35 | theme,
|
36 | ...rest
|
37 | } = _ref;
|
38 | const {
|
39 | current: timer
|
40 | } = React.useRef(new Animated.Value(0));
|
41 | const {
|
42 | current: fade
|
43 | } = React.useRef(new Animated.Value(!animating && hidesWhenStopped ? 0 : 1));
|
44 | const rotation = React.useRef(undefined);
|
45 | const {
|
46 | animation: {
|
47 | scale
|
48 | }
|
49 | } = theme;
|
50 | const startRotation = React.useCallback(() => {
|
51 |
|
52 | Animated.timing(fade, {
|
53 | duration: 200 * scale,
|
54 | toValue: 1,
|
55 | isInteraction: false,
|
56 | useNativeDriver: true
|
57 | }).start();
|
58 |
|
59 | if (rotation.current) {
|
60 | timer.setValue(0);
|
61 |
|
62 | Animated.loop(rotation.current).start();
|
63 | }
|
64 | }, [scale, fade, timer]);
|
65 |
|
66 | const stopRotation = () => {
|
67 | if (rotation.current) {
|
68 | rotation.current.stop();
|
69 | }
|
70 | };
|
71 |
|
72 | React.useEffect(() => {
|
73 | if (rotation.current === undefined) {
|
74 |
|
75 | rotation.current = Animated.timing(timer, {
|
76 | duration: DURATION,
|
77 | easing: Easing.linear,
|
78 |
|
79 | useNativeDriver: Platform.OS !== 'web',
|
80 | toValue: 1,
|
81 | isInteraction: false
|
82 | });
|
83 | }
|
84 |
|
85 | if (animating) {
|
86 | startRotation();
|
87 | } else if (hidesWhenStopped) {
|
88 |
|
89 | Animated.timing(fade, {
|
90 | duration: 200 * scale,
|
91 | toValue: 0,
|
92 | useNativeDriver: true,
|
93 | isInteraction: false
|
94 | }).start(stopRotation);
|
95 | } else {
|
96 | stopRotation();
|
97 | }
|
98 | }, [animating, fade, hidesWhenStopped, startRotation, scale, timer]);
|
99 | const color = indicatorColor || theme.colors.primary;
|
100 | const size = typeof indicatorSize === 'string' ? indicatorSize === 'small' ? 24 : 48 : indicatorSize ? indicatorSize : 24;
|
101 | const frames = 60 * DURATION / 1000;
|
102 | const easing = Easing.bezier(0.4, 0.0, 0.7, 1.0);
|
103 | const containerStyle = {
|
104 | width: size,
|
105 | height: size / 2,
|
106 | overflow: 'hidden'
|
107 | };
|
108 | return React.createElement(View, _extends({
|
109 | style: [styles.container, style]
|
110 | }, rest, {
|
111 | accessible: true,
|
112 | accessibilityRole: "progressbar",
|
113 | accessibilityState: {
|
114 | busy: animating
|
115 | }
|
116 | }), React.createElement(Animated.View, {
|
117 | style: [{
|
118 | width: size,
|
119 | height: size,
|
120 | opacity: fade
|
121 | }],
|
122 | collapsable: false
|
123 | }, [0, 1].map(index => {
|
124 |
|
125 | const inputRange = Array.from(new Array(frames), (_, frameIndex) => frameIndex / (frames - 1));
|
126 | const outputRange = Array.from(new Array(frames), (_, frameIndex) => {
|
127 | let progress = 2 * frameIndex / (frames - 1);
|
128 | const rotation = index ? +(360 - 15) : -(180 - 15);
|
129 |
|
130 | if (progress > 1.0) {
|
131 | progress = 2.0 - progress;
|
132 | }
|
133 |
|
134 | const direction = index ? -1 : +1;
|
135 | return `${direction * (180 - 30) * easing(progress) + rotation}deg`;
|
136 | });
|
137 | const layerStyle = {
|
138 | width: size,
|
139 | height: size,
|
140 | transform: [{
|
141 | rotate: timer.interpolate({
|
142 | inputRange: [0, 1],
|
143 | outputRange: [`${0 + 30 + 15}deg`, `${2 * 360 + 30 + 15}deg`]
|
144 | })
|
145 | }]
|
146 | };
|
147 | const viewportStyle = {
|
148 | width: size,
|
149 | height: size,
|
150 | transform: [{
|
151 | translateY: index ? -size / 2 : 0
|
152 | }, {
|
153 | rotate: timer.interpolate({
|
154 | inputRange,
|
155 | outputRange
|
156 | })
|
157 | }]
|
158 | };
|
159 | const offsetStyle = index ? {
|
160 | top: size / 2
|
161 | } : null;
|
162 | const lineStyle = {
|
163 | width: size,
|
164 | height: size,
|
165 | borderColor: color,
|
166 | borderWidth: size / 10,
|
167 | borderRadius: size / 2
|
168 | };
|
169 | return React.createElement(Animated.View, {
|
170 | key: index,
|
171 | style: [styles.layer]
|
172 | }, React.createElement(Animated.View, {
|
173 | style: layerStyle
|
174 | }, React.createElement(Animated.View, {
|
175 | style: [containerStyle, offsetStyle],
|
176 | collapsable: false
|
177 | }, React.createElement(Animated.View, {
|
178 | style: viewportStyle
|
179 | }, React.createElement(Animated.View, {
|
180 | style: containerStyle,
|
181 | collapsable: false
|
182 | }, React.createElement(Animated.View, {
|
183 | style: lineStyle
|
184 | }))))));
|
185 | })));
|
186 | };
|
187 |
|
188 | const styles = StyleSheet.create({
|
189 | container: {
|
190 | justifyContent: 'center',
|
191 | alignItems: 'center'
|
192 | },
|
193 | layer: { ...StyleSheet.absoluteFillObject,
|
194 | justifyContent: 'center',
|
195 | alignItems: 'center'
|
196 | }
|
197 | });
|
198 | export default withTheme(ActivityIndicator);
|
199 |
|
\ | No newline at end of file |