src/declarative/util/qdeclarativespringanimation.cpp
changeset 37 758a864f9613
parent 33 3e2da88830cd
equal deleted inserted replaced
36:ef0373b55136 37:758a864f9613
    52 #include <math.h>
    52 #include <math.h>
    53 
    53 
    54 QT_BEGIN_NAMESPACE
    54 QT_BEGIN_NAMESPACE
    55 
    55 
    56 
    56 
    57 
    57 class QDeclarativeSpringAnimationPrivate : public QDeclarativePropertyAnimationPrivate
    58 class QDeclarativeSpringAnimationPrivate : public QDeclarativeAbstractAnimationPrivate
       
    59 {
    58 {
    60     Q_DECLARE_PUBLIC(QDeclarativeSpringAnimation)
    59     Q_DECLARE_PUBLIC(QDeclarativeSpringAnimation)
    61 public:
    60 public:
    62     QDeclarativeSpringAnimationPrivate()
    61 
    63         : currentValue(0), to(0), from(0), maxVelocity(0), lastTime(0)
    62 
    64         , mass(1.0), spring(0.), damping(0.), velocity(0), epsilon(0.01)
    63     struct SpringAnimation {
    65         , modulus(0.0), useMass(false), haveModulus(false), enabled(true)
    64         SpringAnimation()
    66         , fromDefined(false), toDefined(false)
    65             : currentValue(0), to(0), velocity(0){}
    67         , mode(Track), clock(this) {}
    66         qreal currentValue;
    68 
    67         qreal to;
    69     qreal currentValue;
    68         qreal velocity;
    70     qreal to;
    69     };
    71     qreal from;
    70     QHash<QDeclarativeProperty, SpringAnimation> activeAnimations;
       
    71 
    72     qreal maxVelocity;
    72     qreal maxVelocity;
    73     qreal velocityms;
    73     qreal velocityms;
    74     int lastTime;
    74     int lastTime;
    75     qreal mass;
    75     qreal mass;
    76     qreal spring;
    76     qreal spring;
    77     qreal damping;
    77     qreal damping;
    78     qreal velocity;
       
    79     qreal epsilon;
    78     qreal epsilon;
    80     qreal modulus;
    79     qreal modulus;
    81 
    80 
    82     bool useMass : 1;
    81     bool useMass : 1;
    83     bool haveModulus : 1;
    82     bool haveModulus : 1;
    84     bool enabled : 1;
       
    85     bool fromDefined : 1;
       
    86     bool toDefined : 1;
       
    87 
    83 
    88     enum Mode {
    84     enum Mode {
    89         Track,
    85         Track,
    90         Velocity,
    86         Velocity,
    91         Spring
    87         Spring
    92     };
    88     };
    93     Mode mode;
    89     Mode mode;
    94 
    90 
    95     void tick(int);
    91     QDeclarativeSpringAnimationPrivate()
       
    92           : maxVelocity(0), velocityms(0), lastTime(0)
       
    93           , mass(1.0), spring(0.), damping(0.), epsilon(0.01)
       
    94           , modulus(0.0), useMass(false), haveModulus(false)
       
    95           , mode(Track), clock(0)
       
    96     { }
       
    97 
       
    98     void tick(int time);
       
    99     bool animate(const QDeclarativeProperty &property, SpringAnimation &animation, int elapsed);
    96     void updateMode();
   100     void updateMode();
    97 
   101 
    98     QTickAnimationProxy<QDeclarativeSpringAnimationPrivate, &QDeclarativeSpringAnimationPrivate::tick> clock;
   102     typedef QTickAnimationProxy<QDeclarativeSpringAnimationPrivate, &QDeclarativeSpringAnimationPrivate::tick> Clock;
       
   103     Clock *clock;
    99 };
   104 };
   100 
   105 
   101 void QDeclarativeSpringAnimationPrivate::tick(int time)
   106 void QDeclarativeSpringAnimationPrivate::tick(int time)
   102 {
   107 {
   103     if (mode == Track) {
   108     if (mode == Track) {
   104         clock.stop();
   109         clock->stop();
   105         return;
   110         return;
   106     }
   111     }
   107 
       
   108     int elapsed = time - lastTime;
   112     int elapsed = time - lastTime;
   109     if (!elapsed)
   113     if (!elapsed)
   110         return;
   114         return;
   111     qreal srcVal = to;
   115 
   112 
       
   113     bool stop = false;
       
   114 
       
   115     if (haveModulus) {
       
   116         currentValue = fmod(currentValue, modulus);
       
   117         srcVal = fmod(srcVal, modulus);
       
   118     }
       
   119     if (mode == Spring) {
   116     if (mode == Spring) {
   120         if (elapsed < 16) // capped at 62fps.
   117         if (elapsed < 16) // capped at 62fps.
   121             return;
   118             return;
       
   119         int count = elapsed / 16;
       
   120         lastTime = time - (elapsed - count * 16);
       
   121     } else {
       
   122         lastTime = time;
       
   123     }
       
   124 
       
   125     QMutableHashIterator<QDeclarativeProperty, SpringAnimation> it(activeAnimations);
       
   126     while (it.hasNext()) {
       
   127         it.next();
       
   128         if (animate(it.key(), it.value(), elapsed))
       
   129             it.remove();
       
   130     }
       
   131 
       
   132     if (activeAnimations.isEmpty())
       
   133         clock->stop();
       
   134 }
       
   135 
       
   136 bool QDeclarativeSpringAnimationPrivate::animate(const QDeclarativeProperty &property, SpringAnimation &animation, int elapsed)
       
   137 {
       
   138 
       
   139     qreal srcVal = animation.to;
       
   140 
       
   141     bool stop = false;
       
   142 
       
   143     if (haveModulus) {
       
   144         animation.currentValue = fmod(animation.currentValue, modulus);
       
   145         srcVal = fmod(srcVal, modulus);
       
   146     }
       
   147     if (mode == Spring) {
   122         // Real men solve the spring DEs using RK4.
   148         // Real men solve the spring DEs using RK4.
   123         // We'll do something much simpler which gives a result that looks fine.
   149         // We'll do something much simpler which gives a result that looks fine.
   124         int count = elapsed / 16;
   150         int count = elapsed / 16;
   125         for (int i = 0; i < count; ++i) {
   151         for (int i = 0; i < count; ++i) {
   126             qreal diff = srcVal - currentValue;
   152             qreal diff = srcVal - animation.currentValue;
   127             if (haveModulus && qAbs(diff) > modulus / 2) {
   153             if (haveModulus && qAbs(diff) > modulus / 2) {
   128                 if (diff < 0)
   154                 if (diff < 0)
   129                     diff += modulus;
   155                     diff += modulus;
   130                 else
   156                 else
   131                     diff -= modulus;
   157                     diff -= modulus;
   132             }
   158             }
   133             if (useMass)
   159             if (useMass)
   134                 velocity = velocity + (spring * diff - damping * velocity) / mass;
   160                 animation.velocity = animation.velocity + (spring * diff - damping * animation.velocity) / mass;
   135             else
   161             else
   136                 velocity = velocity + spring * diff - damping * velocity;
   162                 animation.velocity = animation.velocity + spring * diff - damping * animation.velocity;
   137             if (maxVelocity > 0.) {
   163             if (maxVelocity > 0.) {
   138                 // limit velocity
   164                 // limit velocity
   139                 if (velocity > maxVelocity)
   165                 if (animation.velocity > maxVelocity)
   140                     velocity = maxVelocity;
   166                     animation.velocity = maxVelocity;
   141                 else if (velocity < -maxVelocity)
   167                 else if (animation.velocity < -maxVelocity)
   142                     velocity = -maxVelocity;
   168                     animation.velocity = -maxVelocity;
   143             }
   169             }
   144             currentValue += velocity * 16.0 / 1000.0;
   170             animation.currentValue += animation.velocity * 16.0 / 1000.0;
   145             if (haveModulus) {
   171             if (haveModulus) {
   146                 currentValue = fmod(currentValue, modulus);
   172                 animation.currentValue = fmod(animation.currentValue, modulus);
   147                 if (currentValue < 0.0)
   173                 if (animation.currentValue < 0.0)
   148                     currentValue += modulus;
   174                     animation.currentValue += modulus;
   149             }
   175             }
   150         }
   176         }
   151         if (qAbs(velocity) < epsilon && qAbs(srcVal - currentValue) < epsilon) {
   177         if (qAbs(animation.velocity) < epsilon && qAbs(srcVal - animation.currentValue) < epsilon) {
   152             velocity = 0.0;
   178             animation.velocity = 0.0;
   153             currentValue = srcVal;
   179             animation.currentValue = srcVal;
   154             stop = true;
   180             stop = true;
   155         }
   181         }
   156         lastTime = time - (elapsed - count * 16);
       
   157     } else {
   182     } else {
   158         qreal moveBy = elapsed * velocityms;
   183         qreal moveBy = elapsed * velocityms;
   159         qreal diff = srcVal - currentValue;
   184         qreal diff = srcVal - animation.currentValue;
   160         if (haveModulus && qAbs(diff) > modulus / 2) {
   185         if (haveModulus && qAbs(diff) > modulus / 2) {
   161             if (diff < 0)
   186             if (diff < 0)
   162                 diff += modulus;
   187                 diff += modulus;
   163             else
   188             else
   164                 diff -= modulus;
   189                 diff -= modulus;
   165         }
   190         }
   166         if (diff > 0) {
   191         if (diff > 0) {
   167             currentValue += moveBy;
   192             animation.currentValue += moveBy;
   168             if (haveModulus)
   193             if (haveModulus)
   169                 currentValue = fmod(currentValue, modulus);
   194                 animation.currentValue = fmod(animation.currentValue, modulus);
   170             if (currentValue > to) {
   195             if (animation.currentValue > animation.to) {
   171                 currentValue = to;
   196                 animation.currentValue = animation.to;
   172                 stop = true;
   197                 stop = true;
   173             }
   198             }
   174         } else {
   199         } else {
   175             currentValue -= moveBy;
   200             animation.currentValue -= moveBy;
   176             if (haveModulus && currentValue < 0.0)
   201             if (haveModulus && animation.currentValue < 0.0)
   177                 currentValue = fmod(currentValue, modulus) + modulus;
   202                 animation.currentValue = fmod(animation.currentValue, modulus) + modulus;
   178             if (currentValue < to) {
   203             if (animation.currentValue < animation.to) {
   179                 currentValue = to;
   204                 animation.currentValue = animation.to;
   180                 stop = true;
   205                 stop = true;
   181             }
   206             }
   182         }
   207         }
   183         lastTime = time;
   208     }
   184     }
   209 
   185 
   210     qreal old_to = animation.to;
   186     qreal old_to = to;
   211 
   187 
   212     QDeclarativePropertyPrivate::write(property, animation.currentValue,
   188     QDeclarativePropertyPrivate::write(defaultProperty, currentValue,
       
   189                                        QDeclarativePropertyPrivate::BypassInterceptor |
   213                                        QDeclarativePropertyPrivate::BypassInterceptor |
   190                                        QDeclarativePropertyPrivate::DontRemoveBinding);
   214                                        QDeclarativePropertyPrivate::DontRemoveBinding);
   191 
   215 
   192     if (stop && old_to == to) // do not stop if we got restarted
   216     return (stop && old_to == animation.to); // do not stop if we got restarted
   193         clock.stop();
       
   194 }
   217 }
   195 
   218 
   196 void QDeclarativeSpringAnimationPrivate::updateMode()
   219 void QDeclarativeSpringAnimationPrivate::updateMode()
   197 {
   220 {
   198     if (spring == 0. && maxVelocity == 0.)
   221     if (spring == 0. && maxVelocity == 0.)
   203         mode = Velocity;
   226         mode = Velocity;
   204 }
   227 }
   205 
   228 
   206 /*!
   229 /*!
   207     \qmlclass SpringAnimation QDeclarativeSpringAnimation
   230     \qmlclass SpringAnimation QDeclarativeSpringAnimation
       
   231     \ingroup qml-animation-transition
       
   232     \inherits Animation
   208     \since 4.7
   233     \since 4.7
   209 
   234 
   210     \brief The SpringAnimation element allows a property to track a value in a spring-like motion.
   235     \brief The SpringAnimation element allows a property to track a value in a spring-like motion.
   211 
   236 
   212     SpringAnimation mimics the oscillatory behavior of a spring, with the appropriate \l spring constant to
   237     SpringAnimation mimics the oscillatory behavior of a spring, with the appropriate \l spring constant to
   221 
   246 
   222     \snippet doc/src/snippets/declarative/springanimation.qml 0
   247     \snippet doc/src/snippets/declarative/springanimation.qml 0
   223 
   248 
   224     Like any other animation element, a SpringAnimation can be applied in a
   249     Like any other animation element, a SpringAnimation can be applied in a
   225     number of ways, including transitions, behaviors and property value 
   250     number of ways, including transitions, behaviors and property value 
   226     sources. The \l PropertyAnimation documentation shows a variety of methods
   251     sources. The \l {QML Animation} documentation shows a variety of methods
   227     for creating animations.
   252     for creating animations.
   228 
   253 
   229     \sa SmoothedAnimation, {QML Animation}, {declarative/animation/basics}{Animation basics example}, {declarative/toys/clocks}{Clocks example}
   254     \sa SmoothedAnimation, {QML Animation}, {declarative/animation/basics}{Animation basics example}, {declarative/toys/clocks}{Clocks example}
   230 */
   255 */
   231 
   256 
   232 QDeclarativeSpringAnimation::QDeclarativeSpringAnimation(QObject *parent)
   257 QDeclarativeSpringAnimation::QDeclarativeSpringAnimation(QObject *parent)
   233 : QDeclarativeAbstractAnimation(*(new QDeclarativeSpringAnimationPrivate),parent)
   258 : QDeclarativeNumberAnimation(*(new QDeclarativeSpringAnimationPrivate),parent)
   234 {
   259 {
       
   260     Q_D(QDeclarativeSpringAnimation);
       
   261     d->clock = new QDeclarativeSpringAnimationPrivate::Clock(d, this);
   235 }
   262 }
   236 
   263 
   237 QDeclarativeSpringAnimation::~QDeclarativeSpringAnimation()
   264 QDeclarativeSpringAnimation::~QDeclarativeSpringAnimation()
   238 {
   265 {
   239 }
   266 }
   240 
       
   241 void QDeclarativeSpringAnimation::setTarget(const QDeclarativeProperty &property)
       
   242 {
       
   243     Q_D(QDeclarativeSpringAnimation);
       
   244     d->defaultProperty = property;
       
   245     d->currentValue = property.read().toReal();
       
   246     if (!d->avoidPropertyValueSourceStart) {
       
   247         setRunning(true);
       
   248     }
       
   249 }
       
   250 
       
   251 qreal QDeclarativeSpringAnimation::to() const
       
   252 {
       
   253     Q_D(const QDeclarativeSpringAnimation);
       
   254     return d->toDefined ? d->to : 0;
       
   255 }
       
   256 
       
   257 /*!
       
   258     \qmlproperty real SpringAnimation::to
       
   259 
       
   260     This property holds the value at which the animation will end.
       
   261 
       
   262     If not set, the animation will continue until it reaches the
       
   263     value that is being tracked.
       
   264 */
       
   265 
       
   266 void QDeclarativeSpringAnimation::setTo(qreal value)
       
   267 {
       
   268     Q_D(QDeclarativeSpringAnimation);
       
   269     if (d->to == value)
       
   270         return;
       
   271 
       
   272     d->to = value;
       
   273     d->toDefined = true;
       
   274     d->lastTime = 0;
       
   275     emit toChanged(value);
       
   276 }
       
   277 
       
   278 qreal QDeclarativeSpringAnimation::from() const
       
   279 {
       
   280     Q_D(const QDeclarativeSpringAnimation);
       
   281     return d->fromDefined ? d->from : 0;
       
   282 }
       
   283 
       
   284 /*!
       
   285     \qmlproperty real SpringAnimation::from
       
   286 
       
   287     This property holds the value from which the animation will begin.
       
   288 
       
   289     If not set, the animation will start whenever the tracked value has
       
   290     changed, regardless of its value.
       
   291 */
       
   292 
       
   293 void QDeclarativeSpringAnimation::setFrom(qreal value)
       
   294 {
       
   295     Q_D(QDeclarativeSpringAnimation);
       
   296     if (d->from == value)
       
   297         return;
       
   298 
       
   299     d->currentValue = d->from = value;
       
   300     d->fromDefined = true;
       
   301     d->lastTime = 0;
       
   302     emit fromChanged(value);
       
   303 }
       
   304 
       
   305 
   267 
   306 /*!
   268 /*!
   307     \qmlproperty real SpringAnimation::velocity
   269     \qmlproperty real SpringAnimation::velocity
   308 
   270 
   309     This property holds the maximum velocity allowed when tracking the source.
   271     This property holds the maximum velocity allowed when tracking the source.
   450                                              TransitionDirection direction)
   412                                              TransitionDirection direction)
   451 {
   413 {
   452     Q_D(QDeclarativeSpringAnimation);
   414     Q_D(QDeclarativeSpringAnimation);
   453     Q_UNUSED(direction);
   415     Q_UNUSED(direction);
   454 
   416 
   455     if (d->clock.state() != QAbstractAnimation::Running)
   417     if (d->clock->state() != QAbstractAnimation::Running) {
   456         d->lastTime = 0;
   418         d->lastTime = 0;
   457 
   419     }
   458     if (!actions.isEmpty()) {
   420 
   459         for (int i = 0; i < actions.size(); ++i) {
   421     QDeclarativeNumberAnimation::transition(actions, modified, direction);
   460             if (!d->toDefined)
   422 
   461                 d->to = actions.at(i).toValue.toReal();
   423     if (!d->actions)
   462             if (!d->fromDefined)
   424         return;
   463                 d->currentValue = actions.at(i).fromValue.toReal();
   425 
   464             if (d->mode != QDeclarativeSpringAnimationPrivate::Track)
   426     if (!d->actions->isEmpty()) {
   465                 modified << d->defaultProperty;
   427         for (int i = 0; i < d->actions->size(); ++i) {
       
   428             const QDeclarativeProperty &property = d->actions->at(i).property;
       
   429             QDeclarativeSpringAnimationPrivate::SpringAnimation &animation
       
   430                     = d->activeAnimations[property];
       
   431             animation.to = d->actions->at(i).toValue.toReal();
       
   432             if (d->fromIsDefined)
       
   433                 animation.currentValue = d->actions->at(i).fromValue.toReal();
       
   434             else
       
   435                 animation.currentValue = property.read().toReal();
   466         }
   436         }
   467     }
   437     }
   468 }
   438 }
   469 
   439 
   470 
   440 
   471 QAbstractAnimation *QDeclarativeSpringAnimation::qtAnimation()
   441 QAbstractAnimation *QDeclarativeSpringAnimation::qtAnimation()
   472 {
   442 {
   473     Q_D(QDeclarativeSpringAnimation);
   443     Q_D(QDeclarativeSpringAnimation);
   474     return &d->clock;
   444     return d->clock;
   475 }
   445 }
   476 
   446 
   477 QT_END_NAMESPACE
   447 QT_END_NAMESPACE