1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
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 qreal time_seconds = useDelta ? qreal(QQmlAnimationTimer::instance()->currentDelta()) / 1000. : qreal(t - lastTime) / 1000.;
249 qreal value = easeFollow(time_seconds);
250 value *= (invert? -1.0: 1.0);
251 QQmlPropertyPrivate::write(target, initialValue + value,
252 QQmlPropertyPrivate::BypassInterceptor
253 | QQmlPropertyPrivate::DontRemoveBinding);
256 void QSmoothedAnimation::init()
263 if (delayedStopTimer->isActive())
264 delayedStopTimer->stop();
266 initialValue = target.read().toReal();
267 lastTime = this->currentTime();
269 if (to == initialValue) {
274 bool hasReversed = trackVelocity != 0. &&
275 ((!invert) == ((initialValue - to) > 0));
278 switch (reversingMode) {
280 case QQuickSmoothedAnimation::Eased:
281 initialVelocity = -trackVelocity;
283 case QQuickSmoothedAnimation::Sync:
284 QQmlPropertyPrivate::write(target, to,
285 QQmlPropertyPrivate::BypassInterceptor
286 | QQmlPropertyPrivate::DontRemoveBinding);
290 case QQuickSmoothedAnimation::Immediate:
296 trackVelocity = initialVelocity;
298 invert = (to < initialValue);
301 QQmlPropertyPrivate::write(target, to,
302 QQmlPropertyPrivate::BypassInterceptor
303 | QQmlPropertyPrivate::DontRemoveBinding);
310 \qmltype SmoothedAnimation
311 \instantiates QQuickSmoothedAnimation
312 \inqmlmodule QtQuick 2
313 \ingroup qtquick-transitions-animations
314 \inherits NumberAnimation
315 \brief Allows a property to smoothly track a value
317 A SmoothedAnimation animates a property's value to a set target value
318 using an ease in/out quad easing curve. When the target value changes,
319 the easing curves used to animate between the old and new target values
320 are smoothly spliced together to create a smooth movement to the new
321 target value that maintains the current velocity.
323 The follow example shows one \l Rectangle tracking the position of another
324 using SmoothedAnimation. The green rectangle's \c x and \c y values are
325 bound to those of the red rectangle. Whenever these values change, the
326 green rectangle smoothly animates to its new position:
328 \snippet qml/smoothedanimation.qml 0
330 A SmoothedAnimation can be configured by setting the \l velocity at which the
331 animation should occur, or the \l duration that the animation should take.
332 If both the \l velocity and \l duration are specified, the one that results in
333 the quickest animation is chosen for each change in the target value.
335 For example, animating from 0 to 800 will take 4 seconds if a velocity
336 of 200 is set, will take 8 seconds with a duration of 8000 set, and will
337 take 4 seconds with both a velocity of 200 and a duration of 8000 set.
338 Animating from 0 to 20000 will take 10 seconds if a velocity of 200 is set,
339 will take 8 seconds with a duration of 8000 set, and will take 8 seconds
340 with both a velocity of 200 and a duration of 8000 set.
342 The default velocity of SmoothedAnimation is 200 units/second. Note that if the range of the
343 value being animated is small, then the velocity will need to be adjusted
344 appropriately. For example, the opacity of an item ranges from 0 - 1.0.
345 To enable a smooth animation in this range the velocity will need to be
346 set to a value such as 0.5 units/second. Animating from 0 to 1.0 with a velocity
347 of 0.5 will take 2000 ms to complete.
349 Like any other animation type, a SmoothedAnimation can be applied in a
350 number of ways, including transitions, behaviors and property value
351 sources. The \l {Animation and Transitions in Qt Quick} documentation shows a
352 variety of methods for creating animations.
354 \sa SpringAnimation, NumberAnimation, {Animation and Transitions in Qt Quick}, {declarative/animation/basics}{Animation basics example}
357 QQuickSmoothedAnimation::QQuickSmoothedAnimation(QObject *parent)
358 : QQuickNumberAnimation(*(new QQuickSmoothedAnimationPrivate), parent)
362 QQuickSmoothedAnimation::~QQuickSmoothedAnimation()
367 QQuickSmoothedAnimationPrivate::QQuickSmoothedAnimationPrivate()
370 anim = new QSmoothedAnimation;
373 QQuickSmoothedAnimationPrivate::~QQuickSmoothedAnimationPrivate()
376 QHash<QQmlProperty, QSmoothedAnimation* >::iterator it;
377 for (it = activeAnimations.begin(); it != activeAnimations.end(); ++it) {
378 it.value()->clearTemplate();
382 void QQuickSmoothedAnimationPrivate::updateRunningAnimations()
384 foreach(QSmoothedAnimation* ease, activeAnimations.values()){
385 ease->maximumEasingTime = anim->maximumEasingTime;
386 ease->reversingMode = anim->reversingMode;
387 ease->velocity = anim->velocity;
388 ease->userDuration = anim->userDuration;
393 QAbstractAnimationJob* QQuickSmoothedAnimation::transition(QQuickStateActions &actions,
394 QQmlProperties &modified,
395 TransitionDirection direction,
396 QObject *defaultTarget)
399 Q_D(QQuickSmoothedAnimation);
401 QQuickStateActions dataActions = QQuickPropertyAnimation::createTransitionActions(actions, modified, defaultTarget);
403 QParallelAnimationGroupJob *wrapperGroup = new QParallelAnimationGroupJob();
405 if (!dataActions.isEmpty()) {
406 QSet<QAbstractAnimationJob*> anims;
407 for (int i = 0; i < dataActions.size(); i++) {
408 QSmoothedAnimation *ease;
410 if (!d->activeAnimations.contains(dataActions[i].property)) {
411 ease = new QSmoothedAnimation(d);
412 d->activeAnimations.insert(dataActions[i].property, ease);
413 ease->target = dataActions[i].property;
416 ease = d->activeAnimations.value(dataActions[i].property);
419 wrapperGroup->appendAnimation(initInstance(ease));
421 ease->to = dataActions[i].toValue.toReal();
423 // copying public members from main value holder animation
424 ease->maximumEasingTime = d->anim->maximumEasingTime;
425 ease->reversingMode = d->anim->reversingMode;
426 ease->velocity = d->anim->velocity;
427 ease->userDuration = d->anim->userDuration;
429 ease->initialVelocity = ease->trackVelocity;
432 ease->prepareForRestart();
436 foreach (QSmoothedAnimation *ease, d->activeAnimations.values()){
437 if (!anims.contains(ease)) {
438 ease->clearTemplate();
439 d->activeAnimations.remove(ease->target);
447 \qmlproperty enumeration QtQuick2::SmoothedAnimation::reversingMode
449 Sets how the SmoothedAnimation behaves if an animation direction is reversed.
454 \li SmoothedAnimation.Eased (default) - the animation will smoothly decelerate, and then reverse direction
455 \li SmoothedAnimation.Immediate - the animation will immediately begin accelerating in the reverse direction, beginning with a velocity of 0
456 \li SmoothedAnimation.Sync - the property is immediately set to the target value
459 QQuickSmoothedAnimation::ReversingMode QQuickSmoothedAnimation::reversingMode() const
461 Q_D(const QQuickSmoothedAnimation);
462 return (QQuickSmoothedAnimation::ReversingMode) d->anim->reversingMode;
465 void QQuickSmoothedAnimation::setReversingMode(ReversingMode m)
467 Q_D(QQuickSmoothedAnimation);
468 if (d->anim->reversingMode == m)
471 d->anim->reversingMode = m;
472 emit reversingModeChanged();
473 d->updateRunningAnimations();
477 \qmlproperty int QtQuick2::SmoothedAnimation::duration
479 This property holds the animation duration, in msecs, used when tracking the source.
481 Setting this to -1 (the default) disables the duration value.
483 If the velocity value and the duration value are both enabled, then the animation will
484 use whichever gives the shorter duration.
486 int QQuickSmoothedAnimation::duration() const
488 Q_D(const QQuickSmoothedAnimation);
489 return d->anim->userDuration;
492 void QQuickSmoothedAnimation::setDuration(int duration)
494 Q_D(QQuickSmoothedAnimation);
496 QQuickNumberAnimation::setDuration(duration);
497 if(duration == d->anim->userDuration)
499 d->anim->userDuration = duration;
500 d->updateRunningAnimations();
503 qreal QQuickSmoothedAnimation::velocity() const
505 Q_D(const QQuickSmoothedAnimation);
506 return d->anim->velocity;
510 \qmlproperty real QtQuick2::SmoothedAnimation::velocity
512 This property holds the average velocity allowed when tracking the 'to' value.
514 The default velocity of SmoothedAnimation is 200 units/second.
516 Setting this to -1 disables the velocity value.
518 If the velocity value and the duration value are both enabled, then the animation will
519 use whichever gives the shorter duration.
521 void QQuickSmoothedAnimation::setVelocity(qreal v)
523 Q_D(QQuickSmoothedAnimation);
524 if (d->anim->velocity == v)
527 d->anim->velocity = v;
528 emit velocityChanged();
529 d->updateRunningAnimations();
533 \qmlproperty int QtQuick2::SmoothedAnimation::maximumEasingTime
535 This property specifies the maximum time, in msecs, any "eases" during the follow should take.
536 Setting this property causes the velocity to "level out" after at a time. Setting
537 a negative value reverts to the normal mode of easing over the entire animation
540 The default value is -1.
542 int QQuickSmoothedAnimation::maximumEasingTime() const
544 Q_D(const QQuickSmoothedAnimation);
545 return d->anim->maximumEasingTime;
548 void QQuickSmoothedAnimation::setMaximumEasingTime(int v)
550 Q_D(QQuickSmoothedAnimation);
551 if(v == d->anim->maximumEasingTime)
553 d->anim->maximumEasingTime = v;
554 emit maximumEasingTimeChanged();
555 d->updateRunningAnimations();