1
/****************************************************************************
3
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
4
** Contact: http://www.qt-project.org/legal
6
** This file is part of the QtQuick module of the Qt Toolkit.
8
** $QT_BEGIN_LICENSE:LGPL$
9
** Commercial License Usage
10
** Licensees holding valid commercial Qt licenses may use this file in
11
** accordance with the commercial license agreement provided with the
12
** Software or, alternatively, in accordance with the terms contained in
13
** a written agreement between you and Digia. For licensing terms and
14
** conditions see http://qt.digia.com/licensing. For further information
15
** use the contact form at http://qt.digia.com/contact-us.
17
** GNU Lesser General Public License Usage
18
** Alternatively, this file may be used under the terms of the GNU Lesser
19
** General Public License version 2.1 as published by the Free Software
20
** Foundation and appearing in the file LICENSE.LGPL included in the
21
** packaging of this file. Please review the following information to
22
** ensure the GNU Lesser General Public License version 2.1 requirements
23
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25
** In addition, as a special exception, Digia gives you certain additional
26
** rights. These rights are described in the Digia Qt LGPL Exception
27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29
** GNU General Public License Usage
30
** Alternatively, this file may be used under the terms of the GNU
31
** General Public License version 3.0 as published by the Free Software
32
** Foundation and appearing in the file LICENSE.GPL included in the
33
** packaging of this file. Please review the following information to
34
** ensure the GNU General Public License version 3.0 requirements will be
35
** met: http://www.gnu.org/copyleft/gpl.html.
40
****************************************************************************/
42
#include "qquicksmoothedanimation_p.h"
43
#include "qquicksmoothedanimation_p_p.h"
45
#include "qquickanimation_p_p.h"
47
#include <qqmlproperty.h>
48
#include <private/qqmlproperty_p.h>
50
#include <private/qqmlglobal_p.h>
52
#include <QtCore/qdebug.h>
56
#define DELAY_STOP_TIMER_INTERVAL 32
61
QSmoothedAnimationTimer::QSmoothedAnimationTimer(QSmoothedAnimation *animation, QObject *parent)
63
, m_animation(animation)
65
connect(this, SIGNAL(timeout()), this, SLOT(stopAnimation()));
68
QSmoothedAnimationTimer::~QSmoothedAnimationTimer()
72
void QSmoothedAnimationTimer::stopAnimation()
77
QSmoothedAnimation::QSmoothedAnimation(QQuickSmoothedAnimationPrivate *priv)
78
: QAbstractAnimationJob(), to(0), velocity(200), userDuration(-1), maximumEasingTime(-1),
79
reversingMode(QQuickSmoothedAnimation::Eased), initialVelocity(0),
80
trackVelocity(0), initialValue(0), invert(false), finalDuration(-1), lastTime(0),
81
useDelta(false), delayedStopTimer(new QSmoothedAnimationTimer(this)), animationTemplate(priv)
83
delayedStopTimer->setInterval(DELAY_STOP_TIMER_INTERVAL);
84
delayedStopTimer->setSingleShot(true);
87
QSmoothedAnimation::~QSmoothedAnimation()
89
delete delayedStopTimer;
90
if (animationTemplate) {
91
if (target.object()) {
92
QHash<QQmlProperty, QSmoothedAnimation* >::iterator it =
93
animationTemplate->activeAnimations.find(target);
94
if (it != animationTemplate->activeAnimations.end() && it.value() == this)
95
animationTemplate->activeAnimations.erase(it);
97
//target is no longer valid, need to search linearly
98
QHash<QQmlProperty, QSmoothedAnimation* >::iterator it;
99
for (it = animationTemplate->activeAnimations.begin(); it != animationTemplate->activeAnimations.end(); ++it) {
100
if (it.value() == this) {
101
animationTemplate->activeAnimations.erase(it);
109
void QSmoothedAnimation::restart()
111
initialVelocity = trackVelocity;
118
void QSmoothedAnimation::prepareForRestart()
120
initialVelocity = trackVelocity;
122
//we are joining a new wrapper group while running, our times need to be restarted
128
//we'll be started when the group starts, which will force an init()
132
void QSmoothedAnimation::updateState(QAbstractAnimationJob::State newState, QAbstractAnimationJob::State /*oldState*/)
134
if (newState == QAbstractAnimationJob::Running)
138
void QSmoothedAnimation::delayedStop()
140
if (!delayedStopTimer->isActive())
141
delayedStopTimer->start();
144
int QSmoothedAnimation::duration() const
149
bool QSmoothedAnimation::recalc()
151
s = to - initialValue;
152
vi = initialVelocity;
154
s = (invert? -1.0: 1.0) * s;
156
if (userDuration >= 0 && velocity > 0) {
158
if (tf > (userDuration / 1000.)) tf = (userDuration / 1000.);
159
} else if (userDuration >= 0) {
160
tf = userDuration / 1000.;
161
} else if (velocity > 0) {
167
finalDuration = ceil(tf * 1000.0);
169
if (maximumEasingTime == 0) {
177
} else if (maximumEasingTime != -1 && tf > (maximumEasingTime / 1000.)) {
178
qreal met = maximumEasingTime / 1000.;
188
a = (s - (vi * tf - 0.5 * vi * ta)) / (tf * ta - ta * ta);
193
sp = vi * ta + 0.5 * a * tp * tp;
194
sd = sp + vp * (tf - 2 * ta);
197
qreal c1 = 0.25 * tf * tf;
198
qreal c2 = 0.5 * vi * tf - s;
199
qreal c3 = -0.25 * vi * vi;
201
qreal a1 = (-c2 + sqrt(c2 * c2 - 4 * c1 * c3)) / (2. * c1);
203
qreal tp1 = 0.5 * tf - 0.5 * vi / a1;
204
qreal vp1 = a1 * tp1 + vi;
206
qreal sp1 = 0.5 * a1 * tp1 * tp1 + vi * tp1;
219
qreal QSmoothedAnimation::easeFollow(qreal time_seconds)
222
if (time_seconds < tp) {
223
trackVelocity = vi + time_seconds * a;
224
value = 0.5 * a * time_seconds * time_seconds + vi * time_seconds;
225
} else if (time_seconds < td) {
228
value = sp + time_seconds * vp;
229
} else if (time_seconds < tf) {
231
trackVelocity = vp - time_seconds * a;
232
value = sd - 0.5 * d * time_seconds * time_seconds + vp * time_seconds;
239
// to normalize 's' between [0..1], divide 'value' by 's'
243
void QSmoothedAnimation::updateCurrentTime(int t)
245
if (!isRunning() && !isPaused()) // This can happen if init() stops the animation in some cases
248
qreal time_seconds = useDelta ? qreal(QQmlAnimationTimer::instance()->currentDelta()) / 1000. : qreal(t - lastTime) / 1000.;
252
qreal value = easeFollow(time_seconds);
253
value *= (invert? -1.0: 1.0);
254
QQmlPropertyPrivate::write(target, initialValue + value,
255
QQmlPropertyPrivate::BypassInterceptor
256
| QQmlPropertyPrivate::DontRemoveBinding);
259
void QSmoothedAnimation::init()
266
if (delayedStopTimer->isActive())
267
delayedStopTimer->stop();
269
initialValue = target.read().toReal();
270
lastTime = this->currentTime();
272
if (to == initialValue) {
277
bool hasReversed = trackVelocity != 0. &&
278
((!invert) == ((initialValue - to) > 0));
281
switch (reversingMode) {
283
case QQuickSmoothedAnimation::Eased:
284
initialVelocity = -trackVelocity;
286
case QQuickSmoothedAnimation::Sync:
287
QQmlPropertyPrivate::write(target, to,
288
QQmlPropertyPrivate::BypassInterceptor
289
| QQmlPropertyPrivate::DontRemoveBinding);
293
case QQuickSmoothedAnimation::Immediate:
299
trackVelocity = initialVelocity;
301
invert = (to < initialValue);
304
QQmlPropertyPrivate::write(target, to,
305
QQmlPropertyPrivate::BypassInterceptor
306
| QQmlPropertyPrivate::DontRemoveBinding);
313
\qmltype SmoothedAnimation
314
\instantiates QQuickSmoothedAnimation
316
\ingroup qtquick-transitions-animations
317
\inherits NumberAnimation
318
\brief Allows a property to smoothly track a value
320
A SmoothedAnimation animates a property's value to a set target value
321
using an ease in/out quad easing curve. When the target value changes,
322
the easing curves used to animate between the old and new target values
323
are smoothly spliced together to create a smooth movement to the new
324
target value that maintains the current velocity.
326
The follow example shows one \l Rectangle tracking the position of another
327
using SmoothedAnimation. The green rectangle's \c x and \c y values are
328
bound to those of the red rectangle. Whenever these values change, the
329
green rectangle smoothly animates to its new position:
331
\snippet qml/smoothedanimation.qml 0
333
A SmoothedAnimation can be configured by setting the \l velocity at which the
334
animation should occur, or the \l duration that the animation should take.
335
If both the \l velocity and \l duration are specified, the one that results in
336
the quickest animation is chosen for each change in the target value.
338
For example, animating from 0 to 800 will take 4 seconds if a velocity
339
of 200 is set, will take 8 seconds with a duration of 8000 set, and will
340
take 4 seconds with both a velocity of 200 and a duration of 8000 set.
341
Animating from 0 to 20000 will take 10 seconds if a velocity of 200 is set,
342
will take 8 seconds with a duration of 8000 set, and will take 8 seconds
343
with both a velocity of 200 and a duration of 8000 set.
345
The default velocity of SmoothedAnimation is 200 units/second. Note that if the range of the
346
value being animated is small, then the velocity will need to be adjusted
347
appropriately. For example, the opacity of an item ranges from 0 - 1.0.
348
To enable a smooth animation in this range the velocity will need to be
349
set to a value such as 0.5 units/second. Animating from 0 to 1.0 with a velocity
350
of 0.5 will take 2000 ms to complete.
352
Like any other animation type, a SmoothedAnimation can be applied in a
353
number of ways, including transitions, behaviors and property value
354
sources. The \l {Animation and Transitions in Qt Quick} documentation shows a
355
variety of methods for creating animations.
357
\sa SpringAnimation, NumberAnimation, {Animation and Transitions in Qt Quick}, {Qt Quick Examples - Animation}
360
QQuickSmoothedAnimation::QQuickSmoothedAnimation(QObject *parent)
361
: QQuickNumberAnimation(*(new QQuickSmoothedAnimationPrivate), parent)
365
QQuickSmoothedAnimation::~QQuickSmoothedAnimation()
370
QQuickSmoothedAnimationPrivate::QQuickSmoothedAnimationPrivate()
373
anim = new QSmoothedAnimation;
376
QQuickSmoothedAnimationPrivate::~QQuickSmoothedAnimationPrivate()
379
QHash<QQmlProperty, QSmoothedAnimation* >::iterator it;
380
for (it = activeAnimations.begin(); it != activeAnimations.end(); ++it) {
381
it.value()->clearTemplate();
385
void QQuickSmoothedAnimationPrivate::updateRunningAnimations()
387
foreach(QSmoothedAnimation* ease, activeAnimations.values()){
388
ease->maximumEasingTime = anim->maximumEasingTime;
389
ease->reversingMode = anim->reversingMode;
390
ease->velocity = anim->velocity;
391
ease->userDuration = anim->userDuration;
396
QAbstractAnimationJob* QQuickSmoothedAnimation::transition(QQuickStateActions &actions,
397
QQmlProperties &modified,
398
TransitionDirection direction,
399
QObject *defaultTarget)
402
Q_D(QQuickSmoothedAnimation);
404
QQuickStateActions dataActions = QQuickPropertyAnimation::createTransitionActions(actions, modified, defaultTarget);
406
QParallelAnimationGroupJob *wrapperGroup = new QParallelAnimationGroupJob();
408
if (!dataActions.isEmpty()) {
409
QSet<QAbstractAnimationJob*> anims;
410
for (int i = 0; i < dataActions.size(); i++) {
411
QSmoothedAnimation *ease;
413
if (!d->activeAnimations.contains(dataActions[i].property)) {
414
ease = new QSmoothedAnimation(d);
415
d->activeAnimations.insert(dataActions[i].property, ease);
416
ease->target = dataActions[i].property;
419
ease = d->activeAnimations.value(dataActions[i].property);
422
wrapperGroup->appendAnimation(initInstance(ease));
424
ease->to = dataActions[i].toValue.toReal();
426
// copying public members from main value holder animation
427
ease->maximumEasingTime = d->anim->maximumEasingTime;
428
ease->reversingMode = d->anim->reversingMode;
429
ease->velocity = d->anim->velocity;
430
ease->userDuration = d->anim->userDuration;
432
ease->initialVelocity = ease->trackVelocity;
435
ease->prepareForRestart();
439
foreach (QSmoothedAnimation *ease, d->activeAnimations.values()){
440
if (!anims.contains(ease)) {
441
ease->clearTemplate();
442
d->activeAnimations.remove(ease->target);
450
\qmlproperty enumeration QtQuick::SmoothedAnimation::reversingMode
452
Sets how the SmoothedAnimation behaves if an animation direction is reversed.
457
\li SmoothedAnimation.Eased (default) - the animation will smoothly decelerate, and then reverse direction
458
\li SmoothedAnimation.Immediate - the animation will immediately begin accelerating in the reverse direction, beginning with a velocity of 0
459
\li SmoothedAnimation.Sync - the property is immediately set to the target value
462
QQuickSmoothedAnimation::ReversingMode QQuickSmoothedAnimation::reversingMode() const
464
Q_D(const QQuickSmoothedAnimation);
465
return (QQuickSmoothedAnimation::ReversingMode) d->anim->reversingMode;
468
void QQuickSmoothedAnimation::setReversingMode(ReversingMode m)
470
Q_D(QQuickSmoothedAnimation);
471
if (d->anim->reversingMode == m)
474
d->anim->reversingMode = m;
475
emit reversingModeChanged();
476
d->updateRunningAnimations();
480
\qmlproperty int QtQuick::SmoothedAnimation::duration
482
This property holds the animation duration, in msecs, used when tracking the source.
484
Setting this to -1 (the default) disables the duration value.
486
If the velocity value and the duration value are both enabled, then the animation will
487
use whichever gives the shorter duration.
489
int QQuickSmoothedAnimation::duration() const
491
Q_D(const QQuickSmoothedAnimation);
492
return d->anim->userDuration;
495
void QQuickSmoothedAnimation::setDuration(int duration)
497
Q_D(QQuickSmoothedAnimation);
499
QQuickNumberAnimation::setDuration(duration);
500
if(duration == d->anim->userDuration)
502
d->anim->userDuration = duration;
503
d->updateRunningAnimations();
506
qreal QQuickSmoothedAnimation::velocity() const
508
Q_D(const QQuickSmoothedAnimation);
509
return d->anim->velocity;
513
\qmlproperty real QtQuick::SmoothedAnimation::velocity
515
This property holds the average velocity allowed when tracking the 'to' value.
517
The default velocity of SmoothedAnimation is 200 units/second.
519
Setting this to -1 disables the velocity value.
521
If the velocity value and the duration value are both enabled, then the animation will
522
use whichever gives the shorter duration.
524
void QQuickSmoothedAnimation::setVelocity(qreal v)
526
Q_D(QQuickSmoothedAnimation);
527
if (d->anim->velocity == v)
530
d->anim->velocity = v;
531
emit velocityChanged();
532
d->updateRunningAnimations();
536
\qmlproperty int QtQuick::SmoothedAnimation::maximumEasingTime
538
This property specifies the maximum time, in msecs, any "eases" during the follow should take.
539
Setting this property causes the velocity to "level out" after at a time. Setting
540
a negative value reverts to the normal mode of easing over the entire animation
543
The default value is -1.
545
int QQuickSmoothedAnimation::maximumEasingTime() const
547
Q_D(const QQuickSmoothedAnimation);
548
return d->anim->maximumEasingTime;
551
void QQuickSmoothedAnimation::setMaximumEasingTime(int v)
553
Q_D(QQuickSmoothedAnimation);
554
if(v == d->anim->maximumEasingTime)
556
d->anim->maximumEasingTime = v;
557
emit maximumEasingTimeChanged();
558
d->updateRunningAnimations();