提交 1e6fb97e 编写于 作者: D devilangelia

Auto Commit

上级 45b7b80a
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, reactive, ref } from 'vue'; import { reactive, ref } from 'vue';
import ProgressBar from './components/ProgressBar.vue' import ProgressBar from './components/ProgressBar.vue'
import { watch } from 'vue'; import { watch } from 'vue';
...@@ -18,10 +18,6 @@ const options = reactive({ ...@@ -18,10 +18,6 @@ const options = reactive({
text: { text: {
size: 50, // 自定义字体大小 size: 50, // 自定义字体大小
color: '#666', // 自定义字体颜色 color: '#666', // 自定义字体颜色
font: 'Arial', // 自定义字体
showPercentage: true, // 是否显示百分比
animation: true, // 是否开启动画
// content: '自定义文本'
} }
}) })
...@@ -42,10 +38,6 @@ function minus() { ...@@ -42,10 +38,6 @@ function minus() {
options.data -= step; options.data -= step;
} }
} }
onMounted(() => {
progressBarRef.value?.init()
})
</script> </script>
<template> <template>
......
<script lang="ts" setup> <script lang="ts" setup>
import { ref, watch, computed } from 'vue'; /**
* 环形进度条组件
* @description 一个可自定义的环形进度条组件,支持渐变色、动画效果和文本显示
*
* @example
* ```vue
* <template>
* <ProgressBar
* :options="{
* data: 75, // 当前值
* maxValue: 200, // 最大值
* lineWidth: 10, // 圆环宽度
* radius: 50, // 圆环半径
* color: ['#66DD00', '#0066FF'], // 渐变色
* text: {
* showPercentage: false // 显示原始值
* }
* }"
* />
* </template>
* ```
*
* @props
* options: {
* data: number; // 当前值
* maxValue?: number; // 最大值,默认 100
* lineWidth?: number; // 圆环宽度,默认 40
* radius?: number; // 圆环半径,默认 100
* color?: string | string[]; // 圆环颜色,支持单色或渐变色数组
* width?: number; // 画布宽度,默认自适应
* height?: number; // 画布高度,默认自适应
* background?: string; // 画布背景色,默认 #fff
* text?: {
* size?: number | null; // 字体大小,null 时自动计算
* color?: string; // 字体颜色,默认 #333
* font?: string; // 字体样式,默认 Arial
* content?: string | number; // 自定义显示文本
* animation?: boolean; // 是否启用数字动画,默认 true
* showPercentage?: boolean; // true显示百分比,false显示原始值,默认 true
* }
* }
*
* @features
* - 自适应画布大小:根据圆环大小自动计算画布尺寸
* - 渐变色支持:可使用数组配置多个颜色实现渐变
* - 文本显示:支持自定义文本、百分比/原始值切换
* - 动画效果:数值变化时平滑过渡
* - 自适应字体:根据圆环大小自动计算合适的字体大小
* - 圆角样式:圆环端点为圆角
* - 透明中心:圆环中心区域透明
*/
import { ref, watch, computed, onMounted } from 'vue';
interface ChartOptions { interface ChartOptions {
data: number; data: number;
...@@ -56,6 +108,7 @@ const props = withDefaults(defineProps<{ ...@@ -56,6 +108,7 @@ const props = withDefaults(defineProps<{
}) })
const myCanvas = ref<HTMLCanvasElement | null>(null); const myCanvas = ref<HTMLCanvasElement | null>(null);
const chartOptions = ref<ChartOptions | null>(null);
// 添加动画执行标志 // 添加动画执行标志
const isAnimating = ref(false); const isAnimating = ref(false);
...@@ -73,12 +126,10 @@ const minCanvasSize = computed(() => { ...@@ -73,12 +126,10 @@ const minCanvasSize = computed(() => {
// 修改画布尺寸的计算属性 // 修改画布尺寸的计算属性
const canvasWidth = computed(() => { const canvasWidth = computed(() => {
// 如果设置了宽度,使用设置的宽度,否则使用计算的最小尺寸
return props.options.width ?? minCanvasSize.value; return props.options.width ?? minCanvasSize.value;
}); });
const canvasHeight = computed(() => { const canvasHeight = computed(() => {
// 如果设置了高度,使用设置的高度,否则使用计算的最小尺寸
return props.options.height ?? minCanvasSize.value; return props.options.height ?? minCanvasSize.value;
}); });
...@@ -139,30 +190,40 @@ const fontSize = computed(() => { ...@@ -139,30 +190,40 @@ const fontSize = computed(() => {
); );
}); });
// 监听 props.options 的变化,更新 chartOptions
watch(() => props.options, (newVal) => {
// 只在没有通过 init 设置配置时更新
if (!chartOptions.value || Object.keys(chartOptions.value).length === 0) {
chartOptions.value = { ...newVal };
}
}, { deep: true });
// 监听 data 的变化
watch(() => props.options.data, (_, oldVal) => {
update(oldVal);
});
function startDraw(end: number, start?: number, dontClear?: boolean) { function startDraw(end: number, start?: number, dontClear?: boolean) {
const ctx = myCanvas.value?.getContext("2d"); const ctx = myCanvas.value?.getContext("2d");
if (!ctx) return; if (!ctx) return;
if (!dontClear) { if (!dontClear) {
// 使用计算属性获取画布尺寸
ctx.clearRect(0, 0, canvasWidth.value, canvasHeight.value); ctx.clearRect(0, 0, canvasWidth.value, canvasHeight.value);
} }
// 根据画布尺寸计算中心点
const centerX = canvasWidth.value / 2; const centerX = canvasWidth.value / 2;
const centerY = canvasHeight.value / 2; const centerY = canvasHeight.value / 2;
// 使用计算出的最大半径
const radius = Math.min(props.options.radius || 100, maxRadius.value); const radius = Math.min(props.options.radius || 100, maxRadius.value);
const lineWidth = props.options.lineWidth; const lineWidth = props.options.lineWidth;
// 创建渐变色
let strokeStyle: string | CanvasGradient = 'red'; let strokeStyle: string | CanvasGradient = 'red';
if (props.options.color) { if (props.options.color) {
if (Array.isArray(props.options.color)) { if (Array.isArray(props.options.color)) {
if (props.options.color.length) { if (props.options.color.length) {
const gradient = ctx.createLinearGradient( const gradient = ctx.createLinearGradient(
centerX - (radius || 100), centerX - radius,
centerY - (radius || 100), centerY - radius,
centerX + (radius || 100), centerX + radius,
centerY + (radius || 100) centerY + radius
); );
const colors = props.options.color; const colors = props.options.color;
if (colors?.length === 1) { if (colors?.length === 1) {
...@@ -175,7 +236,6 @@ function startDraw(end: number, start?: number, dontClear?: boolean) { ...@@ -175,7 +236,6 @@ function startDraw(end: number, start?: number, dontClear?: boolean) {
} }
} }
} else { } else {
// 如果是单个颜色字符串
strokeStyle = props.options.color; strokeStyle = props.options.color;
} }
} }
...@@ -210,18 +270,15 @@ function startDraw(end: number, start?: number, dontClear?: boolean) { ...@@ -210,18 +270,15 @@ function startDraw(end: number, start?: number, dontClear?: boolean) {
// 处理显示文本 // 处理显示文本
let displayText: string; let displayText: string;
if (props.options.text?.content !== undefined) { if (props.options.text?.content !== undefined) {
// 使用自定义文本
displayText = String(props.options.text.content); displayText = String(props.options.text.content);
} else { } else {
const maxValue = props.options.maxValue || 100; const maxValue = props.options.maxValue || 100;
const currentValue = currentDisplayNumber.value; const currentValue = currentDisplayNumber.value;
if (props.options.text?.showPercentage !== false) { if (props.options.text?.showPercentage !== false) {
// 显示百分比
const percentage = Math.round((currentValue / maxValue) * 100); const percentage = Math.round((currentValue / maxValue) * 100);
displayText = `${percentage}%`; displayText = `${percentage}%`;
} else { } else {
// 显示原始数值
displayText = String(Math.round(currentValue)); displayText = String(Math.round(currentValue));
} }
} }
...@@ -279,14 +336,12 @@ function update(oldData: number) { ...@@ -279,14 +336,12 @@ function update(oldData: number) {
draw: (progress) => { draw: (progress) => {
progress = Math.max(0, progress); progress = Math.max(0, progress);
const maxValue = props.options.maxValue || 100; const maxValue = props.options.maxValue || 100;
// 使用最大值参数计算当前值的比例
const maxData = Math.min(props.options.data, maxValue); const maxData = Math.min(props.options.data, maxValue);
const currentData = oldData + (maxData - oldData) * progress; const currentData = oldData + (maxData - oldData) * progress;
// 根据最大值计算角度
const end = Math.PI * (1.5 + (currentData / maxValue) * 2); const end = Math.PI * (1.5 + (currentData / maxValue) * 2);
if (props.options.text?.animation !== false && props.options.text?.content === undefined) { if (props.options.text?.animation !== false &&
// 更新显示的数值为实际值,而不是百分比 props.options.text?.content === undefined) {
currentDisplayNumber.value = currentData; currentDisplayNumber.value = currentData;
} }
...@@ -326,15 +381,10 @@ function init() { ...@@ -326,15 +381,10 @@ function init() {
}); });
} }
watch(() => props.options.data, (_, oldVal) => { onMounted(() => {
// 直接监听 data 的变化,而不是整个 options 对象 init();
update(oldVal);
}); });
defineExpose({
init,
update
});
</script> </script>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册