Skip to content

Instantly share code, notes, and snippets.

@SatyaSnehith
Last active May 7, 2021 12:14
Show Gist options
  • Save SatyaSnehith/c62503e836eb9b4f262c8bd4eb7e28e4 to your computer and use it in GitHub Desktop.
Save SatyaSnehith/c62503e836eb9b4f262c8bd4eb7e28e4 to your computer and use it in GitHub Desktop.
Doughnut chart in react native
import React, {Component, useState} from 'react';
import {View, TouchableWithoutFeedback, StyleSheet, Animated, Easing} from 'react-native';
import Svg, {Path} from 'react-native-svg';
function getPos(x, y, radius, angle) {
let radians = (angle + 90.0) * Math.PI / 180.0;
return {
x: (radius * Math.sin(radians)) + x,
y: (radius * Math.cos(radians)) + y
};
}
const Arc = (props) => {
var width = props.size;
var height = width;
var centerX = width / 2;
var centerY = centerX;
var outerRadius = width / 2;
var innerRadius = outerRadius - 40;
let pad = props.padding;
var startAngle = parseInt(props.startAngle) + pad;
var endAngle = parseInt(props.endAngle) - pad;
var outerStart = getPos(centerX, centerY, outerRadius, startAngle);
var innerStart = getPos(centerX, centerY, innerRadius, startAngle);
var largeArcFlag = endAngle - startAngle <= 180 ? "0" : "1";
// var innerEnd = getPos(centerX, centerY, innerRadius, endAngle)
// var outerEnd = getPos(centerX, centerY, outerRadius, endAngle)
let fromStrokeWidth = 0;
let toStrokeWidth = 5;
props.select(() => {
//scale animation - increasing stroke width - stroke color must be same as arc color
strokeWidth.setValue(fromStrokeWidth)
Animated.timing(strokeWidth, {
toValue: toStrokeWidth,
duration: 350,
useNativeDriver: true,
}).start();
//zIndex animation for avoiding overlaping with other arcs
zIndex.setValue(0)
Animated.timing(zIndex, {
toValue: 1,
duration: 350,
useNativeDriver: true,
}).start();
})
props.unselect(() => {
strokeWidth.setValue(toStrokeWidth)
Animated.timing(strokeWidth, {
toValue: fromStrokeWidth,
duration: 350,
useNativeDriver: true,
}).start();
zIndex.setValue(1)
Animated.timing(zIndex, {
toValue: 0,
duration: 350,
useNativeDriver: true,
}).start();
})
const AnimatedPath = Animated.createAnimatedComponent(Path);
const AnimatedSvg = Animated.createAnimatedComponent(Svg);
//main animation
const anim = new Animated.Value(0);
let ir = [];
let ds = [];
for (let i = startAngle; i < endAngle; ++i) {
var outerEnd = getPos(centerX, centerY, outerRadius, i)
var innerEnd = getPos(centerX, centerY, innerRadius, i)
ir.push(parseInt(i - startAngle));
ds.push(["M ", outerStart.x, outerStart.y,
" A ", outerRadius, outerRadius, "0", largeArcFlag, "0" , outerEnd.x , outerEnd.y,
" L ", innerEnd.x, innerEnd.y,
" A ", innerRadius, innerRadius, "0", largeArcFlag, "1", innerStart.x, innerStart.y,
" Z "].join(" "));
}
Animated.timing(anim, {
toValue: endAngle - startAngle,
duration: 350,
useNativeDriver: true,
delay: props.animDelay,
easing: Easing.easeInCirc
}).start();
//opacity animation
let opacity = new Animated.Value(0);
Animated.timing(opacity, {
toValue: 1,
duration: 700,
useNativeDriver: true,
delay: props.animDelay,
}).start();
const strokeWidth = new Animated.Value(fromStrokeWidth);
const zIndex = new Animated.Value(0);
const elevation = new Animated.Value(0);
let padding = 10;
return(
<Animated.View style={[styles.center, styles.pos, {
width:(width + padding),
height:(height + padding),
opacity: opacity,
zIndex: zIndex,
alignItems: "center"}]}>
<AnimatedSvg>
<AnimatedPath
transform="translate(5, 5)"
d={anim.interpolate({
inputRange: ir,
outputRange: ds,
})} stroke={props.color} strokeWidth={strokeWidth} strokeLinejoin="round" fill={props.color}/>
</AnimatedSvg>
</Animated.View>
)
}
const styles = StyleSheet.create({
bg: {
backgroundColor: "#ffffff"
},
border: {
borderColor: "#000000",
borderWidth: 10,
borderStyle: "solid"
},
center: {
justifyContent: "center",
display: 'flex',
position: "relative",
},
pos: {
position: "absolute"
}
})
class Pie {
constructor(size, padding) {
this.size = size;
this.padding = padding;
this.arcs = [];
this.refs = [];
this.clicks = [];
this.startAngle = 0;
this.myRef = React.createRef();
this.index = 1;
}
add(percent, color, key) {
let angle = 360 * percent / 100;
let endAngle = this.startAngle + angle;
let ref = {
startAngle: this.startAngle,
endAngle: endAngle,
key: key,
selected: false
};
this.arcs.push(
<Arc
size={this.size}
key={key}
padding={this.padding}
startAngle={this.startAngle}
endAngle={endAngle}
color={color}
animDelay={150}
select={(action) => {
ref.select = action;
}}
unselect={(action) => {
ref.unselect = action;
}}/>
);
this.refs.push(ref);
this.index++;
this.startAngle = endAngle;
}
fireClick(angle) {
for (let i = 0; i < this.refs.length; ++i) {
let ref = this.refs[i];
if (angle > ref.startAngle && angle < ref.endAngle) {
this.click(ref.key);
if (ref.selected) {
ref.unselect();
ref.selected = false;
} else {
ref.select();
ref.selected = true;
}
if (this.lastSelection != undefined && this.lastSelection.key != ref.key && this.lastSelection.selected) {
this.lastSelection.unselect();
this.lastSelection.selected = false;
}
this.lastSelection = ref;
}
}
}
onClick(click) {
this.click = click;
}
handlePress(event) {
let x = event.nativeEvent.locationX;
let y = event.nativeEvent.locationY;
let center = this.size / 2;
let angle = Math.atan2(y - center, x - center) * 180 / Math.PI;
angle = angle < 0 ? -angle : 360 - angle;
console.log(angle);
this.fireClick(angle);
}
get() {
return (
<TouchableWithoutFeedback onPress={(event) => {this.handlePress(event);}} >
<View style={[styles.center, styles.bg, {
width: this.size + 10,
height: this.size + 10,
alignSelf: "center"
}]}>{this.arcs}</View>
</TouchableWithoutFeedback>
);
}
}
class Example extends Component {
constructor(props) {
super(props);
}
render() {
let pie = new Pie(250, 0.5);
pie.add(10, "green", "c0");
pie.add(5, "cyan", "c1");
pie.add(1, "orange", "c2");
pie.add(15, "gray", "c3");
pie.add(15, "red", "c4");
pie.add(10, "green", "c5");
pie.add(5, "cyan", "c6");
pie.add(9, "orange", "c7");
pie.add(15, "gray", "c8");
pie.add(15, "red", "c9");
pie.onClick(function(key) {
console.log(key);
});
return pie.get();
}
}
export default Example;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment