/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>
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。