nihgwu / react-native-pie

Pie chart for React Native
MIT License
107 stars 25 forks source link

Divider Support #32

Closed zgordon01 closed 4 years ago

zgordon01 commented 4 years ago

Release 1.1.0

Description

An option to include dividers between the pie sections. Supports round and butt strokeCap. Removing RingShape workaround

Updated readme to include strokeCap. Updating readme examples with new images. ios

nihgwu commented 4 years ago

looks awesome 👍

milindagrawal commented 4 years ago

Not at all different than what you have done here but tried to make it more readable

import React from 'react';
import PropTypes from 'prop-types';
import { Platform } from 'react-native';
import { Surface, Shape, Path, Group } from '@react-native-community/art';

function createPath(cx, cy, r, startAngle, arcAngle) {
  const p = new Path();
  p.path.push(0, cx + r * Math.cos(startAngle), cy + r * Math.sin(startAngle));
  p.path.push(4, cx, cy, r, startAngle, startAngle + arcAngle, 1);
  return p;
}

const ArcShape = ({ radius, width, color, strokeCap, startAngle, arcAngle }) => {
  const path = createPath(
    radius,
    radius,
    radius - width / 2,
    startAngle / 180 * Math.PI,
    arcAngle / 180 * Math.PI,
  );

  return <Shape d={path} stroke={color} strokeWidth={width} strokeCap={strokeCap} />;
};

const getPercentage = (percentage) => (percentage < 6 ? percentage + (6 - percentage) : percentage);

const _renderArc = ({ sections, width, radius, backgroundColor, strokeCap, dividerSize }) => {
  const hasValidPercentage = sections.every(({ percentage }) => percentage !== 100);
  const shouldShowRoundDividers = !!dividerSize && strokeCap === 'round' && hasValidPercentage;
  let startValue = 0;
  let arcArray = [];
  let dividerColorOverlayArray = [];
  let dividerArray = [];

  sections.forEach((section, index) => {
    const { percentage, color } = section;
    const calculatedPercentage = shouldShowRoundDividers ? getPercentage(percentage) : percentage;
    const startAngle = startValue / 100 * 360;
    const arcAngle = calculatedPercentage / 100 * 360;
    startValue += calculatedPercentage;

    arcArray.push(<ArcShape
      key={index}
      radius={radius}
      width={width}
      color={color}
      startAngle={startAngle + dividerSize}
      arcAngle={arcAngle - dividerSize}
      strokeCap={strokeCap}
    />);

    if (shouldShowRoundDividers) {
      dividerArray.push(<ArcShape
        key={index}
        radius={radius}
        width={width}
        color={backgroundColor}
        startAngle={startAngle - dividerSize / 2}
        arcAngle={dividerSize}
        strokeCap={strokeCap}
      />);

      dividerColorOverlayArray.push(<ArcShape
        key={index}
        radius={radius}
        width={width}
        color={color}
        startAngle={startAngle + arcAngle - dividerSize / 2 - 1}
        arcAngle={1}
        strokeCap={strokeCap}
      />);
    }
  });

  return [...arcArray, ...dividerArray, ...dividerColorOverlayArray];
}

const Pie = ({ sections, radius, innerRadius, backgroundColor, strokeCap, dividerSize }) => {
  const width = radius - innerRadius;
  const backgroundPath = createPath(radius, radius, radius - width / 2, 0, 360);

  return (
    <Surface width={radius * 2} height={radius * 2}>
      <Group rotation={-90} originX={radius} originY={radius}>
        <Shape
          d={backgroundPath}
          stroke={backgroundColor}
          strokeWidth={width}
        />
        <ArcShape radius={radius} width={width} color={backgroundColor} startAngle={0} arcAngle={360} />
        {_renderArc({ sections, width, radius, backgroundColor, strokeCap, dividerSize })}
      </Group>
    </Surface>
  );
};

Pie.propTypes = {
  sections: PropTypes.arrayOf(
    PropTypes.exact({
      percentage: PropTypes.number.isRequired,
      color: PropTypes.string.isRequired,
    }),
  ).isRequired,
  radius: PropTypes.number.isRequired,
  innerRadius: PropTypes.number,
  backgroundColor: PropTypes.string,
  strokeCap: PropTypes.oneOf(['butt', 'square', 'round']),
  dividerSize: PropTypes.number,
};

Pie.defaultProps = {
  dividerSize: 0,
  innerRadius: 0,
  backgroundColor: '#FFFFFF',
  strokeCap: 'butt',
};

export default Pie;
zgordon01 commented 4 years ago

Seems like we can remove this from the readme based on my above comments:

On android there is a ring shape drawing issue in React Native, I've made a PR to resolve it https://github.com/facebook/react-native/pull/15042, I also made a warkarond for this compoent https://github.com/nihgwu/react-native-pie/commit/86adf51339854ef3dc50df8cef6d12afb9df7b82, and will remove it when that PR is shipped with a stable release

@nihgwu do you know what RN version that was shipped in so I can add a clause to the readme?

nihgwu commented 4 years ago

@zgordon01 image seems it's 0.50.0-rc.0

zgordon01 commented 4 years ago

@nihgwu @milindagrawal check now. I did a docs update based on some discussion with the ring shape drawing issue, as well as addressed code comments

nihgwu commented 4 years ago

Thanks 👍

nihgwu commented 4 years ago

v1.1.0 released

nihgwu commented 4 years ago

BTW, what's your username for npm? I'll invite you as the admin

zgordon01 commented 4 years ago

BTW, what's your username for npm? I'll invite you as the admin

zgordon01, same as github

nihgwu commented 4 years ago

@zgordon01 invited