水波图

作者: 金桔柠檬加冰 | 来源:发表于2019-08-16 14:06 被阅读0次

    父页面

    import React, { Component } from 'react'
    import WaterWave2 from '../../components/WaterWave2'
    
    class WWave2 extends Component {
      constructor(props) {
        super(props)
        this.state = {
          dateType: '本日',
          contactData: null
        }
      }
    
      render() {
        let { dateType } = this.state
        return (
          <div>
            <WaterWave2
                height={150}
                title={dateType+"完成率"}
                percent='40'
            />
          </div>
        )
      }
    }
    
    export default WWave2
    

    WaterWave2组件中的index.js

    import React, { PureComponent } from 'react';
    import autoHeight from './autoHeight';
    import './index.less';
    
    /* eslint no-return-assign: 0 */
    /* eslint no-mixed-operators: 0 */
    // riddle: https://riddle.alibaba-inc.com/riddles/2d9a4b90
    
    @autoHeight()
    class WaterWave2 extends PureComponent {
      state = {
        radio: 1,
      };
    
      componentDidMount() {
        this.renderChart();
        this.resize();
        window.addEventListener(
          'resize',
          () => {
            requestAnimationFrame(() => this.resize());
          },
          { passive: true }
        );
      }
    
      componentDidUpdate(props) {
        const { percent } = this.props;
        if (props.percent !== percent) {
          // 不加这个会造成绘制缓慢
          this.renderChart('update');
        }
      }
    
      componentWillUnmount() {
        cancelAnimationFrame(this.timer);
        if (this.node) {
          this.node.innerHTML = '';
        }
        window.removeEventListener('resize', this.resize);
      }
    
      resize = () => {
        if (this.root) {
          const { height } = this.props;
          const { offsetWidth } = this.root.parentNode;
          this.setState({
            radio: offsetWidth < height ? offsetWidth / height : 1,
          });
        }
      };
    
      renderChart(type) {
        const { percent, color = '#1890FF' } = this.props;
        const data = percent / 100;
        const self = this;
        cancelAnimationFrame(this.timer);
    
        if (!this.node || (data !== 0 && !data)) {
          return;
        }
    
        const canvas = this.node;
        const ctx = canvas.getContext('2d');
        const canvasWidth = canvas.width;
        const canvasHeight = canvas.height;
        const radius = canvasWidth / 2;
        const lineWidth = 2;
        const cR = radius - lineWidth;
    
        ctx.beginPath();
        ctx.lineWidth = lineWidth * 2;
    
        const axisLength = canvasWidth - lineWidth;
        const unit = axisLength / 8;
        const range = 0.2; // 振幅
        let currRange = range;
        const xOffset = lineWidth;
        let sp = 0; // 周期偏移量
        let currData = 0;
        const waveupsp = 0.005; // 水波上涨速度
    
        let arcStack = [];
        const bR = radius - lineWidth;
        const circleOffset = -(Math.PI / 2);
        let circleLock = true;
    
        for (let i = circleOffset; i < circleOffset + 2 * Math.PI; i += 1 / (8 * Math.PI)) {
          arcStack.push([radius + bR * Math.cos(i), radius + bR * Math.sin(i)]);
        }
    
        const cStartPoint = arcStack.shift();
        ctx.strokeStyle = color;
        ctx.moveTo(cStartPoint[0], cStartPoint[1]);
    
        function drawSin() {
          ctx.beginPath();
          ctx.save();
    
          const sinStack = [];
          for (let i = xOffset; i <= xOffset + axisLength; i += 20 / axisLength) {
            const x = sp + (xOffset + i) / unit;
            const y = Math.sin(x) * currRange;
            const dx = i;
            const dy = 2 * cR * (1 - currData) + (radius - cR) - unit * y;
    
            ctx.lineTo(dx, dy);
            sinStack.push([dx, dy]);
          }
    
          const startPoint = sinStack.shift();
    
          ctx.lineTo(xOffset + axisLength, canvasHeight);
          ctx.lineTo(xOffset, canvasHeight);
          ctx.lineTo(startPoint[0], startPoint[1]);
    
          const gradient = ctx.createLinearGradient(0, 0, 0, canvasHeight);
          gradient.addColorStop(0, '#ffffff');
          gradient.addColorStop(1, color);
          ctx.fillStyle = gradient;
          ctx.fill();
          ctx.restore();
        }
    
        function render() {
          ctx.clearRect(0, 0, canvasWidth, canvasHeight);
          if (circleLock && type !== 'update') {
            if (arcStack.length) {
              const temp = arcStack.shift();
              ctx.lineTo(temp[0], temp[1]);
              ctx.stroke();
            } else {
              circleLock = false;
              ctx.lineTo(cStartPoint[0], cStartPoint[1]);
              ctx.stroke();
              arcStack = null;
    
              ctx.globalCompositeOperation = 'destination-over';
              ctx.beginPath();
              ctx.lineWidth = lineWidth;
              ctx.arc(radius, radius, bR, 0, 2 * Math.PI, 1);
    
              ctx.beginPath();
              ctx.save();
              ctx.arc(radius, radius, radius - 3 * lineWidth, 0, 2 * Math.PI, 1);
    
              ctx.restore();
              ctx.clip();
              ctx.fillStyle = color;
            }
          } else {
            if (data >= 0.85) {
              if (currRange > range / 4) {
                const t = range * 0.01;
                currRange -= t;
              }
            } else if (data <= 0.1) {
              if (currRange < range * 1.5) {
                const t = range * 0.01;
                currRange += t;
              }
            } else {
              if (currRange <= range) {
                const t = range * 0.01;
                currRange += t;
              }
              if (currRange >= range) {
                const t = range * 0.01;
                currRange -= t;
              }
            }
            if (data - currData > 0) {
              currData += waveupsp;
            }
            if (data - currData < 0) {
              currData -= waveupsp;
            }
    
            sp += 0.07;
            drawSin();
          }
          self.timer = requestAnimationFrame(render);
        }
        render();
      }
    
      render() {
        const { radio } = this.state;
        const { percent, title, height } = this.props;
        return (
          <div
            className='waterWave'
            ref={n => (this.root = n)}
            style={{ transform: `scale(${radio})` }}
          >
            {/* style={{ width: height, height, overflow: 'hidden' }} */}
            <div  style={{ overflow: 'hidden' }} >
              <canvas
                className='waterWaveCanvasWrapper'
                ref={n => (this.node = n)}
                width={height * 2}
                height={height * 2}
              />
            </div>
          </div>
        );
      }
    }
    export default WaterWave2;
    
    

    autoHeight.js

    /* eslint eqeqeq: 0 */
    import React from 'react';
    
    function computeHeight(node) {
      const totalHeight = parseInt(getComputedStyle(node).height, 10);
      const padding =
        parseInt(getComputedStyle(node).paddingTop, 10) +
        parseInt(getComputedStyle(node).paddingBottom, 10);
      return totalHeight - padding;
    }
    
    function getAutoHeight(n) {
      if (!n) {
        return 0;
      }
    
      let node = n;
    
      let height = computeHeight(node);
    
      while (!height) {
        node = node.parentNode;
        if (node) {
          height = computeHeight(node);
        } else {
          break;
        }
      }
    
      return height;
    }
    
    const autoHeight = () => WrappedComponent =>
      class extends React.Component {
        state = {
          computedHeight: 0,
        };
    
        componentDidMount() {
          const { height } = this.props;
          if (!height) {
            const h = getAutoHeight(this.root);
            // eslint-disable-next-line
            this.setState({ computedHeight: h });
          }
        }
    
        handleRoot = node => {
          this.root = node;
        };
    
        render() {
          const { height } = this.props;
          const { computedHeight } = this.state;
          const h = height || computedHeight;
          return (
            <div ref={this.handleRoot}>{h > 0 && <WrappedComponent {...this.props} height={h} />}</div>
          );
        }
      };
    
    export default autoHeight;
    
    

    index.less

    @import '../../../node_modules/antd/lib/style/themes/default.less';
    
    .waterWave {
      position: relative;
      display: inline-block;
      transform-origin: left;
      .text {
        position: absolute;
        top: 32px;
        left: 0;
        width: 100%;
        text-align: center;
        span {
          color: @text-color-secondary;
          font-size: 14px;
          line-height: 22px;
        }
        h4 {
          color: @heading-color;
          font-size: 24px;
          line-height: 32px;
        }
      }
      .waterWaveCanvasWrapper {
        transform: scale(0.5);
        transform-origin: 0 0;
      }
    }
    
    

    效果图

    image.png

    相关文章

      网友评论

          本文标题:水波图

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