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 QtDeclarative 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 "qdeclarativesmoothedanimation_p.h"
43 #include "qdeclarativesmoothedanimation_p_p.h"
45 #include "qdeclarativeanimation_p_p.h"
47 #include <qdeclarativeproperty.h>
48 #include <private/qdeclarativeproperty_p.h>
50 #include <private/qdeclarativeglobal_p.h>
52 #include <QtCore/qdebug.h>
56 #define DELAY_STOP_TIMER_INTERVAL 32
60 QSmoothedAnimation::QSmoothedAnimation(QObject *parent)
61 : QAbstractAnimation(parent), to(0), velocity(200), userDuration(-1), maximumEasingTime(-1),
62 reversingMode(QDeclarativeSmoothedAnimation::Eased), initialVelocity(0),
63 trackVelocity(0), initialValue(0), invert(false), finalDuration(-1), lastTime(0)
67 void QSmoothedAnimation::restart()
69 initialVelocity = trackVelocity;
70 if (state() != QAbstractAnimation::Running)
76 void QSmoothedAnimation::updateState(QAbstractAnimation::State newState, QAbstractAnimation::State /*oldState*/)
78 if (newState == QAbstractAnimation::Running)
82 void QSmoothedAnimation::timerEvent(QTimerEvent *event)
84 if (event->timerId() == delayedStopTimer.timerId()) {
85 delayedStopTimer.stop();
88 QAbstractAnimation::timerEvent(event);
92 void QSmoothedAnimation::delayedStop()
94 if (!delayedStopTimer.isActive())
95 delayedStopTimer.start(DELAY_STOP_TIMER_INTERVAL, this);
98 int QSmoothedAnimation::duration() const
103 bool QSmoothedAnimation::recalc()
105 s = to - initialValue;
106 vi = initialVelocity;
108 s = (invert? -1.0: 1.0) * s;
110 if (userDuration > 0 && velocity > 0) {
112 if (tf > (userDuration / 1000.)) tf = (userDuration / 1000.);
113 } else if (userDuration > 0) {
114 tf = userDuration / 1000.;
115 } else if (velocity > 0) {
121 finalDuration = ceil(tf * 1000.0);
123 if (maximumEasingTime == 0) {
131 } else if (maximumEasingTime != -1 && tf > (maximumEasingTime / 1000.)) {
132 qreal met = maximumEasingTime / 1000.;
142 a = (s - (vi * tf - 0.5 * vi * ta)) / (tf * ta - ta * ta);
147 sp = vi * ta + 0.5 * a * tp * tp;
148 sd = sp + vp * (tf - 2 * ta);
151 qreal c1 = 0.25 * tf * tf;
152 qreal c2 = 0.5 * vi * tf - s;
153 qreal c3 = -0.25 * vi * vi;
155 qreal a1 = (-c2 + sqrt(c2 * c2 - 4 * c1 * c3)) / (2. * c1);
157 qreal tp1 = 0.5 * tf - 0.5 * vi / a1;
158 qreal vp1 = a1 * tp1 + vi;
160 qreal sp1 = 0.5 * a1 * tp1 * tp1 + vi * tp1;
173 qreal QSmoothedAnimation::easeFollow(qreal time_seconds)
176 if (time_seconds < tp) {
177 trackVelocity = vi + time_seconds * a;
178 value = 0.5 * a * time_seconds * time_seconds + vi * time_seconds;
179 } else if (time_seconds < td) {
182 value = sp + time_seconds * vp;
183 } else if (time_seconds < tf) {
185 trackVelocity = vp - time_seconds * a;
186 value = sd - 0.5 * d * time_seconds * time_seconds + vp * time_seconds;
193 // to normalize 's' between [0..1], divide 'value' by 's'
197 void QSmoothedAnimation::updateCurrentTime(int t)
199 qreal time_seconds = qreal(t - lastTime) / 1000.;
201 qreal value = easeFollow(time_seconds);
202 value *= (invert? -1.0: 1.0);
203 QDeclarativePropertyPrivate::write(target, initialValue + value,
204 QDeclarativePropertyPrivate::BypassInterceptor
205 | QDeclarativePropertyPrivate::DontRemoveBinding);
208 void QSmoothedAnimation::init()
215 if (delayedStopTimer.isActive())
216 delayedStopTimer.stop();
218 initialValue = target.read().toReal();
219 lastTime = this->currentTime();
221 if (to == initialValue) {
226 bool hasReversed = trackVelocity != 0. &&
227 ((!invert) == ((initialValue - to) > 0));
230 switch (reversingMode) {
232 case QDeclarativeSmoothedAnimation::Eased:
233 initialVelocity = -trackVelocity;
235 case QDeclarativeSmoothedAnimation::Sync:
236 QDeclarativePropertyPrivate::write(target, to,
237 QDeclarativePropertyPrivate::BypassInterceptor
238 | QDeclarativePropertyPrivate::DontRemoveBinding);
242 case QDeclarativeSmoothedAnimation::Immediate:
248 trackVelocity = initialVelocity;
250 invert = (to < initialValue);
253 QDeclarativePropertyPrivate::write(target, to,
254 QDeclarativePropertyPrivate::BypassInterceptor
255 | QDeclarativePropertyPrivate::DontRemoveBinding);
262 \qmlclass SmoothedAnimation QDeclarativeSmoothedAnimation
263 \inqmlmodule QtQuick 2
264 \ingroup qml-animation-transition
265 \inherits NumberAnimation
266 \brief The SmoothedAnimation element allows a property to smoothly track a value.
268 A SmoothedAnimation animates a property's value to a set target value
269 using an ease in/out quad easing curve. When the target value changes,
270 the easing curves used to animate between the old and new target values
271 are smoothly spliced together to create a smooth movement to the new
272 target value that maintains the current velocity.
274 The follow example shows one \l Rectangle tracking the position of another
275 using SmoothedAnimation. The green rectangle's \c x and \c y values are
276 bound to those of the red rectangle. Whenever these values change, the
277 green rectangle smoothly animates to its new position:
279 \snippet doc/src/snippets/declarative/smoothedanimation.qml 0
281 A SmoothedAnimation can be configured by setting the \l velocity at which the
282 animation should occur, or the \l duration that the animation should take.
283 If both the \l velocity and \l duration are specified, the one that results in
284 the quickest animation is chosen for each change in the target value.
286 For example, animating from 0 to 800 will take 4 seconds if a velocity
287 of 200 is set, will take 8 seconds with a duration of 8000 set, and will
288 take 4 seconds with both a velocity of 200 and a duration of 8000 set.
289 Animating from 0 to 20000 will take 10 seconds if a velocity of 200 is set,
290 will take 8 seconds with a duration of 8000 set, and will take 8 seconds
291 with both a velocity of 200 and a duration of 8000 set.
293 The default velocity of SmoothedAnimation is 200 units/second. Note that if the range of the
294 value being animated is small, then the velocity will need to be adjusted
295 appropriately. For example, the opacity of an item ranges from 0 - 1.0.
296 To enable a smooth animation in this range the velocity will need to be
297 set to a value such as 0.5 units/second. Animating from 0 to 1.0 with a velocity
298 of 0.5 will take 2000 ms to complete.
300 Like any other animation element, a SmoothedAnimation can be applied in a
301 number of ways, including transitions, behaviors and property value
302 sources. The \l {QML Animation and Transitions} documentation shows a
303 variety of methods for creating animations.
305 \sa SpringAnimation, NumberAnimation, {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example}
308 QDeclarativeSmoothedAnimation::QDeclarativeSmoothedAnimation(QObject *parent)
309 : QDeclarativeNumberAnimation(*(new QDeclarativeSmoothedAnimationPrivate), parent)
313 QDeclarativeSmoothedAnimation::~QDeclarativeSmoothedAnimation()
317 QDeclarativeSmoothedAnimationPrivate::QDeclarativeSmoothedAnimationPrivate()
318 : wrapperGroup(new QParallelAnimationGroup), anim(new QSmoothedAnimation)
320 Q_Q(QDeclarativeSmoothedAnimation);
321 QDeclarative_setParent_noEvent(wrapperGroup, q);
322 QDeclarative_setParent_noEvent(anim, q);
325 void QDeclarativeSmoothedAnimationPrivate::updateRunningAnimations()
327 foreach(QSmoothedAnimation* ease, activeAnimations.values()){
328 ease->maximumEasingTime = anim->maximumEasingTime;
329 ease->reversingMode = anim->reversingMode;
330 ease->velocity = anim->velocity;
331 ease->userDuration = anim->userDuration;
336 QAbstractAnimation* QDeclarativeSmoothedAnimation::qtAnimation()
338 Q_D(QDeclarativeSmoothedAnimation);
339 return d->wrapperGroup;
342 void QDeclarativeSmoothedAnimation::transition(QDeclarativeStateActions &actions,
343 QDeclarativeProperties &modified,
344 TransitionDirection direction)
346 Q_D(QDeclarativeSmoothedAnimation);
347 QDeclarativeNumberAnimation::transition(actions, modified, direction);
352 QSet<QAbstractAnimation*> anims;
353 for (int i = 0; i < d->actions->size(); i++) {
354 QSmoothedAnimation *ease;
356 if (!d->activeAnimations.contains((*d->actions)[i].property)) {
357 ease = new QSmoothedAnimation();
358 d->wrapperGroup->addAnimation(ease);
359 d->activeAnimations.insert((*d->actions)[i].property, ease);
360 needsRestart = false;
362 ease = d->activeAnimations.value((*d->actions)[i].property);
365 ease->target = (*d->actions)[i].property;
366 ease->to = (*d->actions)[i].toValue.toReal();
368 // copying public members from main value holder animation
369 ease->maximumEasingTime = d->anim->maximumEasingTime;
370 ease->reversingMode = d->anim->reversingMode;
371 ease->velocity = d->anim->velocity;
372 ease->userDuration = d->anim->userDuration;
374 ease->initialVelocity = ease->trackVelocity;
381 for (int i = d->wrapperGroup->animationCount() - 1; i >= 0 ; --i) {
382 if (!anims.contains(d->wrapperGroup->animationAt(i))) {
383 QSmoothedAnimation *ease = static_cast<QSmoothedAnimation*>(d->wrapperGroup->animationAt(i));
384 d->activeAnimations.remove(ease->target);
385 d->wrapperGroup->takeAnimation(i);
392 \qmlproperty enumeration QtQuick2::SmoothedAnimation::reversingMode
394 Sets how the SmoothedAnimation behaves if an animation direction is reversed.
399 \o SmoothedAnimation.Eased (default) - the animation will smoothly decelerate, and then reverse direction
400 \o SmoothedAnimation.Immediate - the animation will immediately begin accelerating in the reverse direction, beginning with a velocity of 0
401 \o SmoothedAnimation.Sync - the property is immediately set to the target value
404 QDeclarativeSmoothedAnimation::ReversingMode QDeclarativeSmoothedAnimation::reversingMode() const
406 Q_D(const QDeclarativeSmoothedAnimation);
407 return (QDeclarativeSmoothedAnimation::ReversingMode) d->anim->reversingMode;
410 void QDeclarativeSmoothedAnimation::setReversingMode(ReversingMode m)
412 Q_D(QDeclarativeSmoothedAnimation);
413 if (d->anim->reversingMode == m)
416 d->anim->reversingMode = m;
417 emit reversingModeChanged();
418 d->updateRunningAnimations();
422 \qmlproperty int QtQuick2::SmoothedAnimation::duration
424 This property holds the animation duration, in msecs, used when tracking the source.
426 Setting this to -1 (the default) disables the duration value.
428 If the velocity value and the duration value are both enabled, then the animation will
429 use whichever gives the shorter duration.
431 int QDeclarativeSmoothedAnimation::duration() const
433 Q_D(const QDeclarativeSmoothedAnimation);
434 return d->anim->userDuration;
437 void QDeclarativeSmoothedAnimation::setDuration(int duration)
439 Q_D(QDeclarativeSmoothedAnimation);
441 QDeclarativeNumberAnimation::setDuration(duration);
442 if(duration == d->anim->userDuration)
444 d->anim->userDuration = duration;
445 d->updateRunningAnimations();
448 qreal QDeclarativeSmoothedAnimation::velocity() const
450 Q_D(const QDeclarativeSmoothedAnimation);
451 return d->anim->velocity;
455 \qmlproperty real QtQuick2::SmoothedAnimation::velocity
457 This property holds the average velocity allowed when tracking the 'to' value.
459 The default velocity of SmoothedAnimation is 200 units/second.
461 Setting this to -1 disables the velocity value.
463 If the velocity value and the duration value are both enabled, then the animation will
464 use whichever gives the shorter duration.
466 void QDeclarativeSmoothedAnimation::setVelocity(qreal v)
468 Q_D(QDeclarativeSmoothedAnimation);
469 if (d->anim->velocity == v)
472 d->anim->velocity = v;
473 emit velocityChanged();
474 d->updateRunningAnimations();
478 \qmlproperty int QtQuick2::SmoothedAnimation::maximumEasingTime
480 This property specifies the maximum time, in msecs, any "eases" during the follow should take.
481 Setting this property causes the velocity to "level out" after at a time. Setting
482 a negative value reverts to the normal mode of easing over the entire animation
485 The default value is -1.
487 int QDeclarativeSmoothedAnimation::maximumEasingTime() const
489 Q_D(const QDeclarativeSmoothedAnimation);
490 return d->anim->maximumEasingTime;
493 void QDeclarativeSmoothedAnimation::setMaximumEasingTime(int v)
495 Q_D(QDeclarativeSmoothedAnimation);
496 if(v == d->anim->maximumEasingTime)
498 d->anim->maximumEasingTime = v;
499 emit maximumEasingTimeChanged();
500 d->updateRunningAnimations();