diff --git a/.changeset/friendly-buttons-approve.md b/.changeset/friendly-buttons-approve.md new file mode 100644 index 00000000..596418b6 --- /dev/null +++ b/.changeset/friendly-buttons-approve.md @@ -0,0 +1,6 @@ +--- +"victory-native": minor +"example": patch +--- + +Add Pie/Donut charts diff --git a/example/app/consts/routes.ts b/example/app/consts/routes.ts index 6c00c8cd..4ce100ed 100644 --- a/example/app/consts/routes.ts +++ b/example/app/consts/routes.ts @@ -58,6 +58,24 @@ export const ChartRoutes: { "This chart showcases using custom shaders from Skia, leveraging shader uniforms derived from Reanimated shared values.", path: "/custom-shaders", }, + { + title: "Pie Chart", + description: + "This is a Pie chart in Victory. It has support for customizing each slice and adding insets.", + path: "/pie-chart", + }, + { + title: "Donut Chart", + description: + "This is how to make a Donut chart in Victory. It is built off of the Pie chart using the `innerRadius` prop.", + path: "/donut-chart", + }, + { + title: "Pie and Donut Assortment", + description: + "This is mixture of Pie and Donut charts, showing off the different ways to customize the charts.", + path: "/pie-and-donut-charts", + }, ]; if (__DEV__) { diff --git a/example/app/donut-chart.tsx b/example/app/donut-chart.tsx new file mode 100644 index 00000000..d9e1f399 --- /dev/null +++ b/example/app/donut-chart.tsx @@ -0,0 +1,134 @@ +import React, { useState } from "react"; +import { SafeAreaView, ScrollView, StyleSheet, View } from "react-native"; +import { LinearGradient, vec } from "@shopify/react-native-skia"; +import { Pie, PolarChart } from "victory-native"; +import { InfoCard } from "example/components/InfoCard"; +import { Button } from "example/components/Button"; +import { appColors } from "./consts/colors"; +import { descriptionForRoute } from "./consts/routes"; + +function calculateGradientPoints( + radius: number, + startAngle: number, + endAngle: number, + centerX: number, + centerY: number, +) { + // Calculate the midpoint angle of the slice for a central gradient effect + const midAngle = (startAngle + endAngle) / 2; + + // Convert angles from degrees to radians + const startRad = (Math.PI / 180) * startAngle; + const midRad = (Math.PI / 180) * midAngle; + + // Calculate start point (inner edge near the pie's center) + const startX = centerX + radius * 0.5 * Math.cos(startRad); + const startY = centerY + radius * 0.5 * Math.sin(startRad); + + // Calculate end point (outer edge of the slice) + const endX = centerX + radius * Math.cos(midRad); + const endY = centerY + radius * Math.sin(midRad); + + return { startX, startY, endX, endY }; +} + +const randomNumber = () => Math.floor(Math.random() * (50 - 25 + 1)) + 125; +function generateRandomColor(): string { + // Generating a random number between 0 and 0xFFFFFF + const randomColor = Math.floor(Math.random() * 0xffffff); + // Converting the number to a hexadecimal string and padding with zeros + return `#${randomColor.toString(16).padStart(6, "0")}`; +} + +const DATA = (numberPoints = 5) => + Array.from({ length: numberPoints }, (_, index) => ({ + value: randomNumber(), + color: generateRandomColor(), + label: `Label ${index + 1}`, + })); + +export default function DonutChart(props: { segment: string }) { + const description = descriptionForRoute(props.segment); + const [data, setData] = useState(DATA(5)); + + return ( + + + + + + {({ slice }) => { + const { startX, startY, endX, endY } = calculateGradientPoints( + slice.radius, + slice.startAngle, + slice.endAngle, + slice.center.x, + slice.center.y, + ); + + return ( + <> + + + + + + ); + }} + + + + + + + {description} + + + +