├── .babelrc
├── .gitignore
├── LICENSE
├── README.md
├── package.json
└── src
└── LoadingIndicator.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015", "stage-2", "react"]
3 | }
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 |
5 | # Runtime data
6 | pids
7 | *.pid
8 | *.seed
9 |
10 | # Directory for instrumented libs generated by jscoverage/JSCover
11 | lib-cov
12 |
13 | # Coverage directory used by tools like istanbul
14 | coverage
15 |
16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
17 | .grunt
18 |
19 | # node-waf configuration
20 | .lock-wscript
21 |
22 | # Compiled binary addons (http://nodejs.org/api/addons.html)
23 | build/Release
24 |
25 | # Dependency directory
26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
27 | node_modules
28 |
29 | # Babel output
30 | /build/
31 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Exponent
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React Loading Indicator
2 |
3 | This is a loading indicator written purely in React that uses SVG and no images. It runs only on web (not React Native).
4 |
5 | 
6 |
7 | By default, its size is 20x20. Basic usage is simple: ``.
8 |
9 | ## Props
10 |
11 | ### color
12 |
13 | The color of the most solid segment—what we call each spoke of the loading indicator. The `color` prop is an object with four keys: `red`, `green`, `blue`, and `alpha`. The first three color components are values between 0 and 255, inclusive. The alpha component is a value between 0 and 1, inclusive.
14 |
15 | ### segments
16 |
17 | The number of segments, evenly spaced from each other.
18 |
19 | ### segmentWidth
20 |
21 | The width of each segment, in logical pixels.
22 |
23 | ### segmentLength
24 |
25 | The length of each segment, in logical pixels.
26 |
27 | ### spacing
28 |
29 | Extra spacing to pad the distance between the center of the loading indicator and each segment, in logical pixels.
30 |
31 | ### fadeTo
32 |
33 | The alpha multiplier of the faintest segments. Each segment's color is determined by multiplying the alpha channel of the `color` prop by a gradually decreasing alpha multiplier that starts at 1 and linearly decreases to the `fadeTo` prop.
34 |
35 | ### fadeSteps
36 |
37 | The number of steps between segments from the boldest segment to the faintest segments. If `fadeSteps` is `segments - 1` then only the last segment will be the faintest, multiplied by `fadeTo`. If `fadeSteps` is a lower value, then several of the last segments will all have the faintest opacity.
38 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-loading-indicator",
3 | "version": "1.0.2",
4 | "description": "A loading indicator written purely in React that uses SVG and no images",
5 | "main": "build/LoadingIndicator.js",
6 | "files": [
7 | "build"
8 | ],
9 | "scripts": {
10 | "prepublish": "babel -d build src"
11 | },
12 | "repository": {
13 | "type": "git",
14 | "url": "git+https://github.com/exponentjs/react-loading-indicator.git"
15 | },
16 | "keywords": [
17 | "react",
18 | "component",
19 | "loading-indicator"
20 | ],
21 | "author": "James Ide",
22 | "license": "MIT",
23 | "bugs": {
24 | "url": "https://github.com/exponentjs/react-loading-indicator/issues"
25 | },
26 | "homepage": "https://github.com/exponentjs/react-loading-indicator#readme",
27 | "dependencies": {
28 | "aphrodite": "^0.6.0",
29 | "classnames": "^2.2.5"
30 | },
31 | "devDependencies": {
32 | "babel-cli": "^6.16.0",
33 | "babel-preset-es2015": "^6.16.0",
34 | "babel-preset-react": "^6.16.0",
35 | "babel-preset-stage-2": "^6.17.0"
36 | },
37 | "peerDependencies": {
38 | "react": "*"
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/LoadingIndicator.js:
--------------------------------------------------------------------------------
1 | import { css, StyleSheet } from 'aphrodite/no-important';
2 | import classNames from 'classnames';
3 | import React from 'react';
4 |
5 | const ActivityIndicatorProps = {
6 | color: {
7 | red: 0,
8 | green: 0,
9 | blue: 0,
10 | alpha: 98 / 255
11 | },
12 | segments: 12,
13 | segmentWidth: 2,
14 | segmentLength: 3,
15 | spacing: 2,
16 | fadeTo: 31 / 98,
17 | fadeSteps: 6,
18 | };
19 |
20 | // RGBA values measured by looking at the refresh control on top of white
21 | // and black and solving a system of equations
22 | const RefreshControlProps = {
23 | color: {
24 | red: Math.round(4845 / 56),
25 | green: Math.round(765 / 8),
26 | blue: Math.round(24225 / 224),
27 | alpha: 224 / 255
28 | },
29 | segments: 12,
30 | segmentWidth: 2,
31 | segmentLength: 5,
32 | spacing: 3,
33 | fadeTo: 0,
34 | fadeSteps: 11,
35 | };
36 |
37 | export default class LoadingIndicator extends React.Component {
38 | static ActivityIndicatorProps = ActivityIndicatorProps;
39 | static RefreshControlProps = RefreshControlProps;
40 |
41 | static defaultProps = ActivityIndicatorProps;
42 |
43 | render() {
44 | let segmentCount = this.props.segments;
45 | let segmentWidth = this.props.segmentWidth;
46 | let segmentLength = this.props.segmentLength;
47 | let innerRadius = segmentWidth * 2 + this.props.spacing;
48 |
49 | let opacityDelta = (1 - this.props.fadeTo) / this.props.fadeSteps;
50 |
51 | let segments = [];
52 | for (let ii = 0; ii < segmentCount; ii++) {
53 | let opacity = 1 - Math.min(ii, this.props.fadeSteps) * opacityDelta;
54 | let rotation = -ii * 360 / segmentCount;
55 | segments.push(
56 |
65 | );
66 | }
67 |
68 | let { red, green, blue, alpha } = this.props.color;
69 | let rgbaColor = `rgba(${red}, ${green}, ${blue}, ${alpha})`;
70 |
71 | let radius = innerRadius + segmentLength + Math.ceil(segmentWidth / 2);
72 |
73 | return (
74 |
88 | );
89 | }
90 | }
91 |
92 | const spinKeyframes = {
93 | from: {
94 | transform: 'rotate(0deg)',
95 | },
96 | to: {
97 | transform: 'rotate(360deg)',
98 | },
99 | };
100 |
101 | const styles = StyleSheet.create({
102 | indicator: {
103 | animationDuration: '1s',
104 | animationIterationCount: 'infinite',
105 | animationName: spinKeyframes,
106 | animationTimingFunction: 'steps(12)',
107 | },
108 | });
109 |
--------------------------------------------------------------------------------