美文网首页React
React中d3(v4)的饼状图绘制与数据更新

React中d3(v4)的饼状图绘制与数据更新

作者: 2f1b6dfcc208 | 来源:发表于2018-05-11 12:20 被阅读3次

    类似页面如下:


    屏幕截图.png

    平时在各个页面中可能会遇到类似的饼状图显示,这里采用d3 (v4版) 绘制,就上面的饼状图可以抽象成一个通用的React组件,使用时只需要传入数据源即可,另外,更新操作也已经封装,直接更新传入的数据源即可。

    • 将d3中的饼状图封装成一个ES6的类
    //饼状图类
    export default class PieChart {
      constructor(
        wrapper,
        dataSource,
        color,
        width = wrapper.offsetWidth,
        height = wrapper.offsetHeight
      ) {
        this.wrapper = wrapper; //容器元素
        this.dataSource = this.handleDataBefore(dataSource);
        this.width = width;
        this.height = height;
        this.radius = Math.min(this.width, this.height) / 2;
        this.color = d3
          .scaleOrdinal()
          .domain(d3.range(this.handleColorBefore(color).length))
          .range(this.handleColorBefore(color)); 
    
        this.initChart();
      }
    
      // 添加默认显示
      handleDataBefore(data) {
        const total = data.reduce((total, current) => (total + current.count), 0)
        if (total === 0) {
          data[data.length] = { region: 'default', count: 1 };
        } else {
          data[data.length] = { region: 'default', count: 0 };
        }
        return data;
      }
      // 添加默认颜色显示
      handleColorBefore(color) {
        color[color.length] = '#B6B9C2';
        return color;
      }
    
      initChart() {
        const pie = d3
          .pie()
          .value(function (d) {
            return d.count;
          })
          .padAngle(0.02)
          .sort(null);
        const arc = d3
          .arc()
          .innerRadius(this.radius * 0.85)
          .outerRadius(this.radius)
          .cornerRadius(5);
        const svg = d3
          .select(this.wrapper)
          .append('svg')
          .attr('width', this.width)
          .attr('height', this.height)
          .attr('viewBox', '0 0 ' + this.width + ' ' + this.height) //当宽高改变时,图表响应式
          .attr('style', 'width:100%')
          .append('g')
          .attr(
            'transform',
            'translate(' + this.width / 2 + ',' + this.height / 2 + ')'
          );
        this.pie = pie;
        this.arc = arc;
        this.svg = svg;
        this.drawChart(this.dataSource);
      }
      drawChart(data) {
        const color = this.color;
        const pie = this.pie;
        const arc = this.arc;
        const svg = this.svg;
    
        let path = svg.selectAll('path');
        const data0 = path.data();
        const data1 = pie(data);
    
        path = path.data(data1, key);
    
        path
          .transition()
          .duration(800)
          .attrTween('d', arcTween);
    
        path
          .enter()
          .append('path')
          .each(function (d, i) {
            var narc = findNeighborArc(i, data0, data1, key);
            if (narc) {
              this._current = narc;
              this._previous = narc;
            } else {
              this._current = d;
            }
          })
          .attr('fill', (d, i) => {
            return color(i);
          })
          .transition()
          .duration(600)
          .attrTween('d', arcTween);
    
        path
          .exit()
          .transition()
          .duration(300)
          // .delay(2000)
          .attrTween('d', function (d, index) {
            var currentIndex = this._previous.data.region;
            var i = d3.interpolateObject(d, this._previous);
            return function (t) {
              return arc(i(t));
            };
          })
          .remove();
    
        function key(d) {
          return d.data.region;
        }
    
        function findNeighborArc(i, data0, data1, key) {
          var d;
          if ((d = findPreceding(i, data0, data1, key))) {
            const obj = cloneObj(d);
            obj.startAngle = d.endAngle;
            return obj;
          } else if ((d = findFollowing(i, data0, data1, key))) {
            const obj = cloneObj(d);
            obj.endAngle = d.startAngle;
            return obj;
          }
          return null;
        }
    
        // Find the element in data0 that joins the highest preceding element in data1.
        function findPreceding(i, data0, data1, key) {
          const m = data0.length;
          while (--i >= 0) {
            var k = key(data1[i]);
            for (var j = 0; j < m; ++j) {
              if (key(data0[j]) === k) return data0[j];
            }
          }
        }
    
        // Find the element in data0 that joins the lowest following element in data1.
        function findFollowing(i, data0, data1, key) {
          const n = data1.length,
            m = data0.length;
          while (++i < n) {
            const k = key(data1[i]);
            for (var j = 0; j < m; ++j) {
              if (key(data0[j]) === k) return data0[j];
            }
          }
        }
    
        function arcTween(d) {
          const i = d3.interpolate(this._current, d);
          this._current = i(0);
          return function (t) {
            return arc(i(t));
          };
        }
    
        function cloneObj(obj) {
          const o = {};
          for (var i in obj) {
            o[i] = obj[i];
          }
          return o;
        }
      }
      updateChart(data) {
        this.drawChart(this.handleDataBefore(data));
      }
      destory() {
        d3.select(this.wrapper).remove(); //移除图表元素
      }
    }
    
    
    • 封装为React通用组件
    import React from 'react';
    import Chart from './pie.js';
    
    import PropTypes from 'prop-types';
    
    export default class PieChart extends React.PureComponent {
      static propTypes = {
        id: PropTypes.string.isRequired,
        dataSource: PropTypes.array.isRequired,
        color: PropTypes.array.isRequired,
        width: PropTypes.number,
        height: PropTypes.number
      }
      constructor(props) {
        super(props);
        this.chartRef = true;
      }
      componentWillUnmount() {
        this.destoryChart();
        this.chartRef = false;
      }
      componentWillReceiveProps(nextProps) {
        if (nextProps.dataSource !== this.props.dataSource) {
          this.updateChart(nextProps);
        }
      }
      drawChart = node => {
        if (this.chartRef) {
          const { dataSource, color } = this.props;
          this.chart = new Chart(node, dataSource, color);
        }
      };
      updateChart = props => {
        this.chart.updateChart(props.dataSource);
      };
      destoryChart = () => {
        this.chart.destory();
        this.chart = null;
      };
      render() {
        const { id, width = 135, height = 120 } = this.props;
        return <div id={id} style={{ width, height }} ref={this.drawChart} />;
      }
    }
    
    
    • 使用
    import React from 'react';
    import PieChart from 'components/PieChart'
    
    export default class Demo extends React.Component{
        state={
            inline:380,
            outline:120
        }
        render(){ 
            const {inline,outline}=this.state;
            const dataSource = [
                    { region: 'inline', count: inline },
                    { region: 'outline', count: outline }
                ]
            const color=['#fa919c', '#f83d59'];
            return(
                <div>
                    <PieChart id='device' dataSource={dataSource} color={color} />
                </div>
            )
        }
    }
    

    github地址:https://github.com/Hfimy/component/tree/master/components/PieChart

    相关文章

      网友评论

        本文标题:React中d3(v4)的饼状图绘制与数据更新

        本文链接:https://www.haomeiwen.com/subject/cpfnzxtx.html