/** @format */ import * as d3 from 'd3'; import _ from 'lodash'; import React, { useEffect, useRef } from 'react'; interface TimerProps { size?: any; [key: string]: any; } const Timer = (props: TimerProps) => { const { size = { width: 130, height: 200 }, data = [ { name: '牛奶', value: 300 }, { name: '咖啡', value: 200 }, { name: '面包', value: 200 }, ], option, series, } = props; const svgRef = useRef(null); const firstUpdate = useRef(true); useEffect(() => { if (firstUpdate.current) { firstUpdate.current = false; onDraw(true); } else { onDraw(false); } return () => { d3.select(svgRef.current) .selectAll('.legend') .remove(); d3.select(svgRef.current) .selectAll('g') .remove(); }; }, [size.height, size.width, data, option]); function onDraw(flag: boolean) { const box = d3.select(svgRef.current); box.style('display', 'flex').style('flex-wrap', 'wrap'); // 布局方向 if (option.direction === 'level') { box.style('flex-direction', 'row'); } else { box.style('flex-direction', 'column'); } const legend = box .selectAll('div') .data(data) .enter() .append('div') .attr('class', 'legend'); // 容器排列 和间距 legend .style('display', 'flex') .style('margin-right', option.levelMargin / 2 + 'px') .style('margin-bottom', option.verticalMargin / 2 + 'px') .style('margin-left', option.levelMargin / 2 + 'px') .style('margin-top', option.verticalMargin / 2 + 'px'); // 数值排列 if (option.numberDirection === 'level') { legend.style('flex-direction', 'row').style('align-items', 'flex-start'); } else { legend .style('flex-direction', 'column') .style('align-items', 'flex-start'); } const legends = legend .append('div') .style('display', 'flex') .style('flex-direction', 'row') .style('align-items', 'center'); const svg = legends.append('svg'); // 渐变色 series.length > 0 && series.forEach((item: any, index: number) => { if (item?.colorLinear) { const rbg = svg .append('g') .append('linearGradient') .attr('id', `linearLegend${index}`) .attr('x1', 0) .attr('x2', '100%') .attr('y1', 0) .attr('y2', 0); rbg .append('stop') .attr('offset', '0%') .attr('stop-color', item.color1); rbg .append('stop') .attr('offset', '10%') .attr('stop-color', item.color1); rbg .append('stop') .attr('offset', '100%') .attr('stop-color', item.color2); } }); // 图例方块 if (option.iconType === 'rect') { svg .attr('width', option.iconWidth) .attr('height', option.iconHeight) .append('rect') .attr('width', option.iconWidth) .attr('height', option.iconHeight) .attr('fill', function(d, i) { if (series[i] && series[i]?.colorLinear) { return `url(#linearLegend${i})`; } if (series[i] && series[i]?.color) { return series[i].color; } return getColor(i); }); } if (option.iconType === 'diamond') { svg .attr('width', option.iconWidth * Math.sqrt(2)) .attr('height', option.iconHeight * Math.sqrt(2)) .append('rect') .attr('width', d3.min([option.iconWidth, option.iconHeight])) .attr('height', d3.min([option.iconWidth, option.iconHeight])) .attr('fill', function(d, i) { if (series[i] && series[i]?.colorLinear) { return `url(#linearLegend${i})`; } if (series[i] && series[i]?.color) { return series[i].color; } return getColor(i); }) .attr('y', 0) .attr('x', d3.min([option.iconWidth, option.iconHeight]) / 2) .style('transform', 'rotate(45deg)') .style('transform-origin', 'center'); } if (option.iconType === 'circle') { svg .attr('width', d3.max([option.iconWidth, option.iconHeight])) .attr('height', d3.max([option.iconWidth, option.iconHeight])) .append('circle') .attr('r', d3.max([option.iconWidth / 2, option.iconHeight / 2])) .attr('cx', d3.max([option.iconWidth / 2, option.iconHeight / 2])) .attr('cy', d3.max([option.iconWidth / 2, option.iconHeight / 2])) .attr('fill', function(d, i) { if (series[i] && series[i]?.colorLinear) { return `url(#linearLegend${i})`; } if (series[i] && series[i]?.color) { return series[i].color; } return getColor(i); }); } if (option.iconType === 'roundRect') { svg .attr('width', option.iconWidth) .attr('height', option.iconHeight) .append('rect') .attr('width', option.iconWidth) .attr('height', option.iconHeight) .attr('rx', option.iconWidth / 4 + 'px') .attr('fill', function(d, i) { if (series[i] && series[i]?.colorLinear) { return `url(#linearLegend${i})`; } if (series[i] && series[i]?.color) { return series[i].color; } return getColor(i); }); } if (option.iconType === 'ringRect') { svg .attr('width', option.iconWidth) .attr('height', option.iconHeight) .append('rect') .attr('width', option.iconWidth) .attr('height', option.iconHeight) .attr('rx', option.iconHeight / 2) .attr('ry', option.iconHeight / 2) .attr('fill', function(d, i) { if (series[i] && series[i]?.colorLinear) { return `url(#linearLegend${i})`; } if (series[i] && series[i]?.color) { return series[i].color; } return getColor(i); }); } if (option.iconType === 'ring') { const arc = d3 .arc() .outerRadius(d3.max([option.iconWidth / 2, option.iconHeight / 2])) .innerRadius(d3.min([option.iconWidth / 2, option.iconHeight / 2])) .startAngle(0) .endAngle(2 * Math.PI); svg .attr('width', d3.max([option.iconWidth, option.iconHeight])) .attr('height', d3.max([option.iconWidth, option.iconHeight])) .append('g') .append('path') .attr( 'transform', `translate(${d3.max([ option.iconWidth / 2, option.iconHeight / 2, ])},${d3.max([option.iconWidth / 2, option.iconHeight / 2])})`, ) .attr('fill', function(d, i) { if (series[i]?.colorLinear) { return `url(#linearLegend${i})`; } if (series[i]?.color) { return series[i].color; } return getColor(i); }) .attr('d', arc); } if (option.iconType === 'none') { svg.attr('width', option.iconWidth).attr('height', option.iconHeight); } // 文字 间距 legends .append('p') .text(function(d) { return d.name; }) .style('color', function(d, index: number) { if (!series) { return; } return series[index]?.textStyle?.color; }) .style('font-family', function(d, index: number) { if (!series) { return; } return series[index]?.textStyle?.fontFamily; }) .style('font-size', function(d, index: number) { if (!series) { return; } return series[index]?.textStyle?.fontSize + 'px'; }) .style('font-weight', function(d, index: number) { if (!series) { return; } return series[index]?.textStyle?.fontWeight; }) .style('margin-left', option.textMargin + 'px'); if (option.isShowNumber) { // 数值 legend .append('p') .attr('class', 'number') .text(function(d) { return d.value; }) .style('color', option.numberStyle.color) .style('font-family', option.numberStyle.fontFamily) .style('font-size', option.numberStyle.fontSize + 'px') .style('font-weight', option.numberStyle.fontWeight) .style('margin-right', option.levelNumberMargin / 2 + 'px') .style('margin-bottom', option.verticalNumberMargin / 2 + 'px') .style('margin-left', option.levelNumberMargin / 2 + 'px') .style('margin-top', option.verticalNumberMargin / 2 + 'px'); } } 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]; } return
; }; export default Timer;