├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── demo ├── android.png └── ios.png ├── package.json └── src └── Pie.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | yarn.lock 3 | yarn-error.log -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | demo/ 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Neo Nie 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-native-pie 2 | 3 | Pie chart for React Native, works on both **iOS** and **Android** 4 | 5 | ## Demo 6 | 7 | ![iOS](demo/ios.png) 8 | ![Android](demo/android.png) 9 | 10 | ## Install 11 | 12 | ```bash 13 | # NPM 14 | npm i --save react-native-pie 15 | OR 16 | # Yarn 17 | yarn add react-native-pie 18 | ``` 19 | 20 | ## For react-native-pie >= 1.1.0 install peer dependency 21 | ```bash 22 | # NPM 23 | npm i --save @react-native-community/art 24 | OR 25 | # Yarn 26 | yarn add @react-native-community/art 27 | ``` 28 | 29 | ## Migration from react-native-pie <= 0.6.2 30 | 31 | Please unlink react-native ART library 32 | 33 | ## Linking module 34 | 35 | ### Mostly automatic linking 36 | 37 | If `react-native` >= 0.60 && react-native-pie >= 1.1.0, the package will be linked automatically. 38 | 39 | ### Manual linking for react-native-pie <= 0.6.2 40 | Link the ART library to your ReactNative project ([how to link a library](https://facebook.github.io/react-native/docs/linking-libraries-ios.html#content)). You'll find the React ART library in `node_modules/react-native/Libraries/ART/ART.xcodeproj` 41 | 42 | 43 | ### React Native Version Support 44 | > **If you are using < `.45`, please install `react-native-pie` `v0.1.0` instead** 45 | > `npm i --save react-native-pie@0.1.0` 46 | 47 | > **Please use >= `0.50.0-rc.0` otherwise there is a ring shape drawing issue with `react-native-pie`** 48 | 49 | ## Usage 50 | 51 | ```jsx 52 | import React from 'react' 53 | import { 54 | StyleSheet, 55 | View, 56 | Text, 57 | } from 'react-native' 58 | import Pie from 'react-native-pie' 59 | 60 | export default () => { 61 | return ( 62 | 63 | 71 | 93 | 116 | 117 | 125 | 149 | 173 | 174 | 182 | 205 | 206 | 217 | 220 | 223 | 60% 224 | 225 | 226 | 227 | 228 | 229 | ) 230 | 231 | } 232 | 233 | const styles = StyleSheet.create({ 234 | container: { alignItems: 'center', justifyContent: 'center', height: 1050 }, 235 | gauge: { 236 | position: 'absolute', 237 | width: 100, 238 | height: 160, 239 | alignItems: 'center', 240 | justifyContent: 'center', 241 | }, 242 | gaugeText: { 243 | backgroundColor: 'transparent', 244 | color: '#000', 245 | fontSize: 24, 246 | }, 247 | }) 248 | ``` 249 | 250 | ## Props 251 | 252 | * **sections** `{percentage, color}` of each section in the pie - array, **required** 253 | * **radius** `radius = size / 2` , - number, **required** 254 | * **innerRadius** defaults to `0` - number, **optional** 255 | * **backgroundColor** defaults to `#fff` - string, **optional** 256 | * **strokeCap** ( `round` , `butt` ) defaults to `butt` - string, **optional** 257 | * **PLEASE NOTE** If using `strokeCap={'round'}` it is highly recommended to use a higher `innerRadius` (around 60% of `radius` and higher) in addition to not having very small percentage sections. This will ensure proper display. We hope to address these issues in future PRs 258 | * **dividerSize** defaults to `0` - percentage size to divide the sections - number, **optional** 259 | 260 | ## License 261 | 262 | MIT 263 | 264 | -------------------------------------------------------------------------------- /demo/android.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nihgwu/react-native-pie/0687c2a8b49faca05bc0dbeadb55909d7b7af17f/demo/android.png -------------------------------------------------------------------------------- /demo/ios.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nihgwu/react-native-pie/0687c2a8b49faca05bc0dbeadb55909d7b7af17f/demo/ios.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-pie", 3 | "version": "1.1.2", 4 | "description": "a pie chart for react native", 5 | "main": "src/Pie.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/nihgwu/react-native-pie.git" 12 | }, 13 | "keywords": [ 14 | "pie", 15 | "doughnut", 16 | "gauge", 17 | "chart", 18 | "react native" 19 | ], 20 | "author": "Neo (nihgwu@live.com)", 21 | "license": "MIT", 22 | "bugs": { 23 | "url": "https://github.com/nihgwu/react-native-pie/issues" 24 | }, 25 | "homepage": "https://github.com/nihgwu/react-native-pie#readme", 26 | "peerDependencies": { 27 | "prop-types": "^15.6.0", 28 | "react": "*", 29 | "react-native": "*", 30 | "@react-native-community/art": "*" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Pie.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import {Platform} from 'react-native'; 4 | import {Surface, Shape, Path, Group} from '@react-native-community/art'; 5 | 6 | function createPath(cx, cy, r, startAngle, arcAngle, isBezian, innerRadius) { 7 | const p = new Path(); 8 | //starting point of our chart 9 | if (isBezian) { 10 | const roundnessOutside = 11 | 1 - (r - innerRadius) / innerRadius - arcAngle * 0.5; 12 | const roundnessInside = 13 | 1 + (r - innerRadius) / innerRadius + arcAngle * 0.5; 14 | const pullback = 0.1; 15 | const anchorForward = 0.2; 16 | //This is for the part that is the divider 17 | p.moveTo( 18 | cx + r * roundnessOutside * Math.cos(startAngle + pullback), 19 | cy + r * roundnessOutside * Math.sin(startAngle + pullback), 20 | ); 21 | p.onBezierCurve( 22 | undefined, 23 | undefined, 24 | cx + r * roundnessOutside * Math.cos(startAngle + pullback), 25 | cy + r * roundnessOutside * Math.sin(startAngle + pullback), 26 | cx + r * Math.cos(startAngle + anchorForward), 27 | cy + r * Math.sin(startAngle + anchorForward), 28 | cx + r * roundnessInside * Math.cos(startAngle + pullback), 29 | cy + r * roundnessInside * Math.sin(startAngle + pullback), 30 | ); 31 | } else { 32 | //This is for the main arc of the pie chart 33 | p.moveTo(cx + r * Math.cos(startAngle), cy + r * Math.sin(startAngle)); 34 | p.onArc( 35 | undefined, 36 | undefined, 37 | undefined, 38 | undefined, 39 | cx, 40 | cy, 41 | r, 42 | r, 43 | startAngle, 44 | startAngle + arcAngle, 45 | ); 46 | } 47 | return p; 48 | } 49 | 50 | const ArcShape = ({ 51 | dimensions, 52 | color, 53 | strokeCap, 54 | startAngle, 55 | arcAngle, 56 | isBezian, 57 | }) => { 58 | const {radius, innerRadius, width, dividerSize} = dimensions; 59 | const path = createPath( 60 | radius, 61 | radius, 62 | radius - width / 2, 63 | (startAngle / 180) * Math.PI, 64 | (arcAngle / 180) * Math.PI, 65 | isBezian, 66 | innerRadius, 67 | ); 68 | const strokeWidth = isBezian ? arcAngle * 5 : width; 69 | return ( 70 | 76 | ); 77 | }; 78 | 79 | //The initial band to set the backgroundColor behind the pie chart 80 | const Background = ({dimensions, color}) => { 81 | return ( 82 | 88 | ); 89 | }; 90 | 91 | const getArcAngle = percentage => (percentage / 100) * 360; 92 | const shouldShowDivider = (sections, dividerSize) => 93 | sections.length > 1 && !Number.isNaN(dividerSize); 94 | 95 | const Sections = ({ 96 | dimensions, 97 | paintedSections, 98 | sections, 99 | shouldShowRoundDividers, 100 | strokeCapForLargeBands, 101 | }) => { 102 | let startValue = 0; 103 | const {radius, width, dividerSize} = dimensions; 104 | const showDividers = shouldShowDivider(sections, dividerSize); 105 | paintedSections = sections.map((section, idx) => { 106 | const {percentage, color} = section; 107 | const startAngle = (startValue / 100) * 360; 108 | const arcAngle = getArcAngle(percentage); 109 | startValue += percentage; 110 | shouldShowRoundDividers && 111 | paintedSections.push({percentage, color, startAngle, arcAngle}); 112 | return ( 113 | 121 | ); 122 | }); 123 | return paintedSections; 124 | }; 125 | 126 | // These are the rounded dividers when strokeCap='round' 127 | const RoundDividers = ({ 128 | dimensions, 129 | paintedSections, 130 | backgroundColor, 131 | visible, 132 | }) => { 133 | const {dividerSize, radius, innerRadius, width} = dimensions; 134 | const dividerOffSet = dividerSize * 2 + 6; 135 | const strokeCap = 'butt'; 136 | const isBezian = true; 137 | let dividerColorOverlayArray = []; 138 | let dividerArray = []; 139 | 140 | if (paintedSections.length > 1 && visible) { 141 | paintedSections.forEach((section, index) => { 142 | const {color, startAngle} = section; 143 | 144 | for (let i = 0; i < dividerSize + 2; i++) { 145 | dividerArray.push( 146 | , 157 | ); 158 | dividerColorOverlayArray.push( 159 | , 170 | ); 171 | } 172 | }); 173 | } 174 | return ( 175 | 176 | {dividerArray} 177 | {dividerColorOverlayArray} 178 | 179 | ); 180 | }; 181 | 182 | // These circles clean up the strokes left over from the bezian curves 183 | const CleanUpCircles = ({dimensions, backgroundColor, visible}) => { 184 | const {radius, innerRadius, width} = dimensions; 185 | const innerBackgroundPath = createPath( 186 | radius, 187 | radius, 188 | innerRadius - width / 2, 189 | 0, 190 | 360, 191 | ); 192 | const outerBackgroundPath = createPath( 193 | radius, 194 | radius, 195 | radius + width / 2, 196 | 0, 197 | 360, 198 | ); 199 | if (width < 100 && visible) { 200 | return ( 201 | <> 202 | 207 | 212 | 213 | ); 214 | } 215 | return null; 216 | }; 217 | 218 | const Pie = ({ 219 | sections, 220 | radius, 221 | innerRadius, 222 | backgroundColor, 223 | strokeCap, 224 | dividerSize, 225 | }) => { 226 | strokeCapForLargeBands = 227 | dividerSize > 0 || strokeCap == 'butt' ? 'butt' : 'butt'; 228 | const shouldShowRoundDividers = strokeCap === 'round'; 229 | let paintedSections = []; 230 | 231 | // This is the width for the arc 232 | const width = radius - innerRadius; 233 | const dimensions = {radius, innerRadius, width, dividerSize}; 234 | 235 | return ( 236 | 237 | 238 | 239 | 246 | 252 | 257 | 258 | 259 | ); 260 | }; 261 | 262 | export default Pie; 263 | 264 | Pie.propTypes = { 265 | sections: PropTypes.arrayOf( 266 | PropTypes.exact({ 267 | percentage: PropTypes.number.isRequired, 268 | color: PropTypes.string.isRequired, 269 | }), 270 | ).isRequired, 271 | radius: PropTypes.number.isRequired, 272 | innerRadius: PropTypes.number, 273 | backgroundColor: PropTypes.string, 274 | strokeCap: PropTypes.string, 275 | dividerSize: PropTypes.number, 276 | }; 277 | 278 | Pie.defaultProps = { 279 | dividerSize: 0, 280 | innerRadius: 0, 281 | backgroundColor: '#fff', 282 | strokeCap: 'butt', 283 | }; 284 | --------------------------------------------------------------------------------