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 "QtQuick1/private/qdeclarativesmoothedanimation_p.h"
43 #include "QtQuick1/private/qdeclarativesmoothedanimation_p_p.h"
45 #include "QtQuick1/private/qdeclarativeanimation_p_p.h"
47 #include <QtDeclarative/qdeclarativeproperty.h>
48 #include "QtDeclarative/private/qdeclarativeproperty_p.h"
50 #include "QtDeclarative/private/qdeclarativeglobal_p.h"
52 #include <QtCore/qdebug.h>
56 #define DELAY_STOP_TIMER_INTERVAL 32
62 QSmoothedAnimation_1::QSmoothedAnimation_1(QObject *parent)
63 : QAbstractAnimation(parent), to(0), velocity(200), userDuration(-1), maximumEasingTime(-1),
64 reversingMode(QDeclarative1SmoothedAnimation::Eased), initialVelocity(0),
65 trackVelocity(0), initialValue(0), invert(false), finalDuration(-1), lastTime(0)
67 delayedStopTimer.setInterval(DELAY_STOP_TIMER_INTERVAL);
68 delayedStopTimer.setSingleShot(true);
69 connect(&delayedStopTimer, SIGNAL(timeout()), this, SLOT(stop()));
72 void QSmoothedAnimation_1::restart()
74 initialVelocity = trackVelocity;
75 if (state() != QAbstractAnimation::Running)
81 void QSmoothedAnimation_1::updateState(QAbstractAnimation::State newState, QAbstractAnimation::State /*oldState*/)
83 if (newState == QAbstractAnimation::Running)
87 void QSmoothedAnimation_1::delayedStop()
89 if (!delayedStopTimer.isActive())
90 delayedStopTimer.start();
93 int QSmoothedAnimation_1::duration() const
98 bool QSmoothedAnimation_1::recalc()
100 s = to - initialValue;
101 vi = initialVelocity;
103 s = (invert? -1.0: 1.0) * s;
105 if (userDuration > 0 && velocity > 0) {
107 if (tf > (userDuration / 1000.)) tf = (userDuration / 1000.);
108 } else if (userDuration > 0) {
109 tf = userDuration / 1000.;
110 } else if (velocity > 0) {
116 finalDuration = ceil(tf * 1000.0);
118 if (maximumEasingTime == 0) {
126 } else if (maximumEasingTime != -1 && tf > (maximumEasingTime / 1000.)) {
127 qreal met = maximumEasingTime / 1000.;
131 qreal c2 = (tf - td) * vi - tf * velocity;
132 qreal c3 = -0.5 * (tf - td) * vi * vi;
134 qreal vp1 = (-c2 + sqrt(c2 * c2 - 4 * c1 * c3)) / (2. * c1);
140 sp = vi * tp + 0.5 * a * tp * tp;
141 sd = sp + (td - tp) * vp;
143 qreal c1 = 0.25 * tf * tf;
144 qreal c2 = 0.5 * vi * tf - s;
145 qreal c3 = -0.25 * vi * vi;
147 qreal a1 = (-c2 + sqrt(c2 * c2 - 4 * c1 * c3)) / (2. * c1);
149 qreal tp1 = 0.5 * tf - 0.5 * vi / a1;
150 qreal vp1 = a1 * tp1 + vi;
152 qreal sp1 = 0.5 * a1 * tp1 * tp1 + vi * tp1;
165 qreal QSmoothedAnimation_1::easeFollow(qreal time_seconds)
168 if (time_seconds < tp) {
169 trackVelocity = vi + time_seconds * a;
170 value = 0.5 * a * time_seconds * time_seconds + vi * time_seconds;
171 } else if (time_seconds < td) {
174 value = sp + time_seconds * vp;
175 } else if (time_seconds < tf) {
177 trackVelocity = vp - time_seconds * a;
178 value = sd - 0.5 * d * time_seconds * time_seconds + vp * time_seconds;
185 // to normalize 's' between [0..1], divide 'value' by 's'
189 void QSmoothedAnimation_1::updateCurrentTime(int t)
191 qreal time_seconds = qreal(t - lastTime) / 1000.;
193 qreal value = easeFollow(time_seconds);
194 value *= (invert? -1.0: 1.0);
195 QDeclarativePropertyPrivate::write(target, initialValue + value,
196 QDeclarativePropertyPrivate::BypassInterceptor
197 | QDeclarativePropertyPrivate::DontRemoveBinding);
200 void QSmoothedAnimation_1::init()
207 if (delayedStopTimer.isActive())
208 delayedStopTimer.stop();
210 initialValue = target.read().toReal();
211 lastTime = this->currentTime();
213 if (to == initialValue) {
218 bool hasReversed = trackVelocity != 0. &&
219 ((!invert) == ((initialValue - to) > 0));
222 switch (reversingMode) {
224 case QDeclarative1SmoothedAnimation::Eased:
225 initialVelocity = -trackVelocity;
227 case QDeclarative1SmoothedAnimation::Sync:
228 QDeclarativePropertyPrivate::write(target, to,
229 QDeclarativePropertyPrivate::BypassInterceptor
230 | QDeclarativePropertyPrivate::DontRemoveBinding);
234 case QDeclarative1SmoothedAnimation::Immediate:
240 trackVelocity = initialVelocity;
242 invert = (to < initialValue);
245 QDeclarativePropertyPrivate::write(target, to,
246 QDeclarativePropertyPrivate::BypassInterceptor
247 | QDeclarativePropertyPrivate::DontRemoveBinding);
254 \qmlclass SmoothedAnimation QDeclarative1SmoothedAnimation
255 \inqmlmodule QtQuick 1
256 \ingroup qml-animation-transition
258 \inherits NumberAnimation
259 \brief The SmoothedAnimation element allows a property to smoothly track a value.
261 A SmoothedAnimation animates a property's value to a set target value
262 using an ease in/out quad easing curve. When the target value changes,
263 the easing curves used to animate between the old and new target values
264 are smoothly spliced together to create a smooth movement to the new
265 target value that maintains the current velocity.
267 The follow example shows one \l Rectangle tracking the position of another
268 using SmoothedAnimation. The green rectangle's \c x and \c y values are
269 bound to those of the red rectangle. Whenever these values change, the
270 green rectangle smoothly animates to its new position:
272 \snippet doc/src/snippets/qtquick1/smoothedanimation.qml 0
274 A SmoothedAnimation can be configured by setting the \l velocity at which the
275 animation should occur, or the \l duration that the animation should take.
276 If both the \l velocity and \l duration are specified, the one that results in
277 the quickest animation is chosen for each change in the target value.
279 For example, animating from 0 to 800 will take 4 seconds if a velocity
280 of 200 is set, will take 8 seconds with a duration of 8000 set, and will
281 take 4 seconds with both a velocity of 200 and a duration of 8000 set.
282 Animating from 0 to 20000 will take 10 seconds if a velocity of 200 is set,
283 will take 8 seconds with a duration of 8000 set, and will take 8 seconds
284 with both a velocity of 200 and a duration of 8000 set.
286 The default velocity of SmoothedAnimation is 200 units/second. Note that if the range of the
287 value being animated is small, then the velocity will need to be adjusted
288 appropriately. For example, the opacity of an item ranges from 0 - 1.0.
289 To enable a smooth animation in this range the velocity will need to be
290 set to a value such as 0.5 units/second. Animating from 0 to 1.0 with a velocity
291 of 0.5 will take 2000 ms to complete.
293 Like any other animation element, a SmoothedAnimation can be applied in a
294 number of ways, including transitions, behaviors and property value
295 sources. The \l {QML Animation and Transitions} documentation shows a
296 variety of methods for creating animations.
298 \sa SpringAnimation, NumberAnimation, {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example}
301 QDeclarative1SmoothedAnimation::QDeclarative1SmoothedAnimation(QObject *parent)
302 : QDeclarative1NumberAnimation(*(new QDeclarative1SmoothedAnimationPrivate), parent)
306 QDeclarative1SmoothedAnimation::~QDeclarative1SmoothedAnimation()
310 QDeclarative1SmoothedAnimationPrivate::QDeclarative1SmoothedAnimationPrivate()
311 : wrapperGroup(new QParallelAnimationGroup), anim(new QSmoothedAnimation_1)
313 Q_Q(QDeclarative1SmoothedAnimation);
314 QDeclarative_setParent_noEvent(wrapperGroup, q);
315 QDeclarative_setParent_noEvent(anim, q);
318 void QDeclarative1SmoothedAnimationPrivate::updateRunningAnimations()
320 foreach(QSmoothedAnimation_1* ease, activeAnimations.values()){
321 ease->maximumEasingTime = anim->maximumEasingTime;
322 ease->reversingMode = anim->reversingMode;
323 ease->velocity = anim->velocity;
324 ease->userDuration = anim->userDuration;
329 QAbstractAnimation* QDeclarative1SmoothedAnimation::qtAnimation()
331 Q_D(QDeclarative1SmoothedAnimation);
332 return d->wrapperGroup;
335 void QDeclarative1SmoothedAnimation::transition(QDeclarative1StateActions &actions,
336 QDeclarativeProperties &modified,
337 TransitionDirection direction)
339 Q_D(QDeclarative1SmoothedAnimation);
340 QDeclarative1NumberAnimation::transition(actions, modified, direction);
345 QSet<QAbstractAnimation*> anims;
346 for (int i = 0; i < d->actions->size(); i++) {
347 QSmoothedAnimation_1 *ease;
349 if (!d->activeAnimations.contains((*d->actions)[i].property)) {
350 ease = new QSmoothedAnimation_1();
351 d->wrapperGroup->addAnimation(ease);
352 d->activeAnimations.insert((*d->actions)[i].property, ease);
353 needsRestart = false;
355 ease = d->activeAnimations.value((*d->actions)[i].property);
358 ease->target = (*d->actions)[i].property;
359 ease->to = (*d->actions)[i].toValue.toReal();
361 // copying public members from main value holder animation
362 ease->maximumEasingTime = d->anim->maximumEasingTime;
363 ease->reversingMode = d->anim->reversingMode;
364 ease->velocity = d->anim->velocity;
365 ease->userDuration = d->anim->userDuration;
367 ease->initialVelocity = ease->trackVelocity;
374 for (int i = d->wrapperGroup->animationCount() - 1; i >= 0 ; --i) {
375 if (!anims.contains(d->wrapperGroup->animationAt(i))) {
376 QSmoothedAnimation_1 *ease = static_cast<QSmoothedAnimation_1*>(d->wrapperGroup->animationAt(i));
377 d->activeAnimations.remove(ease->target);
378 d->wrapperGroup->takeAnimation(i);
385 \qmlproperty enumeration QtQuick1::SmoothedAnimation::reversingMode
387 Sets how the SmoothedAnimation behaves if an animation direction is reversed.
392 \o SmoothedAnimation.Eased (default) - the animation will smoothly decelerate, and then reverse direction
393 \o SmoothedAnimation.Immediate - the animation will immediately begin accelerating in the reverse direction, beginning with a velocity of 0
394 \o SmoothedAnimation.Sync - the property is immediately set to the target value
397 QDeclarative1SmoothedAnimation::ReversingMode QDeclarative1SmoothedAnimation::reversingMode() const
399 Q_D(const QDeclarative1SmoothedAnimation);
400 return (QDeclarative1SmoothedAnimation::ReversingMode) d->anim->reversingMode;
403 void QDeclarative1SmoothedAnimation::setReversingMode(ReversingMode m)
405 Q_D(QDeclarative1SmoothedAnimation);
406 if (d->anim->reversingMode == m)
409 d->anim->reversingMode = m;
410 emit reversingModeChanged();
411 d->updateRunningAnimations();
415 \qmlproperty int QtQuick1::SmoothedAnimation::duration
417 This property holds the animation duration, in msecs, used when tracking the source.
419 Setting this to -1 (the default) disables the duration value.
421 If the velocity value and the duration value are both enabled, then the animation will
422 use whichever gives the shorter duration.
424 int QDeclarative1SmoothedAnimation::duration() const
426 Q_D(const QDeclarative1SmoothedAnimation);
427 return d->anim->userDuration;
430 void QDeclarative1SmoothedAnimation::setDuration(int duration)
432 Q_D(QDeclarative1SmoothedAnimation);
434 QDeclarative1NumberAnimation::setDuration(duration);
435 if(duration == d->anim->userDuration)
437 d->anim->userDuration = duration;
438 d->updateRunningAnimations();
441 qreal QDeclarative1SmoothedAnimation::velocity() const
443 Q_D(const QDeclarative1SmoothedAnimation);
444 return d->anim->velocity;
448 \qmlproperty real QtQuick1::SmoothedAnimation::velocity
450 This property holds the average velocity allowed when tracking the 'to' value.
452 The default velocity of SmoothedAnimation is 200 units/second.
454 Setting this to -1 disables the velocity value.
456 If the velocity value and the duration value are both enabled, then the animation will
457 use whichever gives the shorter duration.
459 void QDeclarative1SmoothedAnimation::setVelocity(qreal v)
461 Q_D(QDeclarative1SmoothedAnimation);
462 if (d->anim->velocity == v)
465 d->anim->velocity = v;
466 emit velocityChanged();
467 d->updateRunningAnimations();
471 \qmlproperty int QtQuick1::SmoothedAnimation::maximumEasingTime
473 This property specifies the maximum time, in msecs, any "eases" during the follow should take.
474 Setting this property causes the velocity to "level out" after at a time. Setting
475 a negative value reverts to the normal mode of easing over the entire animation
478 The default value is -1.
480 int QDeclarative1SmoothedAnimation::maximumEasingTime() const
482 Q_D(const QDeclarative1SmoothedAnimation);
483 return d->anim->maximumEasingTime;
486 void QDeclarative1SmoothedAnimation::setMaximumEasingTime(int v)
488 Q_D(QDeclarative1SmoothedAnimation);
489 if(v == d->anim->maximumEasingTime)
491 d->anim->maximumEasingTime = v;
492 emit maximumEasingTimeChanged();
493 d->updateRunningAnimations();