javascript 如何在纯 QML+JS 中创建圆形进度条?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22873550/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 23:56:13  来源:igfitidea点击:

How to create a circular progress bar in pure QML+JS?

javascriptqmlubuntu-touch

提问by Nik

My application is made using QML+JS and I am looking to create a circular progress bar widget. I can create the circle using a QML Rectangle and settings its radius equal to its width/2 to make it into a circle. How do I create a progress bar out of it?

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

I am planning to implement the following mockup.

我计划实施以下模型。

enter image description here

在此处输入图片说明

回答by Charles

I've implemented a basic circular progress using a Canvas.

我已经使用 Canvas 实现了一个基本的循环进度。

enter image description here

在此处输入图片说明

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()
    }
}

回答by warranty_void

I found a kinda elegant solution in plain QML which can be also used for styling a regular QtQuick ProgressBar component. The idea behind this is to use a ConicalGradienton a border-only Rectangle.

我在普通 QML 中找到了一种优雅的解决方案,它也可用于为常规 QtQuick ProgressBar 组件设置样式。这背后的想法是ConicalGradient在仅边框上使用 a Rectangle

Here is the code:

这是代码:

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() + "%"
      }
   }
}

enter image description here

在此处输入图片说明

回答by andrewsomething

I came across an example by Diego Dotta on GitHubusing two rotating circles that seems to work nicely for this use case. It involves setting the duration of a PropertyAnimation. So while this works well for a timer that you can set, it would need a different approach for something you didn't know how long it would take. This is tweaked a bit and ported to QtQuick 2.0:

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

main.qml:

主.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:

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; }
    }
}

example image

示例图像

回答by Vova Shevchyk

Just use EEIoT (https://github.com/IndeemaSoftware/EEIoT) Knob component. Change parameters fromAngle: 0 and toAngle: Math.PI * 2. Also reverse: true if you need progress to be reversed

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

enter image description here

在此处输入图片说明

回答by DrIDK

The best way would be to use PNG file image. Because it runs faster than pure qml, particulary if you use gradient. If you want pure qml only, I didn't find anyway except if you add a custom C++ module to youproject. See http://qt-project.org/doc/qt-4.8/qml-extending.html

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