src/declarative/util/qdeclarativesmoothedanimation.cpp
changeset 30 5dc02b23752f
child 33 3e2da88830cd
equal deleted inserted replaced
29:b72c6db6890b 30:5dc02b23752f
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "private/qdeclarativesmoothedanimation_p.h"
       
    43 #include "private/qdeclarativesmoothedanimation_p_p.h"
       
    44 
       
    45 #include "private/qdeclarativeanimation_p_p.h"
       
    46 
       
    47 #include <qdeclarativeproperty.h>
       
    48 #include "private/qdeclarativeproperty_p.h"
       
    49 
       
    50 #include "private/qdeclarativeglobal_p.h"
       
    51 
       
    52 #include <QtCore/qdebug.h>
       
    53 
       
    54 #include <math.h>
       
    55 
       
    56 #define DELAY_STOP_TIMER_INTERVAL 32
       
    57 
       
    58 QT_BEGIN_NAMESPACE
       
    59 
       
    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)
       
    64 {
       
    65     delayedStopTimer.setInterval(DELAY_STOP_TIMER_INTERVAL);
       
    66     delayedStopTimer.setSingleShot(true);
       
    67     connect(&delayedStopTimer, SIGNAL(timeout()), this, SLOT(stop()));
       
    68 }
       
    69 
       
    70 void QSmoothedAnimation::restart()
       
    71 {
       
    72     initialVelocity = trackVelocity;
       
    73     if (state() != QAbstractAnimation::Running)
       
    74         start();
       
    75     else
       
    76         init();
       
    77 }
       
    78 
       
    79 void QSmoothedAnimation::updateState(QAbstractAnimation::State newState, QAbstractAnimation::State /*oldState*/)
       
    80 {
       
    81     if (newState == QAbstractAnimation::Running)
       
    82         init();
       
    83 }
       
    84 
       
    85 void QSmoothedAnimation::delayedStop()
       
    86 {
       
    87     if (!delayedStopTimer.isActive())
       
    88         delayedStopTimer.start();
       
    89 }
       
    90 
       
    91 int QSmoothedAnimation::duration() const
       
    92 {
       
    93     return -1;
       
    94 }
       
    95 
       
    96 bool QSmoothedAnimation::recalc()
       
    97 {
       
    98     s = to - initialValue;
       
    99     vi = initialVelocity;
       
   100 
       
   101     s = (invert? -1.0: 1.0) * s;
       
   102 
       
   103     if (userDuration > 0 && velocity > 0) {
       
   104         tf = s / velocity;
       
   105         if (tf > (userDuration / 1000.)) tf = (userDuration / 1000.);
       
   106     } else if (userDuration > 0) {
       
   107         tf = userDuration / 1000.;
       
   108     } else if (velocity > 0) {
       
   109         tf = s / velocity;
       
   110     } else {
       
   111         return false;
       
   112     }
       
   113 
       
   114     finalDuration = ceil(tf * 1000.0);
       
   115 
       
   116     if (maximumEasingTime == 0) {
       
   117         a = 0;
       
   118         d = 0;
       
   119         tp = 0;
       
   120         td = tf;
       
   121         vp = velocity;
       
   122         sp = 0;
       
   123         sd = s;
       
   124     } else if (maximumEasingTime != -1 && tf > (maximumEasingTime / 1000.)) {
       
   125         qreal met = maximumEasingTime / 1000.;
       
   126         td = tf - met;
       
   127 
       
   128         qreal c1 = td;
       
   129         qreal c2 = (tf - td) * vi - tf * velocity;
       
   130         qreal c3 = -0.5 * (tf - td) * vi * vi;
       
   131 
       
   132         qreal vp1 = (-c2 + sqrt(c2 * c2 - 4 * c1 * c3)) / (2. * c1);
       
   133 
       
   134         vp = vp1;
       
   135         a = vp / met;
       
   136         d = a;
       
   137         tp = (vp - vi) / a;
       
   138         sp = vi * tp + 0.5 * a * tp * tp;
       
   139         sd = sp + (td - tp) * vp;
       
   140     } else {
       
   141         qreal c1 = 0.25 * tf * tf;
       
   142         qreal c2 = 0.5 * vi * tf - s;
       
   143         qreal c3 = -0.25 * vi * vi;
       
   144 
       
   145         qreal a1 = (-c2 + sqrt(c2 * c2 - 4 * c1 * c3)) / (2. * c1);
       
   146 
       
   147         qreal tp1 = 0.5 * tf - 0.5 * vi / a1;
       
   148         qreal vp1 = a1 * tp1 + vi;
       
   149 
       
   150         qreal sp1 = 0.5 * a1 * tp1 * tp1 + vi * tp1;
       
   151 
       
   152         a = a1;
       
   153         d = a1;
       
   154         tp = tp1;
       
   155         td = tp1;
       
   156         vp = vp1;
       
   157         sp = sp1;
       
   158         sd = sp1;
       
   159     }
       
   160     return true;
       
   161 }
       
   162 
       
   163 qreal QSmoothedAnimation::easeFollow(qreal time_seconds)
       
   164 {
       
   165     qreal value;
       
   166     if (time_seconds < tp) {
       
   167         trackVelocity = vi + time_seconds * a;
       
   168         value = 0.5 * a * time_seconds * time_seconds + vi * time_seconds;
       
   169     } else if (time_seconds < td) {
       
   170         time_seconds -= tp;
       
   171         trackVelocity = vp;
       
   172         value = sp + time_seconds * vp;
       
   173     } else if (time_seconds < tf) {
       
   174         time_seconds -= td;
       
   175         trackVelocity = vp - time_seconds * a;
       
   176         value = sd - 0.5 * d * time_seconds * time_seconds + vp * time_seconds;
       
   177     } else {
       
   178         trackVelocity = 0;
       
   179         value = s;
       
   180         delayedStop();
       
   181     }
       
   182 
       
   183     // to normalize 's' between [0..1], divide 'value' by 's'
       
   184     return value;
       
   185 }
       
   186 
       
   187 void QSmoothedAnimation::updateCurrentTime(int t)
       
   188 {
       
   189     qreal time_seconds = qreal(t - lastTime) / 1000.;
       
   190 
       
   191     qreal value = easeFollow(time_seconds);
       
   192     value *= (invert? -1.0: 1.0);
       
   193     QDeclarativePropertyPrivate::write(target, initialValue + value,
       
   194                                        QDeclarativePropertyPrivate::BypassInterceptor
       
   195                                        | QDeclarativePropertyPrivate::DontRemoveBinding);
       
   196 }
       
   197 
       
   198 void QSmoothedAnimation::init()
       
   199 {
       
   200     if (velocity == 0) {
       
   201         stop();
       
   202         return;
       
   203     }
       
   204 
       
   205     if (delayedStopTimer.isActive())
       
   206         delayedStopTimer.stop();
       
   207 
       
   208     initialValue = target.read().toReal();
       
   209     lastTime = this->currentTime();
       
   210 
       
   211     if (to == initialValue) {
       
   212         stop();
       
   213         return;
       
   214     }
       
   215 
       
   216     bool hasReversed = trackVelocity != 0. &&
       
   217                       ((trackVelocity > 0) == ((initialValue - to) > 0));
       
   218 
       
   219     if (hasReversed) {
       
   220         switch (reversingMode) {
       
   221             default:
       
   222             case QDeclarativeSmoothedAnimation::Eased:
       
   223                 break;
       
   224             case QDeclarativeSmoothedAnimation::Sync:
       
   225                 QDeclarativePropertyPrivate::write(target, to,
       
   226                                                    QDeclarativePropertyPrivate::BypassInterceptor
       
   227                                                    | QDeclarativePropertyPrivate::DontRemoveBinding);
       
   228                 stop();
       
   229                 return;
       
   230             case QDeclarativeSmoothedAnimation::Immediate:
       
   231                 initialVelocity = 0;
       
   232                 delayedStop();
       
   233                 break;
       
   234         }
       
   235     }
       
   236 
       
   237     trackVelocity = initialVelocity;
       
   238 
       
   239     invert = (to < initialValue);
       
   240 
       
   241     if (!recalc()) {
       
   242         QDeclarativePropertyPrivate::write(target, to,
       
   243                                            QDeclarativePropertyPrivate::BypassInterceptor
       
   244                                            | QDeclarativePropertyPrivate::DontRemoveBinding);
       
   245         stop();
       
   246         return;
       
   247     }
       
   248 }
       
   249 
       
   250 /*!
       
   251     \qmlclass SmoothedAnimation QDeclarativeSmoothedAnimation
       
   252     \since 4.7
       
   253     \inherits NumberAnimation
       
   254     \brief The SmoothedAnimation element allows a property to smoothly track a value.
       
   255 
       
   256     The SmoothedAnimation animates a property's value to a set target value
       
   257     using an ease in/out quad easing curve.  If the animation is restarted
       
   258     with a different target value, the easing curves used to animate to the old
       
   259     and the new target values are smoothly spliced together to avoid any obvious
       
   260     visual glitches by maintaining the current velocity.
       
   261 
       
   262     The property animation is configured by setting the velocity at which the
       
   263     animation should occur, or the duration that the animation should take.
       
   264     If both a velocity and a duration are specified, the one that results in
       
   265     the quickest animation is chosen for each change in the target value.
       
   266 
       
   267     For example, animating from 0 to 800 will take 4 seconds if a velocity
       
   268     of 200 is set, will take 8 seconds with a duration of 8000 set, and will
       
   269     take 4 seconds with both a velocity of 200 and a duration of 8000 set.
       
   270     Animating from 0 to 20000 will take 10 seconds if a velocity of 200 is set,
       
   271     will take 8 seconds with a duration of 8000 set, and will take 8 seconds
       
   272     with both a velocity of 200 and a duration of 8000 set.
       
   273 
       
   274     The follow example shows one rectangle tracking the position of another.
       
   275 \code
       
   276 import Qt 4.7
       
   277 
       
   278 Rectangle {
       
   279     width: 800; height: 600; color: "blue"
       
   280 
       
   281     Rectangle {
       
   282         color: "green"
       
   283         width: 60; height: 60;
       
   284         x: rect1.x - 5; y: rect1.y - 5;
       
   285         Behavior on x { SmoothedAnimation { velocity: 200 } }
       
   286         Behavior on y { SmoothedAnimation { velocity: 200 } }
       
   287     }
       
   288 
       
   289     Rectangle {
       
   290         id: rect1
       
   291         color: "red"
       
   292         width: 50; height: 50;
       
   293     }
       
   294 
       
   295     focus: true
       
   296     Keys.onRightPressed: rect1.x = rect1.x + 100
       
   297     Keys.onLeftPressed: rect1.x = rect1.x - 100
       
   298     Keys.onUpPressed: rect1.y = rect1.y - 100
       
   299     Keys.onDownPressed: rect1.y = rect1.y + 100
       
   300 }
       
   301 \endcode
       
   302 
       
   303     The default velocity of SmoothedAnimation is 200 units/second.  Note that if the range of the
       
   304     value being animated is small, then the velocity will need to be adjusted
       
   305     appropriately.  For example, the opacity of an item ranges from 0 - 1.0.
       
   306     To enable a smooth animation in this range the velocity will need to be
       
   307     set to a value such as 0.5 units/second.  Animating from 0 to 1.0 with a velocity
       
   308     of 0.5 will take 2000 ms to complete.
       
   309 
       
   310     \sa SpringFollow
       
   311 */
       
   312 
       
   313 QDeclarativeSmoothedAnimation::QDeclarativeSmoothedAnimation(QObject *parent)
       
   314 : QDeclarativeNumberAnimation(*(new QDeclarativeSmoothedAnimationPrivate), parent)
       
   315 {
       
   316 }
       
   317 
       
   318 QDeclarativeSmoothedAnimation::~QDeclarativeSmoothedAnimation()
       
   319 {
       
   320 }
       
   321 
       
   322 QDeclarativeSmoothedAnimationPrivate::QDeclarativeSmoothedAnimationPrivate()
       
   323     : wrapperGroup(new QParallelAnimationGroup), anim(new QSmoothedAnimation)
       
   324 {
       
   325     Q_Q(QDeclarativeSmoothedAnimation);
       
   326     QDeclarative_setParent_noEvent(wrapperGroup, q);
       
   327     QDeclarative_setParent_noEvent(anim, q);
       
   328 }
       
   329 
       
   330 QAbstractAnimation* QDeclarativeSmoothedAnimation::qtAnimation()
       
   331 {
       
   332     Q_D(QDeclarativeSmoothedAnimation);
       
   333     return d->wrapperGroup;
       
   334 }
       
   335 
       
   336 void QDeclarativeSmoothedAnimation::transition(QDeclarativeStateActions &actions,
       
   337                                                QDeclarativeProperties &modified,
       
   338                                                TransitionDirection direction)
       
   339 {
       
   340     Q_D(QDeclarativeSmoothedAnimation);
       
   341     QDeclarativeNumberAnimation::transition(actions, modified, direction);
       
   342 
       
   343     if (!d->actions)
       
   344         return;
       
   345 
       
   346     QSet<QAbstractAnimation*> anims;
       
   347     for (int i = 0; i < d->actions->size(); i++) {
       
   348         QSmoothedAnimation *ease;
       
   349         bool needsRestart;
       
   350         if (!d->activeAnimations.contains((*d->actions)[i].property)) {
       
   351             ease = new QSmoothedAnimation();
       
   352             d->wrapperGroup->addAnimation(ease);
       
   353             d->activeAnimations.insert((*d->actions)[i].property, ease);
       
   354             needsRestart = false;
       
   355         } else {
       
   356             ease = d->activeAnimations.value((*d->actions)[i].property);
       
   357             needsRestart = true;
       
   358         }
       
   359 
       
   360         ease->target = (*d->actions)[i].property;
       
   361         ease->to = (*d->actions)[i].toValue.toReal();
       
   362 
       
   363         // copying public members from main value holder animation
       
   364         ease->maximumEasingTime = d->anim->maximumEasingTime;
       
   365         ease->reversingMode = d->anim->reversingMode;
       
   366         ease->velocity = d->anim->velocity;
       
   367         ease->userDuration = d->anim->userDuration;
       
   368 
       
   369         ease->initialVelocity = ease->trackVelocity;
       
   370 
       
   371         if (needsRestart)
       
   372             ease->init();
       
   373         anims.insert(ease);
       
   374     }
       
   375 
       
   376     for (int i = d->wrapperGroup->animationCount() - 1; i >= 0 ; --i) {
       
   377         if (!anims.contains(d->wrapperGroup->animationAt(i))) {
       
   378             QSmoothedAnimation *ease = static_cast<QSmoothedAnimation*>(d->wrapperGroup->animationAt(i));
       
   379             d->activeAnimations.remove(ease->target);
       
   380             d->wrapperGroup->takeAnimation(i);
       
   381             delete ease;
       
   382         }
       
   383     }
       
   384 }
       
   385 
       
   386 /*!
       
   387     \qmlproperty enumeration SmoothedAnimation::reversingMode
       
   388 
       
   389     Sets how the SmoothedAnimation behaves if an animation direction is reversed.
       
   390 
       
   391     If reversing mode is \c SmoothedAnimation.Eased, the animation will smoothly decelerate, and
       
   392     then reverse direction.  If the reversing mode is \c SmoothedAnimation.Immediate, the
       
   393     animation will immediately begin accelerating in the reverse direction,
       
   394     begining with a velocity of 0.  If the reversing mode is \c SmoothedAnimation.Sync, the
       
   395     property is immediately set to the target value.
       
   396 */
       
   397 QDeclarativeSmoothedAnimation::ReversingMode QDeclarativeSmoothedAnimation::reversingMode() const
       
   398 {
       
   399     Q_D(const QDeclarativeSmoothedAnimation);
       
   400     return (QDeclarativeSmoothedAnimation::ReversingMode) d->anim->reversingMode;
       
   401 }
       
   402 
       
   403 void QDeclarativeSmoothedAnimation::setReversingMode(ReversingMode m)
       
   404 {
       
   405     Q_D(QDeclarativeSmoothedAnimation);
       
   406     if (d->anim->reversingMode == m)
       
   407         return;
       
   408 
       
   409     d->anim->reversingMode = m;
       
   410     emit reversingModeChanged();
       
   411 }
       
   412 
       
   413 /*!
       
   414     \qmlproperty int SmoothedAnimation::duration
       
   415 
       
   416     This property holds the animation duration, in msecs, used when tracking the source.
       
   417 
       
   418     Setting this to -1 (the default) disables the duration value.
       
   419 */
       
   420 int QDeclarativeSmoothedAnimation::duration() const
       
   421 {
       
   422     Q_D(const QDeclarativeSmoothedAnimation);
       
   423     return d->anim->userDuration;
       
   424 }
       
   425 
       
   426 void QDeclarativeSmoothedAnimation::setDuration(int duration)
       
   427 {
       
   428     Q_D(QDeclarativeSmoothedAnimation);
       
   429     if (duration != -1)
       
   430         QDeclarativeNumberAnimation::setDuration(duration);
       
   431     d->anim->userDuration = duration;
       
   432 }
       
   433 
       
   434 qreal QDeclarativeSmoothedAnimation::velocity() const
       
   435 {
       
   436     Q_D(const QDeclarativeSmoothedAnimation);
       
   437     return d->anim->velocity;
       
   438 }
       
   439 
       
   440 /*!
       
   441     \qmlproperty real SmoothedAnimation::velocity
       
   442 
       
   443     This property holds the average velocity allowed when tracking the 'to' value.
       
   444 
       
   445     The default velocity of SmoothedAnimation is 200 units/second.
       
   446 
       
   447     Setting this to -1 disables the velocity value.
       
   448 */
       
   449 void QDeclarativeSmoothedAnimation::setVelocity(qreal v)
       
   450 {
       
   451     Q_D(QDeclarativeSmoothedAnimation);
       
   452     if (d->anim->velocity == v)
       
   453         return;
       
   454 
       
   455     d->anim->velocity = v;
       
   456     emit velocityChanged();
       
   457 }
       
   458 
       
   459 /*!
       
   460     \qmlproperty int SmoothedAnimation::maximumEasingTime
       
   461 
       
   462     This property specifies the maximum time, in msecs, an "eases" during the follow should take.
       
   463     Setting this property causes the velocity to "level out" after at a time.  Setting
       
   464     a negative value reverts to the normal mode of easing over the entire animation
       
   465     duration.
       
   466 
       
   467     The default value is -1.
       
   468 */
       
   469 int QDeclarativeSmoothedAnimation::maximumEasingTime() const
       
   470 {
       
   471     Q_D(const QDeclarativeSmoothedAnimation);
       
   472     return d->anim->maximumEasingTime;
       
   473 }
       
   474 
       
   475 void QDeclarativeSmoothedAnimation::setMaximumEasingTime(int v)
       
   476 {
       
   477     Q_D(QDeclarativeSmoothedAnimation);
       
   478     d->anim->maximumEasingTime = v;
       
   479     emit maximumEasingTimeChanged();
       
   480 }
       
   481 
       
   482 QT_END_NAMESPACE