├── .gitignore ├── .npmignore ├── .prettierrc ├── LICENSE ├── README.md ├── index.js ├── lib ├── animated │ ├── AnimatedBar.js │ └── AnimatedCircle.js ├── const │ └── index.js ├── loader │ ├── BreathingLoader.js │ ├── BubblesLoader.js │ ├── CirclesLoader.js │ ├── CirclesRotationScaleLoader.js │ ├── ColorDotsLoader.js │ ├── DotsLoader.js │ ├── DoubleCircleLoader.js │ ├── EatBeanLoader.js │ ├── LineDotsLoader.js │ ├── LinesLoader.js │ ├── MusicLoader.js │ ├── NineCubesLoader.js │ ├── OpacityDotsLoader.js │ ├── PulseLoader.js │ ├── RippleLoader.js │ ├── RotationCircleLoader.js │ ├── RotationHoleLoader.js │ └── TextLoader.js └── shape │ ├── Bar.js │ ├── Bar2.js │ ├── Bar3.js │ └── Circle.js ├── package-lock.json ├── package.json └── screenshot ├── ss1.gif ├── ss2.gif ├── ss3.gif └── ss4.gif /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /node_modules -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /screenshot 2 | /.idea 3 | /node_modules 4 | .prettierrc 5 | README.md 6 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100, 3 | "trailingComma": "all", 4 | "tabWidth": 2, 5 | "semi": true, 6 | "singleQuote": true, 7 | "bracketSpacing": true, 8 | "arrowParens": "always" 9 | } 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | MIT License 3 | 4 | Copyright (c) 2016 Di Wang 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-native-indicator 2 | 3 | ![npm](https://img.shields.io/npm/dw/react-native-indicator.svg) ![npm](https://img.shields.io/npm/v/react-native-indicator.svg) [![npm](https://img.shields.io/npm/l/express.svg)](https://www.npmjs.com/package/react-native-indicator) 4 | 5 | A useful indicator component for React Native 6 | 7 | 8 | 9 | 10 | 11 | 12 | ## Installation 13 | 14 | Make sure that you are in your React Native project directory and run: 15 | ``` 16 | $ npm install react-native-indicator --save 17 | $ npm install @react-native-community/art --save 18 | ``` 19 | 20 | For react-native >= 0.60 ReactNativeART should be auto-linked and no additional action is required. 21 | 22 | For react-native < 0.60 you need to link ReactNative ART: 23 | ``` 24 | $ react-native link @react-native-community/art 25 | ``` 26 | 27 | More info, following the [Art module](https://github.com/react-native-community/art) instruction to configure. 28 | 29 | ## Usage 30 | 31 | Import react-native-indicator as a JavaScript module: 32 | ``` 33 | import { CirclesLoader, PulseLoader, TextLoader, DotsLoader, ... } from 'react-native-indicator'; 34 | ``` 35 | 36 | Here is currently available types: 37 | 38 | - [PulseLoader](#PulseLoader) 39 | - [DotsLoader](#DotsLoader) 40 | - [TextLoader](#TextLoader) 41 | - [BubblesLoader](#BubblesLoader) 42 | - [CirclesLoader](#CirclesLoader) 43 | - [BreathingLoader](#BreathingLoader) 44 | - [RippleLoader](#RippleLoader) 45 | - [LinesLoader](#LinesLoader) 46 | - [MusicBarLoader](#MusicBarLoader) 47 | - [EatBeanLoader](#EatBeanLoader) 48 | - [DoubleCircleLoader](#DoubleCircleLoader) 49 | - [RotationCircleLoader](#RotationCircleLoader) 50 | - [RotationHoleLoader](#RotationHoleLoader) 51 | - [CirclesRotationScaleLoader](#CirclesRotationScaleLoader) 52 | - [NineCubesLoader](#NineCubesLoader) 53 | - [LineDotsLoader](#LineDotsLoader) 54 | - [ColorDotsLoader](#ColorDotsLoader) 55 | - [OpacityDotsLoader](#OpacityDotsLoader) 56 | 57 | ``` 58 | render(){ 59 | return( 60 | 61 | 62 | 63 | 64 | ); 65 | } 66 | ``` 67 | 68 | ## Props 69 | 70 | 71 | 72 | ##### PulseLoader 73 | 74 | | prop | type | default | description | 75 | | ---- | ---- | ---- | ---- | 76 | | size | number | 30 | circle's size | 77 | | color | string | '#1e90ff' | indicator's color | 78 | | frequency | number | 1000 | scale's frequency | 79 | 80 | 81 | 82 | 83 | ##### DotsLoader 84 | 85 | | prop | type | default | description | 86 | | ---- | ---- | ---- | ---- | 87 | | size | number | 10 | dot's size | 88 | | color | string | '#1e90ff' | indicator's color | 89 | | betweenSpace | number | 5 | distance between two dots | 90 | 91 | 92 | 93 | 94 | ##### TextLoader 95 | 96 | | prop | type | default | description | 97 | | ---- | ---- | ---- | ---- | 98 | | text | string | 'Loading' | contents | 99 | | textStyle | style | inherited | text's style | 100 | 101 | 102 | 103 | 104 | ##### BubblesLoader 105 | 106 | | prop | type | default | description | 107 | | ---- | ---- | ---- | ---- | 108 | | size | number | 40 | circle's size | 109 | | color | string | '#1e90ff' | indicator's color | 110 | | dotRadius | number | 10 | each dot's size | 111 | 112 | 113 | 114 | 115 | ##### CirclesLoader 116 | 117 | | prop | type | default | description | 118 | | ---- | ---- | ---- | ---- | 119 | | size | number | 40 | circle's size | 120 | | color | string | '#1e90ff' | indicator's color | 121 | | dotRadius | number | 8 | each dot's size | 122 | 123 | 124 | 125 | 126 | ##### BreathingLoader 127 | 128 | | prop | type | default | description | 129 | | ---- | ---- | ---- | ---- | 130 | | size | number | 10 | circle's size | 131 | | color | string | '#1e90ff' | indicator's color | 132 | | strokeWidth | number | 3 | outline width | 133 | | frequency | number | 800 | scale's frequency | 134 | 135 | 136 | 137 | 138 | ##### RippleLoader 139 | 140 | | prop | type | default | description | 141 | | ---- | ---- | ---- | ---- | 142 | | size | number | 10 | circle's size | 143 | | frequency | number | 1600 | scale's frequency | 144 | | color | string | '#1e90ff' | indicator's color | 145 | | strokeWidth | number | 3 | outline width | 146 | 147 | 148 | 149 | 150 | ##### LinesLoader 151 | 152 | | prop | type | default | description | 153 | | ---- | ---- | ---- | ---- | 154 | | color | string | '#1e90ff' | indicator's color | 155 | | barWidth | number | 5 | each bar's width | 156 | | barHeight | number | 40 | each bar's height | 157 | | barNumber | number | 5 | the number of bar | 158 | | betweenSpace | number | 2 | distance between two bars | 159 | 160 | 161 | 162 | 163 | ##### MusicBarLoader 164 | 165 | | prop | type | default | description | 166 | | ---- | ---- | ---- | ---- | 167 | | color | string | '#1e90ff' | indicator's color | 168 | | barWidth | number | 3 | each bar's width | 169 | | barHeight | number | 30 | each bar's height | 170 | | betweenSpace | number | 5 | distance between two bars | 171 | 172 | 173 | 174 | 175 | ##### EatBeanLoader 176 | 177 | | prop | type | default | description | 178 | | ---- | ---- | ---- | ---- | 179 | | color | string | '#1e90ff' | indicator's color | 180 | | size | number | 30 | indicator's size | 181 | 182 | 183 | 184 | 185 | ##### DoubleCircleLoader 186 | 187 | | prop | type | default | description | 188 | | ---- | ---- | ---- | ---- | 189 | | size | number | 30 | circle's size | 190 | | color | string | '#1e90ff' | indicator's color | 191 | 192 | 193 | 194 | 195 | ##### RotationCircleLoader 196 | 197 | | prop | type | default | description | 198 | | ---- | ---- | ---- | ---- | 199 | | size | number | 30 | indicator's size | 200 | | color | string | '#1e90ff' | indicator's color | 201 | | rotationSpeed | number | 800 | rotation speed | 202 | 203 | 204 | 205 | 206 | ##### RotationHoleLoader 207 | 208 | | prop | type | default | description | 209 | | ---- | ---- | ---- | ---- | 210 | | size | number | 40 | indicator's size | 211 | | color | string | '#1e90ff' | indicator's color | 212 | | rotationSpeed | number | 800 | rotation speed | 213 | | strokeWidth | number | 8 | circle outline's width | 214 | 215 | 216 | 217 | 218 | ##### CirclesRotationScaleLoader 219 | 220 | | prop | type | default | description | 221 | | ---- | ---- | ---- | ---- | 222 | | size | number | 50 | indicator's size | 223 | | color | string | '#1e90ff' | indicator's color | 224 | 225 | 226 | 227 | 228 | ##### NineCubesLoader 229 | 230 | | prop | type | default | description | 231 | | ---- | ---- | ---- | ---- | 232 | | size | number | 20 | each cube's size | 233 | | color | string | '#1e90ff' | indicator's color | 234 | 235 | 236 | 237 | 238 | ##### LineDotsLoader 239 | 240 | **warning:** *this indicator will occupy a whole horizontal space automatically, which means you don't need to set any center props. Just keeping the direction of its parent View is vertical.* 241 | 242 | | prop | type | default | description | 243 | | ---- | ---- | ---- | ---- | 244 | | size | number | 10 | dot's size | 245 | | color | string | '#1e90ff' | indicator's color | 246 | | dotsNumber | number | 5 | the number of dots | 247 | | betweenSpace | number | 5 | distance between two dots | 248 | 249 | 250 | 251 | 252 | ##### ColorDotsLoader 253 | 254 | | prop | type | default | description | 255 | | ---- | ---- | ---- | ---- | 256 | | size | number | 15 | each cube's size | 257 | | betweenSpace | number | 7 | distance between two dots | 258 | | color1 | string | '#ff4500'(red) | 1st color | 259 | | color2 | string | '#ffd700'(yellow) | 2nd color | 260 | | color3 | string | '#9acd32'(green) | 3rd color | 261 | 262 | ##### OpacityDotsLoader 263 | 264 | | prop | type | default | description | 265 | | ---- | ---- | ---- | ---- | 266 | | size | number | 10 | dot's size | 267 | | color | string | '#1e90ff' | indicator's color | 268 | | betweenSpace | number | 5 | distance between two dots | 269 | | speed | number | 200 | change speed | 270 | 271 | ## License 272 | 273 | MIT 274 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import Breathing from './lib/loader/BreathingLoader'; 2 | import Bubbles from './lib/loader/BubblesLoader'; 3 | import Circles from './lib/loader/CirclesLoader'; 4 | import CirclesRotationScale from './lib/loader/CirclesRotationScaleLoader'; 5 | import ColorDots from './lib/loader/ColorDotsLoader'; 6 | import Dots from './lib/loader/DotsLoader'; 7 | import DoubleCircle from './lib/loader/DoubleCircleLoader'; 8 | import EatBean from './lib/loader/EatBeanLoader'; 9 | import Lines from './lib/loader/LinesLoader'; 10 | import LineDots from './lib/loader/LineDotsLoader'; 11 | import Music from './lib/loader/MusicLoader'; 12 | import NineCubes from './lib/loader/NineCubesLoader'; 13 | import OpacityDots from './lib/loader/OpacityDotsLoader'; 14 | import Pulse from './lib/loader/PulseLoader'; 15 | import Ripple from './lib/loader/RippleLoader'; 16 | import RotationCircle from './lib/loader/RotationCircleLoader'; 17 | import RotationHole from './lib/loader/RotationHoleLoader'; 18 | import Text from './lib/loader/TextLoader'; 19 | 20 | export const BreathingLoader = Breathing; 21 | export const BubblesLoader = Bubbles; 22 | export const CirclesLoader = Circles; 23 | export const CirclesRotationScaleLoader = CirclesRotationScale; 24 | export const ColorDotsLoader = ColorDots; 25 | export const DotsLoader = Dots; 26 | export const DoubleCircleLoader = DoubleCircle; 27 | export const EatBeanLoader = EatBean; 28 | export const LinesLoader = Lines; 29 | export const LineDotsLoader = LineDots; 30 | export const MusicBarLoader = Music; 31 | export const NineCubesLoader = NineCubes; 32 | export const OpacityDotsLoader = OpacityDots; 33 | export const PulseLoader = Pulse; 34 | export const RippleLoader = Ripple; 35 | export const RotationCircleLoader = RotationCircle; 36 | export const RotationHoleLoader = RotationHole; 37 | export const TextLoader = Text; 38 | -------------------------------------------------------------------------------- /lib/animated/AnimatedBar.js: -------------------------------------------------------------------------------- 1 | import Bar from '../shape/Bar'; 2 | import { Animated } from 'react-native'; 3 | 4 | export default Animated.createAnimatedComponent(Bar); 5 | -------------------------------------------------------------------------------- /lib/animated/AnimatedCircle.js: -------------------------------------------------------------------------------- 1 | import Circle from '../shape/Circle'; 2 | import { Animated } from 'react-native'; 3 | 4 | export default Animated.createAnimatedComponent(Circle); 5 | -------------------------------------------------------------------------------- /lib/const/index.js: -------------------------------------------------------------------------------- 1 | export const color = '#1e90ff'; 2 | -------------------------------------------------------------------------------- /lib/loader/BreathingLoader.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Animated } from 'react-native'; 4 | import { Surface } from '@react-native-community/art'; 5 | import AnimatedCircle from '../animated/AnimatedCircle'; 6 | import { color } from '../const'; 7 | 8 | export default class BreathingLoader extends React.PureComponent { 9 | static propTypes = { 10 | color: PropTypes.string, 11 | size: PropTypes.number, 12 | strokeWidth: PropTypes.number, 13 | frequency: PropTypes.number, 14 | }; 15 | 16 | static defaultProps = { 17 | color, 18 | size: 30, 19 | strokeWidth: 3, 20 | frequency: 800, 21 | }; 22 | 23 | constructor(props) { 24 | super(props); 25 | this.state = { 26 | scale: new Animated.Value(0.1), 27 | }; 28 | } 29 | 30 | componentDidMount() { 31 | this._animation(); 32 | } 33 | 34 | componentWillUnmount() { 35 | this.unmounted = true; 36 | } 37 | 38 | _animation = () => { 39 | Animated.sequence([ 40 | Animated.timing(this.state.scale, { 41 | toValue: 1, 42 | duration: this.props.frequency, 43 | useNativeDriver: false, 44 | }), 45 | Animated.timing(this.state.scale, { 46 | toValue: 0.1, 47 | duration: this.props.frequency, 48 | useNativeDriver: false, 49 | }), 50 | ]).start(() => { 51 | !this.unmounted && this._animation(); 52 | }); 53 | }; 54 | 55 | render() { 56 | const { color, size, strokeWidth } = this.props; 57 | return ( 58 | 59 | 67 | 68 | ); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /lib/loader/BubblesLoader.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Animated } from 'react-native'; 4 | import { Surface } from '@react-native-community/art'; 5 | import AnimatedCircle from '../animated/AnimatedCircle'; 6 | import { color } from '../const'; 7 | 8 | export default class BubblesLoader extends React.PureComponent { 9 | static propTypes = { 10 | color: PropTypes.string, 11 | dotRadius: PropTypes.number, 12 | size: PropTypes.number, 13 | }; 14 | 15 | static defaultProps = { 16 | color, 17 | dotRadius: 10, 18 | size: 40, 19 | }; 20 | 21 | state = { 22 | opacities: [ 23 | new Animated.Value(1), 24 | new Animated.Value(1), 25 | new Animated.Value(1), 26 | new Animated.Value(1), 27 | new Animated.Value(1), 28 | new Animated.Value(1), 29 | new Animated.Value(1), 30 | new Animated.Value(1), 31 | ], 32 | }; 33 | eachDegree = 360 / this.state.opacities.length; 34 | timers = []; 35 | 36 | componentDidMount() { 37 | this.state.opacities.forEach((item, i) => { 38 | const id = setTimeout(() => { 39 | this._animation(i); 40 | }, i * 150); 41 | this.timers.push(id); 42 | }); 43 | } 44 | 45 | componentWillUnmount() { 46 | this.unmounted = true; 47 | this.timers.forEach((id) => { 48 | clearTimeout(id); 49 | }); 50 | } 51 | 52 | _animation = (i) => { 53 | Animated.sequence([ 54 | Animated.timing(this.state.opacities[i], { 55 | toValue: 0.2, 56 | duration: 600, 57 | useNativeDriver: false, 58 | }), 59 | Animated.timing(this.state.opacities[i], { 60 | toValue: 1, 61 | duration: 600, 62 | useNativeDriver: false, 63 | }), 64 | ]).start(() => { 65 | !this.unmounted && this._animation(i); 66 | }); 67 | }; 68 | 69 | render() { 70 | const { size, dotRadius, color } = this.props; 71 | const { opacities } = this.state; 72 | return ( 73 | 74 | {opacities.map((item, i) => { 75 | let radian = (i * this.eachDegree * Math.PI) / 180; 76 | let x = Math.round((size / 2) * Math.cos(radian)) + size / 2 + dotRadius / 2; 77 | let y = Math.round((size / 2) * Math.sin(radian)) + size / 2 + dotRadius / 2; 78 | return ( 79 | 87 | ); 88 | })} 89 | 90 | ); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /lib/loader/CirclesLoader.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Animated } from 'react-native'; 4 | import { Surface } from '@react-native-community/art'; 5 | import AnimatedCircle from '../animated/AnimatedCircle'; 6 | import { color } from '../const'; 7 | 8 | export default class CirclesLoader extends React.PureComponent { 9 | static propTypes = { 10 | color: PropTypes.string, 11 | dotRadius: PropTypes.number, 12 | size: PropTypes.number, 13 | }; 14 | 15 | static defaultProps = { 16 | color, 17 | dotRadius: 8, 18 | size: 40, 19 | }; 20 | 21 | state = { 22 | opacities: [ 23 | new Animated.Value(1), 24 | new Animated.Value(1), 25 | new Animated.Value(1), 26 | new Animated.Value(1), 27 | new Animated.Value(1), 28 | new Animated.Value(1), 29 | new Animated.Value(1), 30 | new Animated.Value(1), 31 | ], 32 | }; 33 | eachDegree = 360 / this.state.opacities.length; 34 | timers = []; 35 | 36 | componentDidMount() { 37 | this.state.opacities.forEach((item, i) => { 38 | const id = setTimeout(() => { 39 | this._animation(i); 40 | }, i * 150); 41 | this.timers.push(id); 42 | }); 43 | } 44 | 45 | componentWillUnmount() { 46 | this.unmounted = true; 47 | this.timers.forEach((id) => { 48 | clearTimeout(id); 49 | }); 50 | } 51 | 52 | _animation = (i) => { 53 | Animated.sequence([ 54 | Animated.timing(this.state.opacities[i], { 55 | toValue: 0.1, 56 | duration: 600, 57 | useNativeDriver: false, 58 | }), 59 | Animated.timing(this.state.opacities[i], { 60 | toValue: 1, 61 | duration: 600, 62 | useNativeDriver: false, 63 | }), 64 | ]).start(() => { 65 | !this.unmounted && this._animation(i); 66 | }); 67 | }; 68 | 69 | render() { 70 | const { size, dotRadius, color } = this.props; 71 | const { opacities } = this.state; 72 | return ( 73 | 74 | {opacities.map((item, i) => { 75 | let radian = (i * this.eachDegree * Math.PI) / 180; 76 | let x = Math.round((size / 2) * Math.cos(radian)) + size / 2 + dotRadius / 2; 77 | let y = Math.round((size / 2) * Math.sin(radian)) + size / 2 + dotRadius / 2; 78 | return ( 79 | 87 | ); 88 | })} 89 | 90 | ); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /lib/loader/CirclesRotationScaleLoader.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Animated, Easing } from 'react-native'; 4 | import { Surface, Group } from '@react-native-community/art'; 5 | import AnimatedCircle from '../animated/AnimatedCircle'; 6 | import { color } from '../const'; 7 | 8 | export default class RotationCircleLoader extends React.PureComponent { 9 | static propTypes = { 10 | size: PropTypes.number, 11 | color: PropTypes.string, 12 | }; 13 | 14 | static defaultProps = { 15 | size: 50, 16 | color, 17 | }; 18 | 19 | state = { 20 | degree: new Animated.Value(0), 21 | scales: [new Animated.Value(0), new Animated.Value(0)] 22 | }; 23 | timers = []; 24 | 25 | componentDidMount() { 26 | this._animation(); 27 | this.state.scales.forEach((item, i) => { 28 | const id = setTimeout(() => { 29 | this._animationCircles(i) 30 | }, i * 500); 31 | this.timers.push(id); 32 | }); 33 | 34 | } 35 | 36 | componentWillUnmount() { 37 | this.unmounted = true; 38 | this.timers.forEach((id) => { 39 | clearTimeout(id); 40 | }); 41 | } 42 | 43 | _animation = () => { 44 | Animated.sequence([ 45 | Animated.timing(this.state.degree, { 46 | toValue: 360, 47 | duration: 2000, 48 | easing: Easing.linear, 49 | useNativeDriver: false 50 | }) 51 | ]).start(() => { 52 | if (!this.unmounted) { 53 | this.state.degree.setValue(0); 54 | this._animation(); 55 | } 56 | }); 57 | }; 58 | 59 | _animationCircles = (i) => { 60 | Animated.sequence([ 61 | Animated.timing(this.state.scales[i], { toValue: 1, duration: 1000, useNativeDriver: false }), 62 | Animated.timing(this.state.scales[i], { 63 | toValue: 0.05, 64 | duration: 1000, 65 | useNativeDriver: false 66 | }), 67 | ]).start(() => { 68 | !this.unmounted && this._animationCircles(i); 69 | }); 70 | }; 71 | 72 | render() { 73 | const { size, color } = this.props; 74 | const degree = this.state.degree.interpolate({ 75 | inputRange: [0, 360], 76 | outputRange: ['0deg', '360deg'] 77 | }); 78 | return ( 79 | 86 | 87 | 88 | 95 | 102 | 103 | 104 | 105 | ); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /lib/loader/ColorDotsLoader.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Animated, Easing } from 'react-native'; 4 | import { Surface, Group } from '@react-native-community/art'; 5 | import AnimatedCircle from '../animated/AnimatedCircle'; 6 | 7 | export default class ColorDotsLoader extends Component { 8 | static propTypes = { 9 | size: PropTypes.number, 10 | betweenSpace: PropTypes.number, 11 | color1: PropTypes.string, 12 | color2: PropTypes.string, 13 | color3: PropTypes.string, 14 | }; 15 | 16 | static defaultProps = { 17 | size: 15, 18 | betweenSpace: 7, 19 | color1: '#ff4500', 20 | color2: '#ffd700', 21 | color3: '#9acd32', 22 | }; 23 | 24 | constructor(props) { 25 | super(props); 26 | const red = this.props.color1; 27 | const yellow = this.props.color2; 28 | const green = this.props.color3; 29 | this.state = { 30 | colors: [red, red, red], 31 | color: yellow, 32 | x: new Animated.Value(-this.props.size / 2), 33 | }; 34 | this.patterns = [ 35 | [yellow, red, red], 36 | [yellow, yellow, red], 37 | [yellow, yellow, yellow], 38 | [green, yellow, yellow], 39 | [green, green, yellow], 40 | [green, green, green], 41 | [red, green, green], 42 | [red, red, green], 43 | [red, red, red], 44 | ]; 45 | this.timers = []; 46 | } 47 | 48 | componentDidMount() { 49 | this._animation(); 50 | } 51 | 52 | componentWillUnmount() { 53 | this.unmounted = true; 54 | this.timers.forEach((id) => { 55 | clearTimeout(id); 56 | }); 57 | } 58 | 59 | _animation = () => { 60 | const { size, betweenSpace, color1, color2, color3 } = this.props; 61 | const id1 = setTimeout(() => { 62 | this.setState({ color: color3 }); 63 | }, 600); 64 | const id2 = setTimeout(() => { 65 | this.setState({ color: color1 }); 66 | }, 1200); 67 | this.timers.push(id1); 68 | this.timers.push(id2); 69 | this.patterns.forEach((item, i) => { 70 | const id = setTimeout(() => { 71 | this.setState({ colors: this.patterns[i] }); 72 | }, i * 200 + 100); 73 | this.timers.push(id); 74 | }); 75 | 76 | Animated.sequence([ 77 | Animated.timing(this.state.x, { 78 | toValue: size * 3 + betweenSpace * 2 + size / 2, 79 | duration: 600, 80 | easing: Easing.linear, 81 | useNativeDriver: false, 82 | }), 83 | Animated.timing(this.state.x, { 84 | toValue: -size / 2, 85 | duration: 0, 86 | useNativeDriver: false, 87 | }), 88 | Animated.timing(this.state.x, { 89 | toValue: size * 3 + betweenSpace * 2 + size / 2, 90 | duration: 600, 91 | easing: Easing.linear, 92 | useNativeDriver: false, 93 | }), 94 | Animated.timing(this.state.x, { 95 | toValue: -size / 2, 96 | duration: 0, 97 | useNativeDriver: false, 98 | }), 99 | Animated.timing(this.state.x, { 100 | toValue: size * 3 + betweenSpace * 2 + size / 2, 101 | duration: 600, 102 | easing: Easing.linear, 103 | useNativeDriver: false, 104 | }), 105 | ]).start(() => { 106 | if (!this.unmounted) { 107 | this.state.x.setValue(-size / 2); 108 | this.setState({ color: color2 }); 109 | this._animation(); 110 | } 111 | }); 112 | }; 113 | 114 | render() { 115 | const { size, betweenSpace } = this.props; 116 | const { color, colors } = this.state; 117 | return ( 118 | 119 | 120 | {colors.map((item, i) => ( 121 | 128 | ))} 129 | 130 | 131 | 132 | ); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /lib/loader/DotsLoader.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Animated } from 'react-native'; 4 | import { Surface } from '@react-native-community/art'; 5 | import AnimatedCircle from '../animated/AnimatedCircle'; 6 | import { color } from '../const'; 7 | 8 | export default class DotsLoader extends React.PureComponent { 9 | static propTypes = { 10 | color: PropTypes.string, 11 | size: PropTypes.number, 12 | betweenSpace: PropTypes.number, 13 | }; 14 | 15 | static defaultProps = { 16 | color, 17 | size: 10, 18 | betweenSpace: 5, 19 | }; 20 | 21 | state = { 22 | scales: [new Animated.Value(0), new Animated.Value(0), new Animated.Value(0)], 23 | }; 24 | 25 | componentDidMount() { 26 | this._animation(); 27 | } 28 | 29 | componentWillUnmount() { 30 | this.unmounted = true; 31 | } 32 | 33 | _animation = () => { 34 | function seq(self, i) { 35 | return Animated.sequence([ 36 | Animated.timing(self.state.scales[i], { 37 | toValue: 1, 38 | duration: 300, 39 | delay: (i + 1) * 200, 40 | useNativeDriver: false, 41 | }), 42 | Animated.timing(self.state.scales[i], { 43 | toValue: 0, 44 | duration: 300, 45 | delay: 50, 46 | useNativeDriver: false, 47 | }), 48 | ]); 49 | } 50 | 51 | Animated.parallel([seq(this, 0), seq(this, 1), seq(this, 2)]).start(() => { 52 | if (!this.unmounted) this._animation(); 53 | }); 54 | }; 55 | 56 | _renderCircle = (i) => { 57 | const { color, size, betweenSpace } = this.props; 58 | return ( 59 | 66 | ); 67 | }; 68 | 69 | render() { 70 | const { size, betweenSpace } = this.props; 71 | return ( 72 | 73 | {this._renderCircle(0)} 74 | {this._renderCircle(1)} 75 | {this._renderCircle(2)} 76 | 77 | ); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /lib/loader/DoubleCircleLoader.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Animated } from 'react-native'; 4 | import { Surface } from '@react-native-community/art'; 5 | import AnimatedCircle from '../animated/AnimatedCircle'; 6 | import { color } from '../const'; 7 | 8 | export default class DoubleCircleLoader extends React.PureComponent { 9 | static propTypes = { 10 | color: PropTypes.string, 11 | size: PropTypes.number, 12 | }; 13 | 14 | static defaultProps = { 15 | color, 16 | size: 30, 17 | }; 18 | 19 | state = { 20 | scales: [new Animated.Value(0), new Animated.Value(0)], 21 | }; 22 | timers = []; 23 | 24 | componentDidMount() { 25 | this.state.scales.forEach((item, i) => { 26 | const id = setTimeout(() => { 27 | this._animation(i); 28 | }, i * 1000); 29 | this.timers.push(id); 30 | }); 31 | } 32 | 33 | componentWillUnmount() { 34 | this.unmounted = true; 35 | this.timers.forEach((id) => { 36 | clearTimeout(id); 37 | }); 38 | } 39 | 40 | _animation = (i) => { 41 | Animated.sequence([ 42 | Animated.timing(this.state.scales[i], { toValue: 1, duration: 1000, useNativeDriver: false }), 43 | Animated.timing(this.state.scales[i], { toValue: 0, duration: 1000, useNativeDriver: false }), 44 | ]).start(() => { 45 | !this.unmounted && this._animation(i); 46 | }); 47 | }; 48 | 49 | render() { 50 | const { color, size } = this.props; 51 | const { scales } = this.state; 52 | return ( 53 | 54 | 62 | 70 | 71 | ); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /lib/loader/EatBeanLoader.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Animated } from 'react-native'; 4 | import { Surface, Shape } from '@react-native-community/art'; 5 | import AnimatedCircle from '../animated/AnimatedCircle'; 6 | import { color } from '../const'; 7 | 8 | export default class EatBeanLoader extends React.PureComponent { 9 | static propTypes = { 10 | size: PropTypes.number, 11 | color: PropTypes.string, 12 | }; 13 | 14 | static defaultProps = { 15 | size: 30, 16 | color, 17 | }; 18 | 19 | state = { 20 | dotsY: [ 21 | new Animated.Value(this.props.size * 2.2), 22 | new Animated.Value(this.props.size * 2.2), 23 | new Animated.Value(this.props.size * 2.2), 24 | new Animated.Value(this.props.size * 2.2), 25 | ], 26 | }; 27 | timers = []; 28 | 29 | componentDidMount() { 30 | this.state.dotsY.forEach((item, i) => { 31 | const id = setTimeout(() => { 32 | this._animation(i); 33 | }, i * 300); 34 | this.timers.push(id); 35 | }); 36 | } 37 | 38 | componentWillUnmount() { 39 | this.unmounted = true; 40 | this.timers.forEach((id) => { 41 | clearTimeout(id); 42 | }); 43 | } 44 | 45 | _animation = (i) => { 46 | Animated.timing(this.state.dotsY[i], { 47 | toValue: this.props.size / 2, 48 | duration: 1200, 49 | useNativeDriver: false, 50 | }).start(() => { 51 | if (!this.unmounted) { 52 | this.state.dotsY[i].setValue(this.props.size * 2.2); 53 | this._animation(i); 54 | } 55 | }); 56 | }; 57 | 58 | render() { 59 | const { size, color } = this.props; 60 | 61 | const sinValue = Math.sqrt(2) / 2; 62 | const x = Math.floor((size / 2) * sinValue) + size / 2; 63 | const startY = Math.floor((size / 2) * sinValue) + size / 2; 64 | const endY = size / 2 - Math.floor((size / 2) * sinValue); 65 | const d = `M${x} ${startY} A ${size / 2} ${size / 2}, 0, 1, 1, ${x} ${endY} L ${size / 66 | 2} ${size / 2} Z`; 67 | return ( 68 | 69 | 70 | {this.state.dotsY.map((item, i) => ( 71 | 72 | ))} 73 | 74 | ); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /lib/loader/LineDotsLoader.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Animated, Dimensions } from 'react-native'; 4 | import { Surface } from '@react-native-community/art'; 5 | import AnimatedCircle from '../animated/AnimatedCircle'; 6 | import { color } from '../const'; 7 | 8 | export default class LineDotsLoader extends React.PureComponent { 9 | static propTypes = { 10 | color: PropTypes.string, 11 | size: PropTypes.number, 12 | dotsNumber: PropTypes.number, 13 | betweenSpace: PropTypes.number, 14 | }; 15 | 16 | static defaultProps = { 17 | color, 18 | size: 10, 19 | dotsNumber: 5, 20 | betweenSpace: 5, 21 | }; 22 | 23 | constructor(props) { 24 | super(props); 25 | this.screenWidth = Dimensions.get('window').width; 26 | const { size, dotsNumber, betweenSpace } = this.props; 27 | const midX = 28 | this.screenWidth / 2 + (size * dotsNumber + betweenSpace * (dotsNumber - 1)) / 2 - size / 2; 29 | let circlesX = []; 30 | this.beginX = []; 31 | this.centerX = []; 32 | this.destX = []; 33 | for (let i = 0; i < dotsNumber; i++) { 34 | let beginX = -size / 2 - (size + betweenSpace) * i; 35 | circlesX.push(new Animated.Value(beginX)); 36 | this.beginX.push(beginX); 37 | this.centerX.push(midX - i * (size + betweenSpace)); 38 | this.destX.push(this.screenWidth + size / 2 + i * (size + betweenSpace)); 39 | } 40 | 41 | this.state = { 42 | x: circlesX, 43 | }; 44 | } 45 | 46 | render() { 47 | const { color, size } = this.props; 48 | return ( 49 | 50 | {this.state.x.map((item, i) => ( 51 | 52 | ))} 53 | 54 | ); 55 | } 56 | 57 | componentDidMount() { 58 | this._animation(); 59 | } 60 | 61 | componentWillUnmount() { 62 | this.unmounted = true; 63 | } 64 | 65 | _animation = () => { 66 | this.state.x.forEach((item, i) => { 67 | Animated.sequence([ 68 | Animated.timing(this.state.x[i], { 69 | toValue: this.centerX[i], 70 | duration: 600, 71 | delay: i * 50, 72 | useNativeDriver: false, 73 | }), 74 | Animated.timing(this.state.x[i], { 75 | toValue: this.centerX[i], 76 | duration: 600, 77 | delay: 300, 78 | useNativeDriver: false, 79 | }), 80 | Animated.timing(this.state.x[i], { 81 | toValue: this.destX[i], 82 | duration: 600, 83 | delay: i * 50, 84 | useNativeDriver: false, 85 | }), 86 | ]).start(() => { 87 | if (i === this.props.dotsNumber - 1) { 88 | for (let idx in this.state.x) { 89 | this.state.x[idx].setValue(this.beginX[idx]); 90 | } 91 | !this.unmounted && this._animation(); 92 | } 93 | }); 94 | }); 95 | }; 96 | } 97 | -------------------------------------------------------------------------------- /lib/loader/LinesLoader.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Animated } from 'react-native'; 4 | import { Surface } from '@react-native-community/art'; 5 | import AnimatedBar from '../animated/AnimatedBar'; 6 | import { color } from '../const'; 7 | 8 | export default class LinesLoader extends React.PureComponent { 9 | static propTypes = { 10 | color: PropTypes.string, 11 | barWidth: PropTypes.number, 12 | barHeight: PropTypes.number, 13 | betweenSpace: PropTypes.number, 14 | barNumber: PropTypes.number, 15 | }; 16 | 17 | static defaultProps = { 18 | color, 19 | betweenSpace: 2, 20 | barNumber: 5, 21 | barWidth: 5, 22 | barHeight: 40, 23 | }; 24 | 25 | constructor(props) { 26 | super(props); 27 | const heights = []; 28 | for (let i = 0; i < this.props.barNumber; i++) { 29 | heights.push(new Animated.Value(this.props.barHeight / 3)); 30 | } 31 | 32 | this.state = { 33 | heights, 34 | }; 35 | } 36 | 37 | componentDidMount() { 38 | this._animation(); 39 | } 40 | 41 | componentWillUnmount() { 42 | this.unmounted = true; 43 | } 44 | 45 | _animation = () => { 46 | function seq(self, i) { 47 | return Animated.sequence([ 48 | Animated.timing(self.state.heights[i], { 49 | toValue: self.props.barHeight, 50 | duration: 400, 51 | delay: i * 200, 52 | useNativeDriver: false, 53 | }), 54 | Animated.timing(self.state.heights[i], { 55 | toValue: self.props.barHeight / 3, 56 | duration: 400, 57 | useNativeDriver: false, 58 | }), 59 | ]); 60 | } 61 | 62 | const anim = []; 63 | for (let i = 0; i < this.props.barNumber; i++) anim.push(seq(this, i)); 64 | 65 | Animated.parallel(anim).start(() => { 66 | !this.unmounted && this._animation(); 67 | }); 68 | }; 69 | 70 | render() { 71 | const { color, betweenSpace, barWidth, barHeight, barNumber } = this.props; 72 | return ( 73 | 74 | {this.state.heights.map((item, i) => ( 75 | 83 | ))} 84 | 85 | ); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /lib/loader/MusicLoader.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Animated } from 'react-native'; 4 | import { Surface } from '@react-native-community/art'; 5 | import Bar from '../shape/Bar2'; 6 | import { color } from '../const'; 7 | 8 | const AnimatedBar = Animated.createAnimatedComponent(Bar); 9 | 10 | export default class LinesLoader extends React.PureComponent { 11 | static propTypes = { 12 | color: PropTypes.string, 13 | barWidth: PropTypes.number, 14 | barHeight: PropTypes.number, 15 | betweenSpace: PropTypes.number, 16 | }; 17 | 18 | static defaultProps = { 19 | color, 20 | betweenSpace: 5, 21 | barWidth: 3, 22 | barHeight: 30, 23 | }; 24 | 25 | constructor(props) { 26 | super(props); 27 | this.fixedMaxValue = [ 28 | this.props.barHeight * 0.8, 29 | this.props.barHeight * 0.4, 30 | this.props.barHeight, 31 | this.props.barHeight * 0.2, 32 | ]; 33 | this.fixedMinValue = [ 34 | this.props.barHeight * 0.3, 35 | this.props.barHeight, 36 | this.props.barHeight * 0.5, 37 | this.props.barHeight * 0.8, 38 | ]; 39 | 40 | this.state = { 41 | heights: [ 42 | new Animated.Value(this.fixedMinValue[0]), 43 | new Animated.Value(this.fixedMinValue[1]), 44 | new Animated.Value(this.fixedMinValue[2]), 45 | new Animated.Value(this.fixedMinValue[3]), 46 | ], 47 | }; 48 | } 49 | 50 | componentDidMount() { 51 | this.state.heights.forEach((item, i) => { 52 | this._animation(i); 53 | }); 54 | } 55 | 56 | componentWillUnmount() { 57 | this.unmounted = true; 58 | } 59 | 60 | _animation = (i) => { 61 | Animated.sequence([ 62 | Animated.timing(this.state.heights[i], { 63 | toValue: this.fixedMaxValue[i], 64 | duration: 500, 65 | useNativeDriver: false, 66 | }), 67 | Animated.timing(this.state.heights[i], { 68 | toValue: this.fixedMinValue[i], 69 | duration: 500, 70 | useNativeDriver: false, 71 | }), 72 | ]).start(() => { 73 | !this.unmounted && this._animation(i); 74 | }); 75 | }; 76 | 77 | render() { 78 | const { color, betweenSpace, barWidth, barHeight, barNumber } = this.props; 79 | return ( 80 | 81 | {this.state.heights.map((item, i) => { 82 | return ( 83 | 91 | ); 92 | })} 93 | 94 | ); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /lib/loader/NineCubesLoader.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Animated } from 'react-native'; 4 | import { Surface } from '@react-native-community/art'; 5 | import Bar3 from '../shape/Bar3'; 6 | import { color } from '../const'; 7 | 8 | const AnimatedBar = Animated.createAnimatedComponent(Bar3); 9 | 10 | export default class NineCubesLoader extends React.PureComponent { 11 | state = { 12 | scales: [ 13 | new Animated.Value(0), 14 | new Animated.Value(0), 15 | new Animated.Value(0), 16 | new Animated.Value(0), 17 | new Animated.Value(0), 18 | ], 19 | }; 20 | 21 | static propTypes = { 22 | size: PropTypes.number, 23 | color: PropTypes.string, 24 | }; 25 | 26 | static defaultProps = { 27 | size: 20, 28 | color, 29 | }; 30 | 31 | _renderCube(i, j, scaleID) { 32 | const { size, color } = this.props; 33 | return ( 34 | 42 | ); 43 | } 44 | 45 | render() { 46 | const { size, color } = this.props; 47 | return ( 48 | 49 | {this._renderCube(0, 0, 2)} 50 | {this._renderCube(0, 1, 1)} 51 | {this._renderCube(0, 2, 0)} 52 | {this._renderCube(1, 0, 3)} 53 | {this._renderCube(1, 1, 2)} 54 | {this._renderCube(1, 2, 1)} 55 | {this._renderCube(2, 0, 4)} 56 | {this._renderCube(2, 1, 3)} 57 | {this._renderCube(2, 2, 2)} 58 | 59 | ); 60 | } 61 | 62 | componentDidMount() { 63 | this._animation(); 64 | } 65 | 66 | componentWillUnmount() { 67 | this.unmounted = true; 68 | } 69 | 70 | _animation = () => { 71 | function seq(self, i) { 72 | return Animated.sequence([ 73 | Animated.timing(self.state.scales[i], { 74 | toValue: 1, 75 | duration: 300, 76 | delay: (i + 1) * 100, 77 | useNativeDriver: false, 78 | }), 79 | Animated.timing(self.state.scales[i], { 80 | toValue: 0, 81 | duration: 300, 82 | delay: 200, 83 | useNativeDriver: false, 84 | }), 85 | ]); 86 | } 87 | 88 | Animated.parallel([seq(this, 0), seq(this, 1), seq(this, 2), seq(this, 3), seq(this, 4)]).start( 89 | () => { 90 | !this.unmounted && this._animation(); 91 | }, 92 | ); 93 | }; 94 | } 95 | -------------------------------------------------------------------------------- /lib/loader/OpacityDotsLoader.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Animated } from 'react-native'; 4 | import { Surface } from '@react-native-community/art'; 5 | import AnimatedCircle from '../animated/AnimatedCircle'; 6 | import { color } from '../const'; 7 | 8 | export default class OpacityDotsLoader extends React.PureComponent { 9 | static propTypes = { 10 | color: PropTypes.string, 11 | size: PropTypes.number, 12 | betweenSpace: PropTypes.number, 13 | speed: PropTypes.number, 14 | }; 15 | 16 | static defaultProps = { 17 | color, 18 | size: 10, 19 | betweenSpace: 5, 20 | speed: 200, 21 | }; 22 | 23 | state = { 24 | opacity: [new Animated.Value(0.5), new Animated.Value(0.5), new Animated.Value(0.5)], 25 | }; 26 | 27 | _renderCircle(i) { 28 | const { color, size, betweenSpace } = this.props; 29 | return ( 30 | 38 | ); 39 | } 40 | 41 | componentDidMount() { 42 | this._animation(); 43 | } 44 | 45 | componentWillUnmount() { 46 | this.unmounted = true; 47 | } 48 | 49 | _animation = () => { 50 | const { speed } = this.props; 51 | 52 | function seq(self, i) { 53 | return Animated.sequence([ 54 | Animated.timing(self.state.opacity[i], { 55 | toValue: 1, 56 | duration: speed, 57 | delay: i * speed, 58 | useNativeDriver: false, 59 | }), 60 | Animated.timing(self.state.opacity[i], { 61 | toValue: 0.3, 62 | duration: speed, 63 | delay: speed, 64 | useNativeDriver: false, 65 | }), 66 | ]); 67 | } 68 | 69 | Animated.parallel([seq(this, 0), seq(this, 1), seq(this, 2)]).start(() => { 70 | !this.unmounted && this._animation(); 71 | }); 72 | }; 73 | 74 | render() { 75 | const { size, betweenSpace } = this.props; 76 | return ( 77 | 78 | {this._renderCircle(0)} 79 | {this._renderCircle(1)} 80 | {this._renderCircle(2)} 81 | 82 | ); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /lib/loader/PulseLoader.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Animated } from 'react-native'; 4 | import { Surface } from '@react-native-community/art'; 5 | import AnimatedCircle from '../animated/AnimatedCircle'; 6 | import { color } from '../const'; 7 | 8 | export default class PulseLoader extends React.PureComponent { 9 | static propTypes = { 10 | color: PropTypes.string, 11 | size: PropTypes.number, 12 | frequency: PropTypes.number, 13 | }; 14 | 15 | static defaultProps = { 16 | color, 17 | size: 30, 18 | frequency: 1000, 19 | }; 20 | 21 | state = { 22 | effect: new Animated.ValueXY({ x: 0, y: 1 }), 23 | }; 24 | 25 | componentDidMount() { 26 | this._animation(); 27 | } 28 | 29 | componentWillUnmount() { 30 | this.unmounted = true; 31 | } 32 | 33 | _animation = () => { 34 | Animated.parallel([ 35 | Animated.timing(this.state.effect.x, { 36 | toValue: 1, 37 | duration: this.props.frequency, 38 | useNativeDriver: false, 39 | }), 40 | Animated.timing(this.state.effect.y, { 41 | toValue: 0.05, 42 | duration: this.props.frequency, 43 | useNativeDriver: false, 44 | }), 45 | ]).start(() => { 46 | if (!this.unmounted) { 47 | this.state.effect.setValue({ x: 0, y: 1 }); 48 | this._animation(); 49 | } 50 | }); 51 | }; 52 | 53 | render() { 54 | const { color, size } = this.props; 55 | return ( 56 | 57 | 65 | 66 | ); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /lib/loader/RippleLoader.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Animated } from 'react-native'; 4 | import { Surface } from '@react-native-community/art'; 5 | import AnimatedCircle from '../animated/AnimatedCircle'; 6 | import { color } from '../const'; 7 | 8 | export default class RippleLoader extends React.PureComponent { 9 | static propTypes = { 10 | color: PropTypes.string, 11 | size: PropTypes.number, 12 | strokeWidth: PropTypes.number, 13 | frequency: PropTypes.number, 14 | }; 15 | 16 | static defaultProps = { 17 | color, 18 | size: 40, 19 | strokeWidth: 3, 20 | frequency: 1600, 21 | }; 22 | 23 | state = { 24 | scales: [new Animated.Value(0.1), new Animated.Value(0.1)], 25 | opacities: [new Animated.Value(1), new Animated.Value(1)], 26 | }; 27 | timers = []; 28 | 29 | componentDidMount() { 30 | this.state.scales.forEach((item, i) => { 31 | const id = setTimeout(() => { 32 | this._animation(i); 33 | }, i * this.props.frequency * 0.75); 34 | this.timers.push(id); 35 | }); 36 | } 37 | 38 | componentWillUnmount() { 39 | this.unmounted = true; 40 | this.timers.forEach((id) => { 41 | clearTimeout(id); 42 | }); 43 | } 44 | 45 | _animation = (i) => { 46 | const { frequency } = this.props; 47 | Animated.parallel([ 48 | Animated.timing(this.state.scales[i], { 49 | toValue: 1, 50 | duration: frequency, 51 | useNativeDriver: false, 52 | }), 53 | Animated.timing(this.state.opacities[i], { 54 | toValue: 0, 55 | duration: frequency, 56 | delay: frequency / 2, 57 | useNativeDriver: false, 58 | }), 59 | ]).start(() => { 60 | if (!this.unmounted) { 61 | this.state.scales[i].setValue(0.1); 62 | this.state.opacities[i].setValue(1); 63 | this._animation(i); 64 | } 65 | }); 66 | }; 67 | 68 | render() { 69 | const { color, size, strokeWidth } = this.props; 70 | const { scales, opacities } = this.state; 71 | return ( 72 | 73 | {scales.map((item, i) => ( 74 | 84 | ))} 85 | 86 | ); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /lib/loader/RotationCircleLoader.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Animated, Easing } from 'react-native'; 4 | import { Surface, Group } from '@react-native-community/art'; 5 | import AnimatedCircle from '../animated/AnimatedCircle'; 6 | import { color } from '../const'; 7 | 8 | export default class RotationCircleLoader extends React.PureComponent { 9 | static propTypes = { 10 | size: PropTypes.number, 11 | color: PropTypes.string, 12 | rotationSpeed: PropTypes.number 13 | }; 14 | 15 | static defaultProps = { 16 | size: 40, 17 | color, 18 | rotationSpeed: 800 19 | }; 20 | 21 | state = { 22 | degree: new Animated.Value(0) 23 | }; 24 | 25 | componentDidMount() { 26 | this._animation(); 27 | } 28 | 29 | componentWillUnmount() { 30 | this.unmounted = true; 31 | } 32 | 33 | _animation = () => { 34 | Animated.sequence([ 35 | Animated.timing(this.state.degree, { 36 | toValue: 360, 37 | duration: this.props.rotationSpeed, 38 | easing: Easing.linear, 39 | useNativeDriver: false 40 | }) 41 | ]).start(() => { 42 | if (!this.unmounted) { 43 | this.state.degree.setValue(0); 44 | this._animation(); 45 | } 46 | }); 47 | }; 48 | 49 | render() { 50 | const { size, color } = this.props; 51 | const degree = this.state.degree.interpolate({ 52 | inputRange: [0, 360], 53 | outputRange: ['0deg', '360deg'] 54 | }); 55 | return ( 56 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | ); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /lib/loader/RotationHoleLoader.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Animated, Easing } from 'react-native'; 4 | import { Surface, Group } from '@react-native-community/art'; 5 | import AnimatedCircle from '../animated/AnimatedCircle'; 6 | import { color } from '../const'; 7 | 8 | export default class RotationHoleLoader extends React.PureComponent { 9 | static propTypes = { 10 | size: PropTypes.number, 11 | color: PropTypes.string, 12 | strokeWith: PropTypes.number, 13 | rotationSpeed: PropTypes.number 14 | }; 15 | 16 | static defaultProps = { 17 | size: 40, 18 | color, 19 | rotationSpeed: 800, 20 | strokeWith: 8 21 | }; 22 | 23 | state = { 24 | degree: new Animated.Value(0) 25 | }; 26 | 27 | componentDidMount() { 28 | this._animation(); 29 | } 30 | 31 | componentWillUnmount() { 32 | this.unmounted = true; 33 | } 34 | 35 | _animation = () => { 36 | Animated.sequence([ 37 | Animated.timing(this.state.degree, { 38 | toValue: 360, 39 | duration: this.props.rotationSpeed, 40 | easing: Easing.linear, 41 | useNativeDriver: false 42 | }) 43 | ]).start(() => { 44 | if (!this.unmounted) { 45 | this.state.degree.setValue(0); 46 | this._animation(); 47 | } 48 | }); 49 | }; 50 | 51 | render() { 52 | const { size, color, strokeWith } = this.props; 53 | const degree = this.state.degree.interpolate({ 54 | inputRange: [0, 360], 55 | outputRange: ['0deg', '360deg'] 56 | }); 57 | return ( 58 | 65 | 66 | 67 | 75 | 81 | 82 | 83 | 84 | ); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /lib/loader/TextLoader.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Text, View } from 'react-native'; 4 | 5 | export default class TextLoader extends React.PureComponent { 6 | static propTypes = { 7 | text: PropTypes.string, 8 | textStyle: Text.propTypes.style, 9 | }; 10 | 11 | static defaultProps = { 12 | text: 'Loading', 13 | }; 14 | 15 | state = { 16 | opacities: [0, 0, 0], 17 | }; 18 | patterns = [[0, 0, 0], [1, 0, 0], [1, 1, 0], [1, 1, 1]]; 19 | timers = []; 20 | 21 | componentDidMount() { 22 | this._animation(1); 23 | } 24 | 25 | componentWillUnmount() { 26 | this.unmounted = true; 27 | this.timers.forEach((id) => { 28 | clearTimeout(id); 29 | }); 30 | } 31 | 32 | _animation = (index) => { 33 | if (!this.unmounted) { 34 | const id = setTimeout(() => { 35 | this.setState({ opacities: this.patterns[index] }); 36 | index++; 37 | if (index >= this.patterns.length) index = 0; 38 | this._animation(index); 39 | }, 500); 40 | this.timers.push(id); 41 | } 42 | }; 43 | 44 | render() { 45 | const { text, textStyle } = this.props; 46 | return ( 47 | 48 | {text} 49 | {this.state.opacities.map((item, i) => ( 50 | 51 | . 52 | 53 | ))} 54 | 55 | ); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /lib/shape/Bar.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The scale center of this bar is left-center 3 | */ 4 | import React from 'react'; 5 | import PropTypes from 'prop-types'; 6 | import { Shape, Path } from '@react-native-community/art'; 7 | 8 | export default class Bar extends React.PureComponent { 9 | static propTypes = { 10 | width: PropTypes.number.isRequired, 11 | height: PropTypes.number.isRequired, 12 | }; 13 | 14 | render() { 15 | const { width, height } = this.props; 16 | 17 | const path = Path() 18 | .moveTo(width, height / 2) 19 | .lineTo(0, height / 2) 20 | .lineTo(0, -height / 2) 21 | .lineTo(width, -height / 2) 22 | .close(); 23 | 24 | return ; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/shape/Bar2.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The scale center of this bar is bottom-center 3 | */ 4 | import React from 'react'; 5 | import PropTypes from 'prop-types'; 6 | import { Shape, Path } from '@react-native-community/art'; 7 | 8 | export default class Bar2 extends React.PureComponent { 9 | static propTypes = { 10 | width: PropTypes.number.isRequired, 11 | height: PropTypes.number.isRequired, 12 | }; 13 | 14 | render() { 15 | const { width, height } = this.props; 16 | 17 | const path = Path() 18 | .moveTo(0, 0) 19 | .lineTo(0, -height) 20 | .lineTo(width, -height) 21 | .lineTo(width, 0) 22 | .close(); 23 | 24 | return ; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/shape/Bar3.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The scale center of this bar is center 3 | */ 4 | import React from 'react'; 5 | import PropTypes from 'prop-types'; 6 | import { Shape, Path } from '@react-native-community/art'; 7 | 8 | export default class Bar3 extends React.PureComponent { 9 | static propTypes = { 10 | width: PropTypes.number.isRequired, 11 | height: PropTypes.number.isRequired, 12 | }; 13 | 14 | render() { 15 | const { width, height } = this.props; 16 | 17 | const path = Path() 18 | .moveTo(width / 2, height / 2) 19 | .lineTo(-width / 2, height / 2) 20 | .lineTo(-width / 2, -height / 2) 21 | .lineTo(width / 2, -height / 2) 22 | .close(); 23 | 24 | return ; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/shape/Circle.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Shape, Path } from '@react-native-community/art'; 4 | 5 | export default class Circle extends React.PureComponent { 6 | static propTypes = { 7 | radius: PropTypes.number.isRequired, 8 | opacity: PropTypes.number, 9 | }; 10 | 11 | render() { 12 | const { radius } = this.props; 13 | 14 | const path = Path() 15 | .moveTo(0, -radius / 2) 16 | .arc(0, radius, 1) 17 | .arc(0, -radius, 1) 18 | .close(); 19 | 20 | return ; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-indicator", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "js-tokens": { 8 | "version": "4.0.0", 9 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 10 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" 11 | }, 12 | "loose-envify": { 13 | "version": "1.4.0", 14 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", 15 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", 16 | "requires": { 17 | "js-tokens": "^3.0.0 || ^4.0.0" 18 | } 19 | }, 20 | "object-assign": { 21 | "version": "4.1.1", 22 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 23 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 24 | }, 25 | "prettier": { 26 | "version": "1.19.1", 27 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", 28 | "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", 29 | "dev": true 30 | }, 31 | "prop-types": { 32 | "version": "15.7.2", 33 | "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", 34 | "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", 35 | "requires": { 36 | "loose-envify": "^1.4.0", 37 | "object-assign": "^4.1.1", 38 | "react-is": "^16.8.1" 39 | } 40 | }, 41 | "react-is": { 42 | "version": "16.8.6", 43 | "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz", 44 | "integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==" 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-indicator", 3 | "version": "1.2.2", 4 | "description": "React Native Indicator Component", 5 | "main": "index.js", 6 | "scripts": { 7 | "lint": "./node_modules/.bin/prettier --config .prettierrc --write lib/**/*.js", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/wangdicoder/react-native-indicator.git" 13 | }, 14 | "keywords": [ 15 | "react-native", 16 | "indicator", 17 | "loader", 18 | "progress" 19 | ], 20 | "author": "Di Wang ", 21 | "license": "MIT", 22 | "bugs": { 23 | "url": "https://github.com/wangdicoder/react-native-indicator/issues" 24 | }, 25 | "homepage": "https://github.com/wangdicoder/react-native-indicator#readme", 26 | "dependencies": { 27 | "prop-types": "^15.6.0" 28 | }, 29 | "devDependencies": { 30 | "prettier": "^1.19.1" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /screenshot/ss1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangdicoder/react-native-indicator/666e616e40a2f8942457ee9631463665b4dc5bb7/screenshot/ss1.gif -------------------------------------------------------------------------------- /screenshot/ss2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangdicoder/react-native-indicator/666e616e40a2f8942457ee9631463665b4dc5bb7/screenshot/ss2.gif -------------------------------------------------------------------------------- /screenshot/ss3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangdicoder/react-native-indicator/666e616e40a2f8942457ee9631463665b4dc5bb7/screenshot/ss3.gif -------------------------------------------------------------------------------- /screenshot/ss4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangdicoder/react-native-indicator/666e616e40a2f8942457ee9631463665b4dc5bb7/screenshot/ss4.gif --------------------------------------------------------------------------------