C

Qt Quick Ultralite Watch Demo

// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial

import QtQuick 2.15
import Watch 1.0

Rectangle {
    id: root
    height: Theme.appHeight
    width: Theme.appWidth
    color: Theme.backgroundColor

    readonly property real startOpacity: 0.0
    readonly property int introAnimationDuration: 100
    readonly property int degreeBaseDuration: 3000
    readonly property int degreeUpdateInterval: 4000
    readonly property int offset: 180
    readonly property int directionsCount: 4
    readonly property int arrowsCount: 2
    readonly property int minDegree: 1
    readonly property int maxDegree: 360
    readonly property real radianRatio: 0.0174532925

    property int currentDegree: 360

    onVisibleChanged: {
        introAnimation.running = visible

        if (!visible) {
            resetOpacity()
            degreeTimer.running = false
        }
    }

    Behavior on currentDegree {
        NumberAnimation {
            id: degreeAnimation
            easing.type: Easing.InOutCubic;
        }
    }

    SequentialAnimation {
        id: introAnimation
        running: root.visible

        NumberAnimation { target: outerRing; property: "opacity"; to: 1.0; duration: introAnimationDuration }
        NumberAnimation { target: innerRing; property: "opacity"; to: 1.0; duration: introAnimationDuration }
        NumberAnimation { target: centerRing; property: "opacity"; to: 1.0; duration: introAnimationDuration }
        NumberAnimation { target: degreeText; property: "opacity"; to: 1.0; duration: introAnimationDuration }
        NumberAnimation { target: arrows; property: "opacity"; to: 1.0; duration: introAnimationDuration }
        NumberAnimation { target: directions; property: "nOpacity"; to: 1.0; duration: introAnimationDuration }
        NumberAnimation { target: directions; property: "eOpacity"; to: 1.0; duration: introAnimationDuration }
        NumberAnimation { target: directions; property: "sOpacity"; to: 1.0; duration: introAnimationDuration }
        NumberAnimation { target: directions; property: "wOpacity"; to: 1.0; duration: introAnimationDuration }
        ScriptAction {
            script: {
                updateAngle()
                degreeTimer.running = true
            }
        }
    }

    Timer {
        id: degreeTimer
        interval: degreeUpdateInterval
        running: false
        repeat: true
        onTriggered: updateAngle()
    }

    MouseArea {
        anchors.fill: parent
        onClicked: MainModel.compassOn = false
    }

    Image {
        id: centerRing
        anchors.centerIn: parent
        source: "images/compass/middleback.png"
        opacity: startOpacity
    }

    Image {
        id: innerRing
        anchors.centerIn: parent
        source: "images/compass/ring1.png"
        opacity: startOpacity
    }

    Image {
        id: outerRing
        anchors.centerIn: parent
        source: "images/compass/ring2.png"
        opacity: startOpacity
    }

    Item {

        anchors.centerIn: parent
        id: directions

        property real nOpacity: startOpacity
        property real sOpacity: startOpacity
        property real wOpacity: startOpacity
        property real eOpacity: startOpacity

        Repeater {

            model: ListModel {
                ListElement { image: "images/compass/N.png" }
                ListElement { image: "images/compass/W.png" }
                ListElement { image: "images/compass/S.png" }
                ListElement { image: "images/compass/E.png" }
            }

            delegate: Image {
                source: model.image
                opacity: getOpacity(index)

                property real angle: ((360 / directionsCount) * index + offset - currentDegree) * radianRatio
                property real radius: root.height / 2 - height / 2

                x: radius * Math.sin(angle) - width / 2
                y: radius * Math.cos(angle) - height / 2
            }
        }
    }

    Image {
        id: arrows
        anchors.centerIn: parent
        source: "images/compass/arrows.png"
        opacity: startOpacity

        transform: Rotation {
            angle: currentDegree
            origin.x: arrows.width / 2
            origin.y: arrows.height / 2
        }
    }

    Text {
        anchors.centerIn: parent
        id: degreeText
        text: currentDegree
        font.pixelSize: 86
        font.family: Theme.fontFamily
        color: Theme.whiteColor
        opacity: startOpacity
    }

    function resetOpacity() {
        arrows.opacity = startOpacity
        outerRing.opacity = startOpacity
        innerRing.opacity = startOpacity
        degreeText.opacity = startOpacity
        centerRing.opacity = startOpacity
        degreeText.opacity = startOpacity
        directions.nOpacity = startOpacity
        directions.eOpacity = startOpacity
        directions.sOpacity = startOpacity
        directions.wOpacity = startOpacity
    }

    function getAnimationDuration(oldValue : int, newValue : int) : int {
        var delta = oldValue - newValue
        return Math.abs(delta) / 100 * degreeBaseDuration
    }

    function getOpacity(index : int) : real {
        switch(index) {
            case 0:
                return directions.nOpacity
            case 1:
                return directions.wOpacity
            case 2:
                return directions.sOpacity
            case 3:
                return directions.eOpacity
        }
    }

    function updateAngle() {
        var newDegree = currentDegree - 180
        if (newDegree < 0) {
            newDegree = 360
        }

        degreeAnimation.duration = getAnimationDuration(currentDegree, newDegree)
        currentDegree = newDegree
    }
}