Solved 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 } ...