/** @format */ import * as d3 from 'd3'; import _ from 'lodash'; import React, { useEffect, useRef } from 'react'; import styled from 'styled-components'; const Box = styled.div``; interface ProgressProps { size?: any; [key: string]: any; } const Progress = (props: ProgressProps) => { const { size = { width: 100, height: 100 }, data = { total: 100, value: 80, name: '未完成', }, option, } = props; // 空数据返回null if (JSON.stringify(data) === '{}' || data.length === 0) { return null; } const styleBase = { height: 44, width: 120, }; const svgRef = useRef(null); const firstUpdate = useRef(true); const pie = d3 .pie() .sort(null) // 在创建布局的时候,默认排序数据从大到小,设为null可以按照数组默认顺序排序 .value(function(d: any) { return d.y; }); useEffect(() => { if (firstUpdate.current) { firstUpdate.current = false; onDraw(true); } else { onDraw(false); } return () => { d3.select(svgRef.current) .selectAll('g') .remove(); }; }, [size.height, size.width, option]); function onDraw(flag: boolean) { const svg = d3.select(svgRef.current); // pie是一个函数 const pieData = pie(data); const radius = d3.min([size.width, size.height]); // 创建计算弧形路径的函数 const arc = d3 .arc() .outerRadius(radius * 0.5 * option.series[0].radius[1]) .innerRadius(radius * 0.5 * option.series[0].radius[0]) .cornerRadius(option.charts.borderRadius) // 圆环润角 .startAngle(0.8 * Math.PI); const arcs = svg .append('g') .attr( 'transform', 'translate(' + size.width / 2 + ',' + size.height / 2 + ')', ); // 添加弧形元素(g中的path) option.series.length > 0 && option.series.forEach((item: any, index: number) => { if (item.itemStyle.colorLinear) { const rbg = svg .append('g') .append('linearGradient') .attr('id', `linearCircular${index}`) .attr('x1', 0) .attr('x2', '100%') .attr('y1', 0) .attr('y2', 0); rbg .append('stop') .attr('offset', '0%') .attr('stop-color', option.series[index].itemStyle.color1); rbg .append('stop') .attr('offset', '100%') .attr('stop-color', option.series[index].itemStyle.color2); } }); // 底色环 arcs .append('path') .datum({ endAngle: -0.8 * Math.PI }) .attr('fill', option.charts.bgColor) .attr('d', arc); const tem = d3.interpolate(0.8 * Math.PI, -0.8 * Math.PI); if (flag) { // 数据环 arcs .append('path') .datum({ endAngle: tem(data.value / data.total) }) .attr('fill', function(d: any, i: number) { if (option.series[i].itemStyle.color) { if (option.series[i].itemStyle.colorLinear) { return `url(#linearCircular${i})`; } return option.series[i].itemStyle.color; } return getColor(i); }) .transition() .duration(750) .attrTween('d', function(d) { const compute = d3.interpolate(0.8 * Math.PI, d.endAngle); return function(t) { d.endAngle = compute(t); return arc(d); }; }); } else { arcs .append('path') .datum({ endAngle: tem(data.value / data.total) }) .attr('fill', function(d: any, i: number) { if (option.series[i].itemStyle.color) { if (option.series[i].itemStyle.colorLinear) { return `url(#linearCircular${i})`; } return option.series[i].itemStyle.color; } return getColor(i); }) .attr('d', function(d: any) { return arc(d); }); } // 环内背景 arcs .append('circle') .attr('r', option.charts.radius) .attr('fill', option.charts.bgColor); // 数值 arcs .append('text') .text(data.value) .attr('text-anchor', 'middle') .attr('dominant-baseline', 'middle') .attr('fill', option.charts.numberStyle.color) .style('font-family', option.charts.numberStyle.fontFamily) .style('font-weight', option.charts.numberStyle.fontWeight) .style('font-size', option.charts.numberStyle.fontSize); // 文字 arcs .append('text') .text(data.name) .attr('text-anchor', 'middle') .attr('dominant-baseline', 'middle') .attr('fill', option.charts.textStyle.color) .style('font-family', option.charts.textStyle.fontFamily) .style('font-weight', option.charts.textStyle.fontWeight) .style('font-size', option.charts.textStyle.fontSize) .attr( 'transform', 'translate(0,' + radius * 0.5 * option.series[0].radius[0] + ')', ); } function getColor(idx: number) { const palette = [ '#2ec7c9', '#b6a2de', '#5ab1ef', '#ffb980', '#d87a80', '#8d98b3', '#e5cf0d', '#97b552', '#95706d', '#dc69aa', '#07a2a4', '#9a7fd1', '#588dd5', '#f5994e', '#c05050', '#59678c', '#c9ab00', '#7eb00a', '#6f5553', '#c14089', ]; return palette[idx % palette.length]; } const style = _.merge(styleBase, size); return ( ); }; Progress.themeUpdate = function(params: any) { return { outerBg: params.primaryColor, innerBg: params.thirdColor, }; }; export default Progress;