canvas之进度条
效果:
封装的组件
<template><div class="circle" :style="{ width: props.radius + 'px', height: props.radius + 'px' }"><div class="circle-bg" :style="{ width: props.radius - 5 + 'px', height: props.radius - 5 + 'px', borderWidth: props.lineWidth + 'px', borderColor: props.borderColor }"></div><canvas :id="circleProgressId" class="circle-progress"></canvas></div>
</template>
<script lang="ts" setup>
import { uniqueId } from 'lodash';const props = withDefaults(defineProps<{radius?: number; // 半径color?: string[]; // 渐变颜色progress: number; // 进度lineWidth?: number; // 线宽}>(),{radius: 35,color: () => ['rgb(72, 157, 125)', 'rgb(52, 254, 125)'],progress: 50,lineWidth: 16,borderColor: 'rgba(0, 150, 255, 1)',}
);// 随机表格id
const circleProgressId: string = uniqueId('circleProgress_');watch(() => props.progress,() => {nextTick(() => {draw();});},{deep: true,immediate: true,}
);let circleLoading: any = null;const draw = () => {circleLoading && clearInterval(circleLoading);let process = 0.0; // 进度const canvas: any = document.getElementById(circleProgressId);if (!canvas) {setTimeout(draw, 200);return;}const ctx = canvas.getContext('2d');const percent = props.progress; // 最终百分比const circleX = canvas.width / 2; // 中心x坐标const circleY = canvas.height / 2; // 中心y坐标const radius = props.radius / 2; // 圆环半径const lineWidth = props.lineWidth; // 圆形线条的宽度// const fontSize = 50; // 字体大小circleLoading = setInterval(() => {loading();}, 33);// 画圆function circle(cx: any, cy: any, r: any) {ctx.beginPath();ctx.moveTo(cx + r, cy);ctx.lineWidth = lineWidth;ctx.strokeStyle = 'transparent';ctx.arc(cx, cy, r, 0, Math.PI * 2);ctx.closePath();ctx.stroke();}// 画弧线function sector(cx: any, cy: any, r: any, progress: number) {ctx.beginPath();ctx.moveTo(cx, cy - r);ctx.lineWidth = lineWidth;// 渐变色 - 可自定义const linGrad = ctx.createLinearGradient(circleX, circleY + radius + lineWidth, circleX, circleY - radius - lineWidth);linGrad.addColorStop(0.0, props.color[1]);linGrad.addColorStop(1.0, props.color[0]);// linGrad.addColorStop(0.5, '#9bc4eb');ctx.strokeStyle = linGrad;const startAngle = (Math.PI * 3) / 2;ctx.arc(cx, cy, r, startAngle, startAngle + Math.PI * 2 * progress);ctx.stroke();}// 刷新function loading() {// 清除canvas内容ctx.clearRect(0, 0, circleX * 2, circleY * 2);// 中间的字// ctx.font = fontSize + 'px April';// ctx.textAlign = 'center';// ctx.textBaseline = 'middle';// ctx.fillStyle = '#999';// ctx.fillText(parseFloat(process).toFixed(0) + '%', circleX, circleY);// 圆形circle(circleX, circleY, radius);// 圆弧process > 0 && sector(circleX, circleY, radius, process / 100);// 画到传入百分比后停止if (process === percent) {clearInterval(circleLoading);return;}// 控制结束时动画的速度if (process / percent > 0.9) {process += 0.8;} else if (process / percent > 0.8) {process += 1.05;} else if (process / percent > 0.7) {process += 1.25;} else {process += 1.5;}// 超出时用传入百分比if (process > percent) {process = percent;}}
};
</script>
<style lang="scss" scoped>
.circle {width: 100%;height: 100%;position: relative;.circle-bg {position: absolute;left: 50%;top: 50%;transform: translate(-50%, -50%);border: 8px solid rgba(42, 113, 88, 0.2); /* 圆环的颜色 */border-radius: 50%; /* 设置为50%表示绘制圆形 */}.circle-progress {position: absolute;left: 50%;top: 50%;transform: translate(-50%, -50%) scale(-1, 1) !important;}
}
</style>
使用方式
<CircleProgress:radius="60"borderColor="rgba(0, 150, 255, 1)":progress="75":color="['#0096FF', '#0096FF']":lineWidth="3"
/>