TheWaterProgrammer
TheWaterProgrammer

Reputation: 8229

How to make a QML animation work only when a property value is increasing not decreasing?

From Qt official documentation here I understand that I can animate the the change x coordinate of a QML item.

Following works but the issue is that I want the animation to be not activate when value of x is decrementing:

Rectangle {
    id: some_item
    x: somePropertyValueThatKeepsChanging

    Behavior on x {
        NumberAnimation {
            duration: 50
        }
    }
}

Question:
How do I make the animation Behavior on x execute only when x is increasing, not when it is decreasing?

I am using Qt commercial version 5.15.1.

Upvotes: 0

Views: 1054

Answers (2)

GrecKo
GrecKo

Reputation: 7150

This is possible by using the targetValue (added in 5.12) and the targetProperty (added in 5.15) of Behavior:

Behavior on x {
    enabled: targetValue > targetProperty.object[targetProperty.name]
    NumberAnimation { duration: 200 }
}

(Note than you can just do targetValue > some_item.x instead of using targetProperty, but targetProperty lets you define generic reusable Behaviors)

Small example:

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    Rectangle {
        id: some_item
        x: 0
        y: 100
        height: 100
        width: 100
        color: "salmon"
        Behavior on x {
            enabled: targetValue > targetProperty.object[targetProperty.name]
            NumberAnimation { duration: 200 }
        }
    }

    Row {
        Button {
            text: "increase"
            onClicked: some_item.x += 10
        }
        Button {
            text: "decrease"
            onClicked: some_item.x -= 10
        }
    }
}

Upvotes: 2

fallerd
fallerd

Reputation: 1728

This is possible, but I don't think this is a built-in feature of QML behaviors/animations, so it needs a little bit of custom implementation.

Rather than bind x directly to the changing value, use an intermediate listener to determine if the new value is greater or less than the current x value, and set the enabled flag on the Behavior prior to setting x. Here is a complete working example:

import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12

Window {
    id: root
    width: 640
    height: 480
    visible: true

    property real somePropertyThatChanges // simulated externally changing value using timer below

    Timer {
        interval: 500
        running: true
        repeat: true
        onTriggered: {
            somePropertyThatChanges = Math.round(Math.random()* rect.parent.height) // simulate value changes every 500ms
        }
    }

    Rectangle {
        id: rect
        color: "lightgrey"
        width: 50
        height: 50 

        property real intermediateValueChecker: root.somePropertyThatChanges

        onIntermediateValueCheckerChanged: {
            if (root.somePropertyThatChanges > rect.x) { // check if new value is greater or less than current x, set behavior.enabled accordingly
                xBehavior.enabled = true
            } else {
                xBehavior.enabled = false
            }
            rect.x = somePropertyThatChanges // set x after the animation is enabled or disabled
        }

        Behavior on x {
            id: xBehavior
            NumberAnimation {
                duration: 500
            }
        }
    }
}

If x is going to be set to a greater value than previously, it will enable the animation, otherwise disable it.

Other note on alternative implementation: If you don't want to use a new property real for this intermediate listener, you could bind a Connections object directly to somePropertyThatChanges instead. I didn't include this because I believe they changed the syntax of these between my Qt version (5.12) and yours, but for me it would look like:

        Connections {
            target: root
            onSomePropertyThatChangesChanged: {  // in QT 5.15 this may need to instead be: function onSomePropertyThatChangesChanged() {
                if (root.somePropertyThatChanges > rect.x) { // same logic
                    xBehavior.enabled = true
                } else {
                    xBehavior.enabled = false
                }
                rect.x = somePropertyThatChanges
            }
        }

Upvotes: 1

Related Questions