make circular progresses use shape instead of canvas

This commit is contained in:
end-4 2025-08-03 18:12:44 +07:00
parent 839593b11e
commit 71d0ac4c5e

View file

@ -2,6 +2,7 @@
// License: LGPL-3.0 - A copy can be found in `licenses` folder of repo
import QtQuick
import QtQuick.Shapes
import qs.modules.common
/**
@ -15,7 +16,7 @@ Item {
property real value: 0
property color primaryColor: Appearance.m3colors.m3onSecondaryContainer
property color secondaryColor: Appearance.colors.colSecondaryContainer
property real gapAngle: Math.PI / 9
property real gapAngle: 180 / 9
property bool fill: false
property int fillOverflow: 2
property bool enableAnimation: true
@ -25,73 +26,66 @@ Item {
width: size
height: size
signal animationFinished();
property real degree: value * 360
property real centerX: root.width / 2
property real centerY: root.height / 2
property real arcRadius: root.size / 2 - root.lineWidth
property real startAngle: -90
Behavior on degree {
enabled: root.enableAnimation
NumberAnimation {
duration: root.animationDuration
easing.type: root.easingType
}
onValueChanged: {
canvas.degree = value * 360;
}
onPrimaryColorChanged: {
canvas.requestPaint();
}
onSecondaryColorChanged: {
canvas.requestPaint();
}
Canvas {
id: canvas
property real degree: 0
Loader {
active: root.fill
anchors.fill: parent
antialiasing: true
onDegreeChanged: {
requestPaint();
sourceComponent: Rectangle {
radius: 9999
color: root.secondaryColor
}
}
onPaint: {
var ctx = getContext("2d");
var x = root.width / 2;
var y = root.height / 2;
var radius = root.size / 2 - root.lineWidth;
var startAngle = (Math.PI / 180) * 270;
var fullAngle = (Math.PI / 180) * (270 + 360);
var progressAngle = (Math.PI / 180) * (270 + degree);
var epsilon = 0.01; // Small angle in radians
ctx.reset();
if (root.fill) {
ctx.fillStyle = root.secondaryColor;
ctx.beginPath();
ctx.arc(x, y, radius + fillOverflow, startAngle, fullAngle);
ctx.fill();
Shape {
anchors.fill: parent
layer.enabled: true
layer.smooth: true
preferredRendererType: Shape.CurveRenderer
ShapePath {
id: secondaryPath
strokeColor: root.secondaryColor
strokeWidth: root.lineWidth
capStyle: ShapePath.RoundCap
fillColor: "transparent"
PathAngleArc {
centerX: root.centerX
centerY: root.centerY
radiusX: root.arcRadius
radiusY: root.arcRadius
startAngle: root.startAngle - root.gapAngle
sweepAngle: -(360 - root.degree - 2 * root.gapAngle)
}
ctx.lineCap = 'round';
ctx.lineWidth = root.lineWidth;
// Secondary
ctx.beginPath();
ctx.arc(x, y, radius, progressAngle + gapAngle, fullAngle - gapAngle);
ctx.strokeStyle = root.secondaryColor;
ctx.stroke();
// Primary (value indication)
var endAngle = progressAngle + (value > 0 ? 0 : epsilon);
ctx.beginPath();
ctx.arc(x, y, radius, startAngle, endAngle);
ctx.strokeStyle = root.primaryColor;
ctx.stroke();
}
Behavior on degree {
enabled: root.enableAnimation
NumberAnimation {
duration: root.animationDuration
easing.type: root.easingType
ShapePath {
id: primaryPath
strokeColor: root.primaryColor
strokeWidth: root.lineWidth
capStyle: ShapePath.RoundCap
fillColor: "transparent"
PathAngleArc {
centerX: root.centerX
centerY: root.centerY
radiusX: root.arcRadius
radiusY: root.arcRadius
startAngle: root.startAngle
sweepAngle: root.degree
}
}
}
}