Change NumberAnimation while running



  • I wonder how to change the property of a NumberAnimation (say "loops" or "to") while it's running.
    Of course I tried to use a variable but it has no effect. Example:

    SequentialAnimation {
        id: anim
    
        // change rotation centers
        PropertyAction { target: rotSpin; property: "origin.x"; value: xCenterNeedle }
        PropertyAction { target: rotSpin; property: "origin.y"; value: yCenterNeedle }
    
        NumberAnimation { id: numAnim; target: rotSpin; property: "angle"; easing.type: Easing.Linear; to: _degree; duration: _time; }
    
        onStopped: {_isSpinning = false; }
    }
    

    I start it:

    function startSpin() {
        _degree += 360;
        _time = _timeForSector * 12;
        anim.loops = Animation.Infinite
        _isSpinning = true;
        anim.start()
    }
    

    and now I want to change something:

    function stopSpin()
    {
        if (_isSpinning)
        {
            var angle = rotSpin.angle;  // current angle
            var currSector = Math.floor(angle / 30); // current sector
    
            numAnim.to = (currSector + 1) * 30; // angle of the next sector
            anim.loops = 1;  // stop when reach the requested angle
        }
    }
    

    but nothing happens.
    Where's my error?

    By the way, the specific goal is to stop a spinning needle on a precise angle (x * 30) when requested.



  • I don't know the answer to your more than one rotation... but I do know Rotations are easier with RotationAnimation QML Type - this way you can cross that 0-360 degree boundary without a full opposite wind.

    I have things like:

    onValueChanged:
       RotationAnimation {
           target: root;
           property: "rotation"
           easing.type: Easing.InOutSine;
           to:root.value; 
           direction: RotationAnimator.Shortest;
           duration: 300; 
    


  • Thanks, but I don't need the shortest path.
    I only need to rotate an Image until a signal fires. At that moment, let's say the angle is 53 degrees, I need to stop the animation when it reaches 60 degrees.



  • @Mark81 said in Change NumberAnimation while running:

    NumberAnimation

    Well, I'm not trying to sell you! I just know that that NumberAnimation will not spin but start counting down from the number that is in your angle just before it crosses the threshold.

    Your "rotate an image until a signal fires" - won't work very well when for example it's winding up / clockwise up close to a full rotation - at which point the value crosses and if the intent is keep spinning - instead for that single animation it will unwind counterclockwise, then the next update continuing to the next target clockwise.

    It just looks very wrong - you do what you recon - maybe you know the value is never reaching a full rotation - but "spin" to me indicated you wish to perform one or more rotations.

    I'll leave you to figure out your target angle - that math is basic - or ... maybe you could just use states

    Dunno, just trying to give you leads to get moving again...

    states: State {
                name: "SectorOne"; when: (value >= 0 && value <= 30 )
     // change rotation centers
        PropertyAction { target: rotSpin; property: "origin.x"; value: xCenterNeedle }
        PropertyAction { target: rotSpin; property: "origin.y"; value: yCenterNeedle }
        PropertyAction { target: rotSpin; property: "angle"; value: 30 }
        NumberAnimation { id: numAnim; target: rotSpin; property: "angle"; easing.type: Easing.Linear; to: _degree; duration: _time;
    }
    State {
                name: "SectorTwo"; when: (value > 30 && value <= 60 )
     // change rotation centers
        PropertyAction { target: rotSpin; property: "origin.x"; value: xCenterNeedle }
        PropertyAction { target: rotSpin; property: "origin.y"; value: yCenterNeedle }
        PropertyAction { target: rotSpin; property: "angle"; value: 60 }
        NumberAnimation { id: numAnim; target: rotSpin; property: "angle"; easing.type: Easing.Linear; to: _degree; duration: _time; }
    


  • Thanks a lot for your answer.
    I will try your approach, but I'm not sure I explained the scenario very well. I'm going to try with a simpler example.

    Let's image to have a stopwatch, with a single needle. When the stopwatch start, the needle begins to "spin" forever.
    But when the user decides to stop it (i.e. the stopSpin() event is fired) the needle cannot just stop where it is but it has to reach the very next "hour". I.e. if the event fired when the needle was pointing to 85 degrees, it should stop ONLY when it has reached 90 degrees.

    Right now I ended up with a workaround.
    In the stopSpin() function I stop the animation and in the onStopped event I read the current angle of my needle. Then I start again the same animation setting the new target and duration fields to reach the desired angle.
    It works (as you said, it's a matter of basic maths) but sometimes the needle stops a while before moving again. Instead the animation should be flawless.



  • I think you'd need to interrupt your animation. I'd be looking for a solution where you just update your target and indeed get flawless animation.

    I haven't had to deal with interrupted animations - I have known baud rates and controls that are reactive (not responsive to user input) so it's easy to setup animation speeds to always complete slightly faster than my configured baud rate.

    Maybe you'll find http://doc.qt.io/qt-5/qml-qtquick-smoothedanimation.html is better for you?



  • @6thC said in Change NumberAnimation while running:

    Maybe you'll find http://doc.qt.io/qt-5/qml-qtquick-smoothedanimation.html is better for you?

    I will give it a try, but unfortunately I cannot use any Easing type but the Linear one. Anyway perhaps I might find a way to use it. It's not clear to me what does the maximumEasingTime property mean. I'm going to play a little with it.



  • I have wanted to do similar things before but eventually decided I was expecting too much from the QML Animation types suddenly trying to get them to something else part way through an animation. I decided to just take more control instead... here's an example minute-hand clock (runs in Qt 5.9.1's qmlscene); click the window to start and stop the clock movement:

    import QtQuick 2.7
    
    Rectangle {
      id: main
      width: 512
      height: 512
    
      Rectangle {
        x: 0.5*parent.width
        y: 0.5*parent.height
        width: 10
        height: 200
        radius: 5
        color: 'red'
        transformOrigin: Item.Top
        rotation: (((control.spinning ? clock.time : control.stopTime)-control.lostTime)/60.0)%360.0   
      }
    
      Timer {
        id: clock
        interval: 1000.0/60.0
        repeat: true
        running: true
        triggeredOnStart: true
        onTriggered: time=(new Date()).getTime()
        property real time
      }
    
      MouseArea {
        id: control
        anchors.fill: parent
        onClicked: {
          if (spinning) {
            stopTime=clock.time
            spinning=false
          } else {
            startTime=clock.time
            lostTime+=(startTime-stopTime)
            spinning=true
          }
        }
        property bool spinning: true
        property real stopTime: 0.0
        property real startTime: 0.0
        property real lostTime: 0.0
      }
    }
    

    I was actually a little paranoid about the resolution of the QML/javascript-world timers so in an actual app I replace that QML Timer with a QTimer with setTimerType(Qt::PreciseTimer) in the hosting C++ QQuickView. Not sure it makes any difference though.



  • Thanks for the hint. Here the changes I've made to allow stopping on defined position:

    rotation: {
            if (control.spinning || clock.time < control.stopTime)
            {
                return ((clock.time - control.lostTime) / 60.0) % 360.0;
            }
            else
            {
                return (( control.stopTime - control.lostTime) / 60.0) % 360.0;
            }
        }
    

    and in the stop event:

    if (spinning)  {
            stopTime=clock.time + 500 // set the stop position
            spinning=false
    }
    ...
    

Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.