Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
DevilAngelia
环形进度条组件,支持渐变色、动画效果和文本显示
提交
1e6fb97e
环
环形进度条组件,支持渐变色、动画效果和文本显示
项目概览
DevilAngelia
/
环形进度条组件,支持渐变色、动画效果和文本显示
与 Fork 源项目一致
Fork自
inscode / VueJS
通知
1
Star
1
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
环
环形进度条组件,支持渐变色、动画效果和文本显示
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
提交
1e6fb97e
编写于
2月 05, 2025
作者:
D
devilangelia
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Auto Commit
上级
45b7b80a
变更
2
隐藏空白更改
内联
并排
Showing
2 changed file
with
77 addition
and
35 deletion
+77
-35
src/App.vue
src/App.vue
+1
-9
src/components/ProgressBar.vue
src/components/ProgressBar.vue
+76
-26
未找到文件。
src/App.vue
浏览文件 @
1e6fb97e
<
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
>
...
...
src/components/ProgressBar.vue
浏览文件 @
1e6fb97e
<
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.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录