[英]How to create a circular progress bar in pure QML+JS?
我的應用程序是使用 QML+JS 制作的,我希望創建一個圓形進度條小部件。 我可以使用 QML 矩形創建圓,並將其半徑設置為寬度/2,使其成為一個圓。 如何從中創建進度條?
我計划實現以下模型。
我已經使用 Canvas 實現了一個基本的循環進度。
import QtQml 2.2
import QtQuick 2.0
// draws two arcs (portion of a circle)
// fills the circle with a lighter secondary color
// when pressed
Canvas {
id: canvas
width: 240
height: 240
antialiasing: true
property color primaryColor: "orange"
property color secondaryColor: "lightblue"
property real centerWidth: width / 2
property real centerHeight: height / 2
property real radius: Math.min(canvas.width, canvas.height) / 2
property real minimumValue: 0
property real maximumValue: 100
property real currentValue: 33
// this is the angle that splits the circle in two arcs
// first arc is drawn from 0 radians to angle radians
// second arc is angle radians to 2*PI radians
property real angle: (currentValue - minimumValue) / (maximumValue - minimumValue) * 2 * Math.PI
// we want both circle to start / end at 12 o'clock
// without this offset we would start / end at 9 o'clock
property real angleOffset: -Math.PI / 2
property string text: "Text"
signal clicked()
onPrimaryColorChanged: requestPaint()
onSecondaryColorChanged: requestPaint()
onMinimumValueChanged: requestPaint()
onMaximumValueChanged: requestPaint()
onCurrentValueChanged: requestPaint()
onPaint: {
var ctx = getContext("2d");
ctx.save();
ctx.clearRect(0, 0, canvas.width, canvas.height);
// fills the mouse area when pressed
// the fill color is a lighter version of the
// secondary color
if (mouseArea.pressed) {
ctx.beginPath();
ctx.lineWidth = 1;
ctx.fillStyle = Qt.lighter(canvas.secondaryColor, 1.25);
ctx.arc(canvas.centerWidth,
canvas.centerHeight,
canvas.radius,
0,
2*Math.PI);
ctx.fill();
}
// First, thinner arc
// From angle to 2*PI
ctx.beginPath();
ctx.lineWidth = 1;
ctx.strokeStyle = primaryColor;
ctx.arc(canvas.centerWidth,
canvas.centerHeight,
canvas.radius,
angleOffset + canvas.angle,
angleOffset + 2*Math.PI);
ctx.stroke();
// Second, thicker arc
// From 0 to angle
ctx.beginPath();
ctx.lineWidth = 3;
ctx.strokeStyle = canvas.secondaryColor;
ctx.arc(canvas.centerWidth,
canvas.centerHeight,
canvas.radius,
canvas.angleOffset,
canvas.angleOffset + canvas.angle);
ctx.stroke();
ctx.restore();
}
Text {
anchors.centerIn: parent
text: canvas.text
color: canvas.primaryColor
}
MouseArea {
id: mouseArea
anchors.fill: parent
onClicked: canvas.clicked()
onPressedChanged: canvas.requestPaint()
}
}
我在普通 QML 中找到了一種優雅的解決方案,它也可用於為常規 QtQuick ProgressBar 組件設置樣式。 這背后的想法是在僅邊框的Rectangle
上使用ConicalGradient
。
這是代碼:
import QtQuick 2.3
import QtQuick.Controls.Styles 1.2
import QtGraphicalEffects 1.0
ProgressBarStyle
{
panel : Rectangle
{
color: "transparent"
implicitWidth: 80
implicitHeight: implicitWidth
Rectangle
{
id: outerRing
z: 0
anchors.fill: parent
radius: Math.max(width, height) / 2
color: "transparent"
border.color: "gray"
order.width: 8
}
Rectangle
{
id: innerRing
z: 1
anchors.fill: parent
anchors.margins: (outerRing.border.width - border.width) / 2
radius: outerRing.radius
color: "transparent"
border.color: "darkgray"
border.width: 4
ConicalGradient
{
source: innerRing
anchors.fill: parent
gradient: Gradient
{
GradientStop { position: 0.00; color: "white" }
GradientStop { position: control.value; color: "white" }
GradientStop { position: control.value + 0.01; color: "transparent" }
GradientStop { position: 1.00; color: "transparent" }
}
}
}
Text
{
id: progressLabel
anchors.centerIn: parent
color: "black"
text: (control.value * 100).toFixed() + "%"
}
}
}
我在 GitHub 上遇到了 Diego Dotta 的一個例子,它使用了兩個旋轉圓,似乎很適合這個用例。 它涉及設置 PropertyAnimation 的持續時間。 因此,雖然這對於您可以設置的計時器很有效,但對於您不知道需要多長時間的事情,它需要一種不同的方法。 這被稍微調整並移植到 QtQuick 2.0:
主.qml :
import QtQuick 2.0
import Ubuntu.Components 0.1
Rectangle {
width: units.gu(50)
height: units.gu(50)
property int seconds : 0
LoadCircle {
id: circle
anchors.centerIn: parent
loadtimer: 10*1000 // 10 seconds
Component.onCompleted: start();
onFinishedChanged: {
timer.stop();
borderColor = "green"
}
}
Rectangle {
id : theTimer
anchors.centerIn: parent
width : units.gu(10) ; height: units.gu(10)
Label {
text: seconds
font.bold: true
fontSize: "x-large"
anchors.centerIn: parent
}
}
Timer {
id: timer
interval: 1000; running: true; repeat: true;
onTriggered: seconds++;
}
}
LoadCircle.qml :
import QtQuick 2.0
import Ubuntu.Components 0.1
Row{
id: circle
property int loadtimer: 4000
property color circleColor: "transparent"
property color borderColor: "red"
property int borderWidth: 10
property alias running: initCircle.running
property bool finished: false;
width: units.gu(30)
height: width
function start(){
part1.rotation = 180
part2.rotation = 180
initCircle.start()
}
function stop(){
initCircle.stop()
}
Item{
width: parent.width/2
height: parent.height
clip: true
Item{
id: part1
width: parent.width
height: parent.height
clip: true
rotation: 180
transformOrigin: Item.Right
Rectangle{
width: circle.width-(borderWidth*2)
height: circle.height-(borderWidth*2)
radius: width/2
x:borderWidth
y:borderWidth
color: circleColor
border.color: borderColor
border.width: borderWidth
smooth: true
}
}
}
Item{
width: parent.width/2
height: parent.height
clip: true
Item{
id: part2
width: parent.width
height: parent.height
clip: true
rotation: 180
transformOrigin: Item.Left
Rectangle{
width: circle.width-(borderWidth*2)
height: circle.height-(borderWidth*2)
radius: width/2
x: -width/2
y: borderWidth
color: circleColor
border.color: borderColor
border.width: borderWidth
smooth: true
}
}
}
SequentialAnimation{
id: initCircle
PropertyAnimation{ target: part2; property: "rotation"; to:360; duration:loadtimer/2 }
PropertyAnimation{ target: part1; property: "rotation"; to:360; duration:loadtimer/2 }
ScriptAction { script: finished = true; }
}
}
我知道使用rotation
屬性的解決方案。 查看示例
https://gitorious.org/apps-4-me/staq-me/source/fd20fe5b6fec053f364219842905e2afc5cfdc9d:ui.qml#L172
只需使用 EEIoT ( https://github.com/IndeemaSoftware/EEIoT ) 旋鈕組件。 更改參數 fromAngle: 0 和 toAngle: Math.PI * 2。如果需要反轉進度,也可以反轉:true
Knob {
id: knob
x: 0
y: 83
width: 100
height: 100
from:0
to: 100
fromAngle: 0
toAngle: Math.PI*2
reverse: false
}
我嘗試了 Canvas 就像接受的答案所建議的那樣,但我發現它很慢。 在我的例子中,我需要指示器來顯示用戶在 state 轉換發生之前需要按住鼠標多長時間,如果指示器落后於它是有問題的,因為用戶認為他們有更多時間但實際上他們沒有。
我發現一個更快的解決方案是使用 Shape。
import QtQuick 2.15
import QtQuick.Shapes 1.15
Shape {
id: root
property real radius: 18
property alias strokeWidth: path.strokeWidth
// value between 0 and 1
property real progress: .75
// don't set these externally. Set radius instead
width: radius * 2
height: width
// antialiasing
layer.enabled: true
layer.samples: 8
ShapePath {
id: path
fillColor: "transparent"
strokeColor: "#77999999"
strokeWidth: 3
startX: radius
startY: strokeWidth/2
PathArc {
x: radiusX * Math.sin(Math.PI * 2 * progress) + radius
y: -radiusY * Math.cos(Math.PI * 2 * progress) + radius
radiusX: radius - strokeWidth/2
radiusY: radius - strokeWidth/2
useLargeArc: x < radius
}
}
}
最好的方法是使用 PNG 文件圖像。 因為它比純 qml 運行得更快,特別是如果你使用梯度。 如果您只想要純 qml,除非您向您的項目添加自定義 C++ 模塊,否則我沒有找到。 見http://qt-project.org/doc/qt-4.8/qml-extending.html
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.