/** @format */ import * as d3 from 'd3'; import _ from 'lodash'; import React, { useEffect, useRef, useState } from 'react'; import NumberBox from './Number'; import { Legend, NumberContent, Title, TitleBox } from './styled'; interface ActionRingChartProps { size?: Record; [key: string]: any; } const ActionRingChart = (props: ActionRingChartProps) => { const { size = { height: 500 }, data, innerRadius = 0.65, outerRadius = 0.9, animationTime = 1000, animationInterval = 2000, showTrueVlaue = false, per = '%', perStyle, perMargin, numberStyle, textStyle, legendShow, legendStyle, legendPosition, legendMarginL, legendMarginT, colors, } = props; const [haveClass, setHaveClass] = useState(false); const [total, setTotal] = useState(0); const [number, setNumber] = useState(0); const [COLORS, setCOLORS] = useState([ '#16a3ff', '#6ddead', '#5edfff', '#3e64ff', '#505bda', ]); const WIDTH = size.width; const HEIGHT = size.height; const margin = { top: 20, right: 0, bottom: 30, left: 40, }; const chartWidth = WIDTH - margin.left - margin.right; const chartHeight = HEIGHT - margin.top - margin.bottom; const [value, setValue] = useState(0); // 初始渐变参数 const containerRef = useRef(null); // dom const counterRef = useRef(number); // 定时器记录 counterRef.current = number; let timer: any; // 自动轮播 useEffect(() => { if (timer) { setHaveClass(false); clearInterval(timer); } timer = setInterval(function() { const currCount = counterRef.current; setHaveClass(true); setNumber(currCount + 1); }, animationInterval); return () => { clearInterval(timer); setHaveClass(false); }; }, [animationInterval]); // 总数计算 useEffect(() => { let count = 0; data && data.length > 0 && data.map((item: any) => { count += item.y; }); setTotal(count); }, [data]); // 圆环样色 useEffect(() => { const color = COLORS; colors && colors.length > 0 && colors.map((item: string, i: number) => { color[i] = item; }); setCOLORS(color); }, [colors]); useEffect(() => { const transition = d3 .select(containerRef.current) // 选中当前dom对象 不然会混淆 .transition() .duration(1000); transition.tween('style', () => { return (t: any) => { setValue(t); }; }); }, []); const pieRadius = d3.min([chartWidth, chartHeight]) / 2; // 半径 const pie = d3 .pie() .sort(null) .value((d: any) => d.y); const pieData = pie(data); const arc = d3 .arc() .innerRadius(pieRadius * innerRadius) // 如果想要实现环图,修改这里的半径参数即可 .outerRadius(pieRadius * outerRadius); // 样式 const perStyles = _.merge(perStyle, { marginLeft: perMargin }); const normalStyle = { cursor: 'pointer', opacity: 1, transform: 'scale(1)', transition: `all ease ${animationTime}ms`, }; const hoverStyle = { transform: 'scale(1.2)', }; const noHoverStyle = { opacity: 1, }; const style: Record = { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'cneter', width: size.width, height: size.height, position: 'relative', }; const centerStyle: Record = { position: 'absolute', width: pieRadius * 0.6, height: pieRadius * 0.6, left: `calc(50% - ${pieRadius * 0.3}px)`, top: `calc(50% - ${pieRadius * 0.3}px)`, textAlign: 'center', display: 'flex', flexDirection: 'column', justifyContent: 'center', }; return (
{pieData.map((d: any, i: any) => { const arcGen = arc .startAngle(d.startAngle * value) .endAngle(d.endAngle * value); const style = number != null ? number % data.length === i ? { ...normalStyle, ...hoverStyle } : { ...normalStyle, ...noHoverStyle } : normalStyle; return ( ); })}
{data && data.length > 0 ? ( ) : null} {showTrueVlaue ? null : {per}} {data && data.length > 0 && data[number % data.length].x}
{data && data.length > 0 && data.map((item: any, i: number) => { return (
{item.x}
); })}
); }; export default ActionRingChart;