乐趣区

关于前端:taro小程序-环形进度条组件

/src/components/CanvasProgress/index.jsx

import Taro from "@tarojs/taro";
import React from "react";
import {View, Canvas, Image} from "@tarojs/components";

import styles from "./styles.module.less";

class CanvasProgress extends React.Component {
  static defaultProps = {style: {}, // 容器的款式
    progress: 0.6, // 进度 [0-1]
    backgroundColor: "#cccccc", // 背景环色彩
    lineColor: "#02a101", // 前景环色彩
    lineWidth: 5, // 线宽
  };

  state = {wrapperId: "_progress_wrapper" + randomStr(),
    canvasId: "_progress_canvas" + randomStr(),
    canvas: null,
    ctx: null,
    width: 0,
    height: 0,
    imageUrl: "",
  };

  componentDidMount() {this.init();
  }

  componentDidUpdate(prevProps) {if (prevProps.progress !== this.props.progress) {this.draw();
    }
  }

  init = async () => {const { wrapperId, canvasId} = this.state;
    const {width, height} = await getElementRect(wrapperId);
    const {canvas, ctx} = await getCanvas(canvasId);
    const dpr = Taro.getSystemInfoSync().pixelRatio;
    canvas.width = width * dpr;
    canvas.height = height * dpr;
    ctx.scale(dpr, dpr);
    this.setState({canvas, ctx, width, height}, this.draw);
  };

  draw = () => {const { progress, backgroundColor, lineWidth, lineColor} = this.props;
    const {canvas, ctx, width, height} = this.state;
    const radius = toFixed(0.5 * Math.min(width, height)) - lineWidth;
    const range = 2 * progress * Math.PI;
    const offsetRadian = -0.5 * Math.PI;

    ctx.lineWidth = lineWidth;
    ctx.lineCap = "round";
    ctx.beginPath();
    // 1
    ctx.strokeStyle = backgroundColor;
    ctx.arc(toFixed(width / 2), toFixed(height / 2), radius, 0, 2 * Math.PI);
    ctx.stroke();
    ctx.closePath();
    // 2
    ctx.beginPath();
    ctx.strokeStyle = lineColor;
    ctx.arc(toFixed(width / 2),
      toFixed(height / 2),
      radius,
      offsetRadian,
      range + offsetRadian
    );
    ctx.stroke();
    ctx.closePath();

    const base64URL = canvas.toDataURL("image/png", 0.5);
    this.setState({imageUrl: base64URL});
  };

  render() {const { style, children} = this.props;
    const {wrapperId, canvasId, imageUrl} = this.state;
    return (
      <View
        id={wrapperId}
        className={styles.container}
        style={{width: "200px", height: "200px", ...style}}
      >
        <Image className={styles.img} src={imageUrl} />
        <Canvas id={canvasId} type="2d" className={styles.canvas} />

        <View className={styles.content}>{children || ""}</View>
      </View>
    );
  }
}

// utils
const toFixed = (number = 0) => 0.01 * Math.floor(100 * number);

function randomStr(len = 16) {
  const string =
    "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
  const l = string.length;
  let str = "";
  for (let i = 0; i < len; i++) {const index = Math.floor((Math.random() * 100 * l) % l);
    str += string[index];
  }
  return str;
}

const getElementRect = async (eleId = "", delay = 200) => {return new Promise((resovle, reject) => {const t = setTimeout(() => {clearTimeout(t);

      Taro.createSelectorQuery()
        .select(`#${eleId}`)
        .boundingClientRect((rect) => {if (rect) {resovle(rect);
          } else {reject("获取不到元素");
          }
        })
        .exec();}, delay);
  });
};

const getCanvas = async (eleId = "", delay = 200) => {return new Promise((resolve, reject) => {const t = setTimeout(() => {clearTimeout(t);
      Taro.createSelectorQuery()
        .select(`#${eleId}`)
        .fields({node: true})
        .exec((res) => {if (res && res[0] && res[0].node) {const canvas = res[0].node;
            const ctx = canvas.getContext("2d");
            resolve({canvas, ctx});
          } else {reject("获取 canvas 失败");
          }
        });
    }, delay);
  });
};

export default CanvasProgress;

/src/components/styles.module.less

.container {
  position: relative;
  overflow: hidden;
}

.canvas,
.img {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

.canvas {transform: translateX(-1000vw);
}

.img {display: block;}

.content {
  position: absolute;
  z-index: 2;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

应用

<CanvasProgress
  style={{width:"200rpx", height: "200rpx", margin: "20rpx"}}
  progress={0.65}
>
  <Text>65%</Text>
</CanvasProgress>
退出移动版