水波图

父页面

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 (
      
) } } 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 (
      
(this.root = n)} style={{ transform: `scale(${radio})` }} > {/* style={{ width: height, height, overflow: 'hidden' }} */}
(this.node = n)} width={height * 2} height={height * 2} />
); } } 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 (
        
{h > 0 && }
); } }; 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

你可能感兴趣的:(水波图)