1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qdeclarativespringanimation_p.h"
44 #include "qdeclarativeanimation_p_p.h"
45 #include <private/qdeclarativeproperty_p.h>
47 #include <QtCore/qdebug.h>
49 #include <private/qobject_p.h>
57 class QDeclarativeSpringAnimationPrivate : public QDeclarativePropertyAnimationPrivate
59 Q_DECLARE_PUBLIC(QDeclarativeSpringAnimation)
63 struct SpringAnimation {
65 : currentValue(0), to(0), velocity(0), start(0), duration(0) {}
72 QHash<QDeclarativeProperty, SpringAnimation> activeAnimations;
93 QDeclarativeSpringAnimationPrivate()
94 : maxVelocity(0), velocityms(0), lastTime(0)
95 , mass(1.0), spring(0.), damping(0.), epsilon(0.01)
96 , modulus(0.0), useMass(false), haveModulus(false)
97 , mode(Track), clock(0)
101 bool animate(const QDeclarativeProperty &property, SpringAnimation &animation, int elapsed);
104 typedef QTickAnimationProxy<QDeclarativeSpringAnimationPrivate, &QDeclarativeSpringAnimationPrivate::tick> Clock;
108 void QDeclarativeSpringAnimationPrivate::tick(int time)
114 int elapsed = time - lastTime;
118 if (mode == Spring) {
119 if (elapsed < 16) // capped at 62fps.
121 int count = elapsed / 16;
122 lastTime = time - (elapsed - count * 16);
127 QMutableHashIterator<QDeclarativeProperty, SpringAnimation> it(activeAnimations);
128 while (it.hasNext()) {
130 if (animate(it.key(), it.value(), elapsed))
134 if (activeAnimations.isEmpty())
138 bool QDeclarativeSpringAnimationPrivate::animate(const QDeclarativeProperty &property, SpringAnimation &animation, int elapsed)
140 qreal srcVal = animation.to;
145 animation.currentValue = fmod(animation.currentValue, modulus);
146 srcVal = fmod(srcVal, modulus);
148 if (mode == Spring) {
149 // Real men solve the spring DEs using RK4.
150 // We'll do something much simpler which gives a result that looks fine.
151 int count = elapsed / 16;
152 for (int i = 0; i < count; ++i) {
153 qreal diff = srcVal - animation.currentValue;
154 if (haveModulus && qAbs(diff) > modulus / 2) {
161 animation.velocity = animation.velocity + (spring * diff - damping * animation.velocity) / mass;
163 animation.velocity = animation.velocity + spring * diff - damping * animation.velocity;
164 if (maxVelocity > 0.) {
166 if (animation.velocity > maxVelocity)
167 animation.velocity = maxVelocity;
168 else if (animation.velocity < -maxVelocity)
169 animation.velocity = -maxVelocity;
171 animation.currentValue += animation.velocity * 16.0 / 1000.0;
173 animation.currentValue = fmod(animation.currentValue, modulus);
174 if (animation.currentValue < 0.0)
175 animation.currentValue += modulus;
178 if (qAbs(animation.velocity) < epsilon && qAbs(srcVal - animation.currentValue) < epsilon) {
179 animation.velocity = 0.0;
180 animation.currentValue = srcVal;
184 qreal moveBy = elapsed * velocityms;
185 qreal diff = srcVal - animation.currentValue;
186 if (haveModulus && qAbs(diff) > modulus / 2) {
193 animation.currentValue += moveBy;
195 animation.currentValue = fmod(animation.currentValue, modulus);
197 animation.currentValue -= moveBy;
198 if (haveModulus && animation.currentValue < 0.0)
199 animation.currentValue = fmod(animation.currentValue, modulus) + modulus;
201 if (lastTime - animation.start >= animation.duration) {
202 animation.currentValue = animation.to;
207 qreal old_to = animation.to;
209 QDeclarativePropertyPrivate::write(property, animation.currentValue,
210 QDeclarativePropertyPrivate::BypassInterceptor |
211 QDeclarativePropertyPrivate::DontRemoveBinding);
213 return (stop && old_to == animation.to); // do not stop if we got restarted
216 void QDeclarativeSpringAnimationPrivate::updateMode()
218 if (spring == 0. && maxVelocity == 0.)
220 else if (spring > 0.)
224 QHash<QDeclarativeProperty, SpringAnimation>::iterator it;
225 for (it = activeAnimations.begin(); it != activeAnimations.end(); ++it) {
226 SpringAnimation &animation = *it;
227 animation.start = lastTime;
228 qreal dist = qAbs(animation.currentValue - animation.to);
229 if (haveModulus && dist > modulus / 2)
230 dist = modulus - fmod(dist, modulus);
231 animation.duration = dist / velocityms;
237 \qmlclass SpringAnimation QDeclarativeSpringAnimation
238 \inqmlmodule QtQuick 2
239 \ingroup qml-animation-transition
240 \inherits NumberAnimation
242 \brief The SpringAnimation element allows a property to track a value in a spring-like motion.
244 SpringAnimation mimics the oscillatory behavior of a spring, with the appropriate \l spring constant to
245 control the acceleration and the \l damping to control how quickly the effect dies away.
247 You can also limit the maximum \l velocity of the animation.
249 The following \l Rectangle moves to the position of the mouse using a
250 SpringAnimation when the mouse is clicked. The use of the \l Behavior
251 on the \c x and \c y values indicates that whenever these values are
252 changed, a SpringAnimation should be applied.
254 \snippet doc/src/snippets/declarative/springanimation.qml 0
256 Like any other animation element, a SpringAnimation can be applied in a
257 number of ways, including transitions, behaviors and property value
258 sources. The \l {QML Animation and Transitions} documentation shows a
259 variety of methods for creating animations.
261 \sa SmoothedAnimation, {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example}, {declarative/toys/clocks}{Clocks example}
264 QDeclarativeSpringAnimation::QDeclarativeSpringAnimation(QObject *parent)
265 : QDeclarativeNumberAnimation(*(new QDeclarativeSpringAnimationPrivate),parent)
267 Q_D(QDeclarativeSpringAnimation);
268 d->clock = new QDeclarativeSpringAnimationPrivate::Clock(d, this);
271 QDeclarativeSpringAnimation::~QDeclarativeSpringAnimation()
276 \qmlproperty real QtQuick2::SpringAnimation::velocity
278 This property holds the maximum velocity allowed when tracking the source.
280 The default value is 0 (no maximum velocity).
283 qreal QDeclarativeSpringAnimation::velocity() const
285 Q_D(const QDeclarativeSpringAnimation);
286 return d->maxVelocity;
289 void QDeclarativeSpringAnimation::setVelocity(qreal velocity)
291 Q_D(QDeclarativeSpringAnimation);
292 d->maxVelocity = velocity;
293 d->velocityms = velocity / 1000.0;
298 \qmlproperty real QtQuick2::SpringAnimation::spring
300 This property describes how strongly the target is pulled towards the
301 source. The default value is 0 (that is, the spring-like motion is disabled).
303 The useful value range is 0 - 5.0.
305 When this property is set and the \l velocity value is greater than 0,
306 the \l velocity limits the maximum speed.
308 qreal QDeclarativeSpringAnimation::spring() const
310 Q_D(const QDeclarativeSpringAnimation);
314 void QDeclarativeSpringAnimation::setSpring(qreal spring)
316 Q_D(QDeclarativeSpringAnimation);
322 \qmlproperty real QtQuick2::SpringAnimation::damping
323 This property holds the spring damping value.
325 This value describes how quickly the spring-like motion comes to rest.
326 The default value is 0.
328 The useful value range is 0 - 1.0. The lower the value, the faster it
331 qreal QDeclarativeSpringAnimation::damping() const
333 Q_D(const QDeclarativeSpringAnimation);
337 void QDeclarativeSpringAnimation::setDamping(qreal damping)
339 Q_D(QDeclarativeSpringAnimation);
343 d->damping = damping;
348 \qmlproperty real QtQuick2::SpringAnimation::epsilon
349 This property holds the spring epsilon.
351 The epsilon is the rate and amount of change in the value which is close enough
352 to 0 to be considered equal to zero. This will depend on the usage of the value.
353 For pixel positions, 0.25 would suffice. For scale, 0.005 will suffice.
355 The default is 0.01. Tuning this value can provide small performance improvements.
357 qreal QDeclarativeSpringAnimation::epsilon() const
359 Q_D(const QDeclarativeSpringAnimation);
363 void QDeclarativeSpringAnimation::setEpsilon(qreal epsilon)
365 Q_D(QDeclarativeSpringAnimation);
366 d->epsilon = epsilon;
370 \qmlproperty real QtQuick2::SpringAnimation::modulus
371 This property holds the modulus value. The default value is 0.
373 Setting a \a modulus forces the target value to "wrap around" at the modulus.
374 For example, setting the modulus to 360 will cause a value of 370 to wrap around to 10.
376 qreal QDeclarativeSpringAnimation::modulus() const
378 Q_D(const QDeclarativeSpringAnimation);
382 void QDeclarativeSpringAnimation::setModulus(qreal modulus)
384 Q_D(QDeclarativeSpringAnimation);
385 if (d->modulus != modulus) {
386 d->haveModulus = modulus != 0.0;
387 d->modulus = modulus;
389 emit modulusChanged();
394 \qmlproperty real QtQuick2::SpringAnimation::mass
395 This property holds the "mass" of the property being moved.
397 The value is 1.0 by default.
399 A greater mass causes slower movement and a greater spring-like
400 motion when an item comes to rest.
402 qreal QDeclarativeSpringAnimation::mass() const
404 Q_D(const QDeclarativeSpringAnimation);
408 void QDeclarativeSpringAnimation::setMass(qreal mass)
410 Q_D(QDeclarativeSpringAnimation);
411 if (d->mass != mass && mass > 0.0) {
412 d->useMass = mass != 1.0;
418 void QDeclarativeSpringAnimation::transition(QDeclarativeStateActions &actions,
419 QDeclarativeProperties &modified,
420 TransitionDirection direction)
422 Q_D(QDeclarativeSpringAnimation);
425 if (d->clock->state() != QAbstractAnimation::Running) {
429 QDeclarativeNumberAnimation::transition(actions, modified, direction);
434 if (!d->actions->isEmpty()) {
435 for (int i = 0; i < d->actions->size(); ++i) {
436 const QDeclarativeProperty &property = d->actions->at(i).property;
437 QDeclarativeSpringAnimationPrivate::SpringAnimation &animation
438 = d->activeAnimations[property];
439 animation.to = d->actions->at(i).toValue.toReal();
440 animation.start = d->lastTime;
441 if (d->fromIsDefined)
442 animation.currentValue = d->actions->at(i).fromValue.toReal();
444 animation.currentValue = property.read().toReal();
445 if (d->mode == QDeclarativeSpringAnimationPrivate::Velocity) {
446 qreal dist = qAbs(animation.currentValue - animation.to);
447 if (d->haveModulus && dist > d->modulus / 2)
448 dist = d->modulus - fmod(dist, d->modulus);
449 animation.duration = dist / d->velocityms;
456 QAbstractAnimation *QDeclarativeSpringAnimation::qtAnimation()
458 Q_D(QDeclarativeSpringAnimation);