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"
/>