【问题标题】:How to create a circular progress bar in pure QML+JS?如何在纯 QML+JS 中创建圆形进度条?
【发布时间】:2014-05-17 09:38:33
【问题描述】:

我的应用程序是使用 QML+JS 制作的,我希望创建一个圆形进度条小部件。我可以使用 QML Rectangle 创建圆形并将其半径设置为等于其宽度/2 以使其成为圆形。如何从中创建进度条?

我计划实现以下模型。

【问题讨论】:

  • 帆布可能吗?在为 Ubuntu Stock Ticker 应用程序开发一些 UI 内容时,我稍微摆弄了一下,我认为它可能适合你想要的。
  • @iBelieve 是的,我意识到了这一点。希望有人准备好一些带有圆形进度条的代码,因为我以前使用过 Canvas,所以这让我更难实现。

标签: javascript qt qml ubuntu-touch


【解决方案1】:

只需使用 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
}

【讨论】:

    【解决方案2】:

    我在普通 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() + "%"
          }
       }
    }
    

    【讨论】:

    • 好主意和更好的整体外观!
    • 它为我画了一个正方形!
    • @George:如果它为您显示一个正方形,那么可能存在与此行相关的问题:“radius: Math.max(width, height) / 2”。如果你在那里使用绝对半径值,它会起作用吗?
    • 好主意,只是提到它可以通过用默认颜色的圆形图像替换内圈和外圈来进一步简化。您可以将source 设置为ConicalGradient 的图像。
    【解决方案3】:

    我已经使用 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()
        }
    }
    

    【讨论】:

    • 嘿,谢谢。这是我几天前在网上找到教程后最终实现的。您介意在您的代码中添加一些 cmets,尤其是在 onPaint() 函数中,以使每个人都清楚吗?然后我可以接受你的回答。提前致谢。
    • 完成。改变适合你吗?
    • 四个边都被稍微截断了..如何做一个完美的圆
    • @GeorgeThomas 这是因为半径大小太大了。如果您以Math.min(canvas.width, canvas.height)/4 为例,它将非常适合(但会小于它填充的整个父组件)。只需根据其父级的大小调整并使其更小(还要考虑线条的宽度)。
    【解决方案4】:

    我遇到了an example by Diego Dotta on GitHub,它使用了两个旋转的圆圈,似乎很适合这个用例。它涉及设置 PropertyAnimation 的持续时间。因此,虽然这对于您可以设置的计时器很有效,但对于您不知道需要多长时间的事情,它需要一种不同的方法。稍作调整并移植到 QtQuick 2.0:

    ma​​in.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; }
        }
    }
    

    【讨论】:

    • 感谢您的解决方案。我找到了一种使用 QML Canvas 实现这一目标的更简单的方法。但是我仍然可以记住这个解决方案,以防我无法在 canvas 方法中实现某些东西。
    • 我怎样才能导入 Ubuntu.Components 0.1
    【解决方案5】:

    【讨论】:

    • 嗯,代码作者使用60个矩形(圆形)来模拟进度。我不确定这会出现多顺利。我会尝试一下并在这里评论它。
    【解决方案6】:

    最好的方法是使用 PNG 文件图像。因为它比纯 qml 运行得更快,特别是如果你使用渐变。如果您只想要纯 qml,除非您将自定义 C++ 模块添加到您的项目中,否则我没有找到。见http://qt-project.org/doc/qt-4.8/qml-extending.html

    【讨论】:

    • 如何处理图片
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-06-19
    • 1970-01-01
    • 2022-01-17
    • 2021-04-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多