index.js 4.9 KB
import React, { PureComponent } from 'react';
import G2 from 'g2';
import Bind from 'lodash-decorators/bind';
import Debounce from 'lodash-decorators/debounce';
import equal from '../equal';

const { Shape } = G2;

const primaryColor = '#2F9CFF';
const backgroundColor = '#F0F2F5';

/* eslint no-underscore-dangle: 0 */
class Gauge extends PureComponent {
  componentDidMount() {
    setTimeout(() => {
      this.renderChart();
    }, 10);
    window.addEventListener('resize', this.resize);
  }

  componentWillReceiveProps(nextProps) {
    if (!equal(this.props, nextProps)) {
      setTimeout(() => {
        this.renderChart(nextProps);
      }, 10);
    }
  }

  componentWillUnmount() {
    window.removeEventListener('resize', this.resize);
    // if (this.chart) {
    //   console.log('data', this.chart);
    //   this.chart.destroy();
    // }
    this.resize.cancel();
  }

  @Bind()
  @Debounce(200)
  resize() {
    if (!this.node) {
      return;
    }
    this.renderChart([]);
  }

  handleRef = (n) => {
    this.node = n;
  };

  initChart(nextProps) {
    const { xUnit, color = primaryColor } = nextProps || this.props;

    Shape.registShape('point', 'dashBoard', {
      drawShape(cfg, group) {
        const originPoint = cfg.points[0];
        const point = this.parsePoint({ x: originPoint.x, y: 0.4 });

        const center = this.parsePoint({
          x: 0,
          y: 0,
        });

        const shape = group.addShape('polygon', {
          attrs: {
            points: [
              [center.x, center.y],
              [point.x + 8, point.y],
              [point.x + 8, point.y - 2],
              [center.x, center.y - 2],
            ],
            radius: 2,
            lineWidth: 2,
            arrow: false,
            fill: color,
          },
        });

        group.addShape('Marker', {
          attrs: {
            symbol: 'circle',
            lineWidth: 2,
            fill: color,
            radius: 8,
            x: center.x,
            y: center.y,
          },
        });
        group.addShape('Marker', {
          attrs: {
            symbol: 'circle',
            lineWidth: 2,
            fill: '#fff',
            radius: 5,
            x: center.x,
            y: center.y,
          },
        });

        const { origin } = cfg;
        group.addShape('text', {
          attrs: {
            x: center.x,
            y: center.y + 80,
            text: `${origin._origin.value}%`,
            textAlign: 'center',
            fontSize: 24,
            fill: 'rgba(0, 0, 0, 0.85)',
          },
        });
        group.addShape('text', {
          attrs: {
            x: center.x,
            y: center.y + 45,
            text: xUnit,
            textAlign: 'center',
            fontSize: 14,
            fill: 'rgba(0, 0, 0, 0.43)',
          },
        });

        return shape;
      },
    });
  }

  renderChart(nextProps) {
    if (this.node.offsetWidth === 0) {
      return;
    }
    const {
      height, color = primaryColor, bgColor = backgroundColor, xUnit,
    } = nextProps || this.props;
    const data = [{ name: xUnit, value: this.props.data }];

    if (this.chart) {
      this.chart.clear();
    }
    if (this.chart) {
      this.chart.destroy();
    }
    if (this.node) {
      this.node.innerHTML = '';
    }

    this.initChart(nextProps);
    const chart = new G2.Chart({
      container: this.node,
      forceFit: true,
      height,
      animate: false,
      plotCfg: {
        margin: [10, 10, 30, 10],
      },
    });

    chart.source(data);

    chart.tooltip(false);

    chart.coord('gauge', {
      startAngle: -1.2 * Math.PI,
      endAngle: 0.20 * Math.PI,
    });
    chart.col('value', {
      type: 'linear',
      nice: true,
      min: 0,
      max: 100,
      tickCount: 6,
    });
    chart.axis('value', {
      subTick: false,
      tickLine: {
        stroke: color,
        lineWidth: 2,
        value: -14,
      },
      labelOffset: -12,
      formatter: (val) => {
        switch (parseInt(val, 10)) {
          case 20:
            return '差';
          case 40:
            return '中';
          case 60:
            return '良';
          case 80:
            return '优';
          default:
            return '';
        }
      },
    });
    chart.point().position('value').shape('dashBoard');
    draw(data);

    /* eslint no-shadow: 0 */
    function draw(data) {
      const val = data[0].value;
      const lineWidth = 12;
      chart.guide().clear();

      chart.guide().arc(() => {
        return [0, 0.95];
      }, () => {
        return [val, 0.95];
      }, {
        stroke: color,
        lineWidth,
      });

      chart.guide().arc(() => {
        return [val, 0.95];
      }, (arg) => {
        return [arg.max, 0.95];
      }, {
        stroke: bgColor,
        lineWidth,
      });

      chart.changeData(data);
    }

    this.chart = chart;
  }

  render() {
    return (
      <div ref={this.handleRef} />
    );
  }
}

export default Gauge;