项目概述

在这里插入图片描述

在这里插入图片描述

项目背景

圆形进度条是一种常见的进度展示组件,适用于需要节省空间或强调视觉效果的场景。本项目基于Qt/QML框架,实现了多种样式的圆形进度条,包括Donut(圆环)、Pie(饼图)和Line(线条)三种样式,为HarmonyOS应用提供灵活的圆形进度展示方案。

组件特性

本项目实现了以下三种圆形进度条样式:

  • Donut样式(圆环):带有内圆的圆环进度条,中间可显示文字
  • Pie样式(饼图):实心圆形进度条,类似饼图
  • Line样式(线条):细线条圆形进度条,简洁风格

所有样式均支持:

  • 自定义进度值(value、minimum、maximum)
  • 自定义颜色(进度色、背景色、文字色)
  • 自定义起始角度
  • 可选的文字显示
  • 可自定义的文字格式

技术栈选择

前端框架:Qt/QML

选择理由:

  1. Canvas API:QML的Canvas组件提供了强大的2D绘制能力,可以精确绘制圆弧和扇形
  2. 数学计算:JavaScript的Math函数可以方便地进行角度和弧度转换
  3. 灵活配置:通过属性控制不同样式,代码复用性高
  4. 性能优化:Canvas绘制性能优异,适合频繁更新

核心技术点

  • QtQuick 2.15:UI框架
  • Canvas API:2D绘制,特别是arc()方法
  • Math.PI:角度和弧度转换
  • 属性绑定:响应式更新

组件设计

整体架构

圆形进度条组件的架构设计:

Item (root)
└── Canvas
    ├── 背景圆绘制
    ├── 进度圆弧/扇形绘制
    ├── 内圆绘制(Donut样式)
    └── 文字绘制(可选)

核心属性

property real value: 0              // 当前值
property real minimum: 0            // 最小值
property real maximum: 100          // 最大值
property real progress: ...         // 计算得出的进度比例(0-1)
property int barStyle: 0            // 样式:0=Donut, 1=Pie, 2=Line
property real startAngle: -90       // 起始角度(度),-90表示从顶部开始
property color progressColor        // 进度颜色
property color backgroundColor      // 背景颜色
property color textColor            // 文字颜色
property bool showText: true        // 是否显示文字
property string textFormat: "%p%"   // 文字格式:%p=百分比, %v=值, %m=最大值

// Donut样式专用
property real outlineWidth: 1       // 轮廓宽度
property real dataWidth: 8          // 线条宽度(Line样式)

核心功能实现

1. 进度值计算

使用标准的进度计算方式:

property real progress: Math.max(0, Math.min(1, (value - minimum) / (maximum - minimum)))

确保进度值始终在0-1范围内。

2. Canvas绘制实现

2.1 初始化与清理
Canvas {
    id: canvas
    anchors.fill: parent
    
    onPaint: {
        if (width <= 0 || height <= 0) return
        
        var ctx = getContext("2d")
        if (!ctx) return
        
        ctx.clearRect(0, 0, width, height)  // 清除画布
        
        // ... 绘制内容
    }
}

说明:

  • 检查尺寸有效性,避免无效绘制
  • 清除画布,确保每次绘制都是干净的
2.2 计算中心点和半径
var centerX = width / 2
var centerY = height / 2
var outerRadius = Math.min(width, height) / 2 - 1  // 外圆半径,留1px边距
var innerRadius = barStyle === 0 ? outerRadius * 0.75 : 0  // 内圆半径(仅Donut样式)

关键技术点:

  • 使用Math.min(width, height)确保圆形适配不同宽高比
  • Donut样式的内圆半径为外圆的75%
  • 其他样式不需要内圆(innerRadius = 0
2.3 背景圆绘制

根据样式绘制不同的背景:

// Donut和Pie样式:填充背景圆
if (barStyle === 0 || barStyle === 1) {
    ctx.beginPath()
    ctx.arc(centerX, centerY, outerRadius, 0, 2 * Math.PI)
    ctx.fillStyle = backgroundColor
    ctx.fill()
}
// Line样式:描边背景圆
else if (barStyle === 2) {
    ctx.beginPath()
    ctx.arc(centerX, centerY, outerRadius - outlineWidth / 2, 0, 2 * Math.PI)
    ctx.strokeStyle = backgroundColor
    ctx.lineWidth = outlineWidth
    ctx.stroke()
}

说明:

  • Donut和Pie样式使用fill()填充
  • Line样式使用stroke()描边
  • Line样式的半径需要减去outlineWidth / 2,确保线条居中
2.4 进度绘制

根据进度值和样式绘制进度:

if (progress > 0) {
    var angle = progress * 360  // 进度角度(度)
    var startAngleRad = startAngle * Math.PI / 180  // 起始角度(弧度)
    var endAngleRad = startAngleRad - angle * Math.PI / 180  // 结束角度(弧度)
    
    ctx.beginPath()
    
    if (barStyle === 0) {
        // Donut样式:绘制圆环
        // ...
    } else if (barStyle === 1) {
        // Pie样式:绘制扇形
        // ...
    } else if (barStyle === 2) {
        // Line样式:绘制圆弧
        // ...
    }
}

关键技术点:

  • 角度计算:angle = progress * 360
  • 弧度转换:角度 * Math.PI / 180
  • 注意:Canvas的arc()方法使用弧度,且角度方向为顺时针
2.5 Donut样式实现
if (barStyle === 0) {
    // 外圆弧
    ctx.arc(centerX, centerY, outerRadius, startAngleRad, endAngleRad, true)
    // 内圆弧(反向)
    ctx.arc(centerX, centerY, innerRadius, endAngleRad, startAngleRad, false)
    ctx.closePath()
    ctx.fillStyle = progressColor
    ctx.fill()
}

关键技术点:

  • 使用两个arc()调用绘制圆环
  • 第一个arc():外圆弧,true表示逆时针
  • 第二个arc():内圆弧,false表示顺时针,形成闭合路径
  • closePath():闭合路径,形成圆环
2.6 Pie样式实现
else if (barStyle === 1) {
    // 移动到圆心
    ctx.moveTo(centerX, centerY)
    // 绘制外圆弧
    ctx.arc(centerX, centerY, outerRadius, startAngleRad, endAngleRad, true)
    // 回到圆心
    ctx.lineTo(centerX, centerY)
    ctx.closePath()
    ctx.fillStyle = progressColor
    ctx.fill()
}

关键技术点:

  • moveTo(centerX, centerY):移动到圆心
  • arc():绘制外圆弧
  • lineTo(centerX, centerY):回到圆心,形成扇形
  • closePath():闭合路径
2.7 Line样式实现
else if (barStyle === 2) {
    // 绘制圆弧
    ctx.arc(centerX, centerY, outerRadius - outlineWidth / 2, startAngleRad, endAngleRad, true)
    ctx.strokeStyle = progressColor
    ctx.lineWidth = dataWidth
    ctx.stroke()
}

关键技术点:

  • 使用stroke()描边,而不是fill()填充
  • 半径减去outlineWidth / 2,确保线条居中
  • lineWidth控制线条粗细
2.8 内圆绘制(Donut样式)
// 绘制内圆(Donut样式)
if (barStyle === 0 && innerRadius > 0) {
    ctx.beginPath()
    ctx.arc(centerX, centerY, innerRadius, 0, 2 * Math.PI)
    ctx.fillStyle = "#FFFFFF"  // 白色内圆
    ctx.fill()
}

说明:

  • 只在Donut样式且内圆半径大于0时绘制
  • 内圆通常使用白色,与背景形成对比
2.9 文字绘制
// 绘制文字
if (showText && (barStyle === 0 || barStyle === 1)) {
    var text = textFormat.replace("%p", Math.round(progress * 100))  // 百分比
    text = text.replace("%v", Math.round(value))                      // 值
    text = text.replace("%m", Math.round(maximum))                   // 最大值
    
    ctx.fillStyle = textColor
    ctx.font = (innerRadius * 0.3) + "px Arial"  // 字体大小根据内圆半径计算
    ctx.textAlign = "center"
    ctx.textBaseline = "middle"
    ctx.fillText(text, centerX, centerY)
}

关键技术点:

  • 文字格式支持占位符:%p(百分比)、%v(值)、%m(最大值)
  • 字体大小根据内圆半径自适应
  • textAligntextBaseline设置为居中

3. 响应式更新

组件响应多个属性的变化:

onWidthChanged: {
    if (width > 0 && height > 0) {
        requestPaint()
    }
}
onHeightChanged: {
    if (width > 0 && height > 0) {
        requestPaint()
    }
}
onProgressChanged: {
    if (canvas.width > 0 && canvas.height > 0) {
        canvas.requestPaint()
    }
}
onBarStyleChanged: {
    if (canvas.width > 0 && canvas.height > 0) {
        canvas.requestPaint()
    }
}
onValueChanged: {
    if (canvas.width > 0 && canvas.height > 0) {
        canvas.requestPaint()
    }
}

说明:

  • 只在尺寸有效时触发重绘
  • 响应所有影响显示效果的属性变化

数学原理

角度与弧度转换

Canvas的arc()方法使用弧度,而通常角度使用度:

弧度 = 角度 × π / 180
角度 = 弧度 × 180 / π

圆弧绘制

arc()方法的参数:

ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise)
  • x, y:圆心坐标
  • radius:半径
  • startAngle:起始角度(弧度)
  • endAngle:结束角度(弧度)
  • anticlockwise:是否逆时针(true=逆时针,false=顺时针)

进度角度计算

进度角度 = 进度比例 × 360°
结束角度 = 起始角度 - 进度角度

注意:使用减法是因为通常进度条从顶部(-90°)开始,顺时针方向绘制。


开发要点与技巧

1. 样式切换

通过barStyle属性切换样式:

property int barStyle: 0  // 0=Donut, 1=Pie, 2=Line

在绘制逻辑中使用条件判断:

if (barStyle === 0) {
    // Donut样式逻辑
} else if (barStyle === 1) {
    // Pie样式逻辑
} else if (barStyle === 2) {
    // Line样式逻辑
}

2. 起始角度设置

默认从顶部开始(-90°):

property real startAngle: -90  // 顶部开始

可以自定义起始角度:

RoundProgressBar {
    startAngle: 0    // 从右侧开始
    // 或
    startAngle: 90   // 从底部开始
    // 或
    startAngle: -90  // 从顶部开始(默认)
}

3. 文字格式自定义

支持多种占位符:

property string textFormat: "%p%"  // 默认:显示百分比

// 示例:
textFormat: "%v/%m"     // 显示:50/100
textFormat: "%p%"       // 显示:50%
textFormat: "%v"        // 显示:50

4. 性能优化

条件绘制:

if (progress > 0) {
    // 只在有进度时绘制
}

尺寸检查:

if (width <= 0 || height <= 0) return

避免无效重绘:

if (canvas.width > 0 && canvas.height > 0) {
    canvas.requestPaint()
}

5. 颜色自定义

支持自定义所有颜色:

RoundProgressBar {
    progressColor: "#2B4AFF"    // 进度颜色(蓝色)
    backgroundColor: "#E0E0E0"   // 背景颜色(浅灰)
    textColor: "#333333"         // 文字颜色(深灰)
}

使用示例

基本使用

import QtQuick 2.15

Column {
    spacing: 20
    
    // Donut样式(圆环)
    RoundProgressBar {
        width: 200
        height: 200
        value: 75
        barStyle: 0  // Donut
        progressColor: "#2B4AFF"
    }
    
    // Pie样式(饼图)
    RoundProgressBar {
        width: 200
        height: 200
        value: 50
        barStyle: 1  // Pie
        progressColor: "#FF42D5"
    }
    
    // Line样式(线条)
    RoundProgressBar {
        width: 200
        height: 200
        value: 25
        barStyle: 2  // Line
        progressColor: "#32CD33"
    }
}

动态更新进度

Item {
    property real progressValue: 0
    
    Timer {
        interval: 100
        running: true
        repeat: true
        onTriggered: {
            progressValue += 1
            if (progressValue > 100) progressValue = 0
        }
    }
    
    RoundProgressBar {
        width: 200
        height: 200
        value: progressValue
        barStyle: 0
    }
}

自定义文字格式

RoundProgressBar {
    width: 200
    height: 200
    value: 60
    barStyle: 0
    textFormat: "%v/%m"        // 显示:60/100
    showText: true
    textColor: "#333333"
}

自定义起始角度

RoundProgressBar {
    width: 200
    height: 200
    value: 50
    barStyle: 0
    startAngle: 0  // 从右侧开始
}

总结与展望

技术总结

本项目成功实现了三种样式的圆形进度条,展示了Qt/QML Canvas API的强大能力:

  1. 多样式支持:通过属性控制实现Donut、Pie、Line三种样式
  2. 数学计算:精确的角度和弧度转换,确保绘制准确
  3. 灵活配置:支持自定义颜色、角度、文字格式等
  4. 性能优化:合理的重绘机制,保证流畅运行

扩展方向

  1. 动画效果:可以添加进度变化的动画过渡
  2. 渐变填充:可以使用渐变填充进度部分
  3. 更多样式:可以添加更多样式,如分段式、多层式等
  4. 交互功能:可以添加点击交互,允许用户手动设置进度

最佳实践

  1. 统一接口:所有样式使用统一的属性接口,便于维护
  2. 数学精确:确保角度和弧度转换的准确性
  3. 性能考虑:合理触发重绘,避免过度绘制
  4. 代码复用:通过条件判断实现样式切换,减少代码重复

相关资源

  • 项目地址:https://gitcode.com/szkygc/HarmonyOs_PC-PGC/tree/main/progress
  • Qt官方文档:https://doc.qt.io/qt-5/qtquick-index.html
  • QML Canvas文档:https://doc.qt.io/qt-5/qml-qtquick-canvas.html
  • Canvas arc方法:https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/arc
Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐