source: trunk/src/declarative/util/qdeclarativesmoothedanimation.cpp@ 1147

Last change on this file since 1147 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 16.2 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 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** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
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** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
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
58QT_BEGIN_NAMESPACE
59
60QSmoothedAnimation::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
70void QSmoothedAnimation::restart()
71{
72 initialVelocity = trackVelocity;
73 if (state() != QAbstractAnimation::Running)
74 start();
75 else
76 init();
77}
78
79void QSmoothedAnimation::updateState(QAbstractAnimation::State newState, QAbstractAnimation::State /*oldState*/)
80{
81 if (newState == QAbstractAnimation::Running)
82 init();
83}
84
85void QSmoothedAnimation::delayedStop()
86{
87 if (!delayedStopTimer.isActive())
88 delayedStopTimer.start();
89}
90
91int QSmoothedAnimation::duration() const
92{
93 return -1;
94}
95
96bool 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
163qreal 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
187void 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
198void 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 ((!invert) == ((initialValue - to) > 0));
218
219 if (hasReversed) {
220 switch (reversingMode) {
221 default:
222 case QDeclarativeSmoothedAnimation::Eased:
223 initialVelocity = -trackVelocity;
224 break;
225 case QDeclarativeSmoothedAnimation::Sync:
226 QDeclarativePropertyPrivate::write(target, to,
227 QDeclarativePropertyPrivate::BypassInterceptor
228 | QDeclarativePropertyPrivate::DontRemoveBinding);
229 trackVelocity = 0;
230 stop();
231 return;
232 case QDeclarativeSmoothedAnimation::Immediate:
233 initialVelocity = 0;
234 break;
235 }
236 }
237
238 trackVelocity = initialVelocity;
239
240 invert = (to < initialValue);
241
242 if (!recalc()) {
243 QDeclarativePropertyPrivate::write(target, to,
244 QDeclarativePropertyPrivate::BypassInterceptor
245 | QDeclarativePropertyPrivate::DontRemoveBinding);
246 stop();
247 return;
248 }
249}
250
251/*!
252 \qmlclass SmoothedAnimation QDeclarativeSmoothedAnimation
253 \ingroup qml-animation-transition
254 \since 4.7
255 \inherits NumberAnimation
256 \brief The SmoothedAnimation element allows a property to smoothly track a value.
257
258 A SmoothedAnimation animates a property's value to a set target value
259 using an ease in/out quad easing curve. When the target value changes,
260 the easing curves used to animate between the old and new target values
261 are smoothly spliced together to create a smooth movement to the new
262 target value that maintains the current velocity.
263
264 The follow example shows one \l Rectangle tracking the position of another
265 using SmoothedAnimation. The green rectangle's \c x and \c y values are
266 bound to those of the red rectangle. Whenever these values change, the
267 green rectangle smoothly animates to its new position:
268
269 \snippet doc/src/snippets/declarative/smoothedanimation.qml 0
270
271 A SmoothedAnimation can be configured by setting the \l velocity at which the
272 animation should occur, or the \l duration that the animation should take.
273 If both the \l velocity and \l duration are specified, the one that results in
274 the quickest animation is chosen for each change in the target value.
275
276 For example, animating from 0 to 800 will take 4 seconds if a velocity
277 of 200 is set, will take 8 seconds with a duration of 8000 set, and will
278 take 4 seconds with both a velocity of 200 and a duration of 8000 set.
279 Animating from 0 to 20000 will take 10 seconds if a velocity of 200 is set,
280 will take 8 seconds with a duration of 8000 set, and will take 8 seconds
281 with both a velocity of 200 and a duration of 8000 set.
282
283 The default velocity of SmoothedAnimation is 200 units/second. Note that if the range of the
284 value being animated is small, then the velocity will need to be adjusted
285 appropriately. For example, the opacity of an item ranges from 0 - 1.0.
286 To enable a smooth animation in this range the velocity will need to be
287 set to a value such as 0.5 units/second. Animating from 0 to 1.0 with a velocity
288 of 0.5 will take 2000 ms to complete.
289
290 Like any other animation element, a SmoothedAnimation can be applied in a
291 number of ways, including transitions, behaviors and property value
292 sources. The \l {QML Animation} documentation shows a variety of methods
293 for creating animations.
294
295 \sa SpringAnimation, NumberAnimation, {QML Animation}, {declarative/animation/basics}{Animation basics example}
296*/
297
298QDeclarativeSmoothedAnimation::QDeclarativeSmoothedAnimation(QObject *parent)
299: QDeclarativeNumberAnimation(*(new QDeclarativeSmoothedAnimationPrivate), parent)
300{
301}
302
303QDeclarativeSmoothedAnimation::~QDeclarativeSmoothedAnimation()
304{
305}
306
307QDeclarativeSmoothedAnimationPrivate::QDeclarativeSmoothedAnimationPrivate()
308 : wrapperGroup(new QParallelAnimationGroup), anim(new QSmoothedAnimation)
309{
310 Q_Q(QDeclarativeSmoothedAnimation);
311 QDeclarative_setParent_noEvent(wrapperGroup, q);
312 QDeclarative_setParent_noEvent(anim, q);
313}
314
315void QDeclarativeSmoothedAnimationPrivate::updateRunningAnimations()
316{
317 foreach(QSmoothedAnimation* ease, activeAnimations.values()){
318 ease->maximumEasingTime = anim->maximumEasingTime;
319 ease->reversingMode = anim->reversingMode;
320 ease->velocity = anim->velocity;
321 ease->userDuration = anim->userDuration;
322 ease->init();
323 }
324}
325
326QAbstractAnimation* QDeclarativeSmoothedAnimation::qtAnimation()
327{
328 Q_D(QDeclarativeSmoothedAnimation);
329 return d->wrapperGroup;
330}
331
332void QDeclarativeSmoothedAnimation::transition(QDeclarativeStateActions &actions,
333 QDeclarativeProperties &modified,
334 TransitionDirection direction)
335{
336 Q_D(QDeclarativeSmoothedAnimation);
337 QDeclarativeNumberAnimation::transition(actions, modified, direction);
338
339 if (!d->actions)
340 return;
341
342 QSet<QAbstractAnimation*> anims;
343 for (int i = 0; i < d->actions->size(); i++) {
344 QSmoothedAnimation *ease;
345 bool needsRestart;
346 if (!d->activeAnimations.contains((*d->actions)[i].property)) {
347 ease = new QSmoothedAnimation();
348 d->wrapperGroup->addAnimation(ease);
349 d->activeAnimations.insert((*d->actions)[i].property, ease);
350 needsRestart = false;
351 } else {
352 ease = d->activeAnimations.value((*d->actions)[i].property);
353 needsRestart = true;
354 }
355 ease->target = (*d->actions)[i].property;
356 ease->to = (*d->actions)[i].toValue.toReal();
357
358 // copying public members from main value holder animation
359 ease->maximumEasingTime = d->anim->maximumEasingTime;
360 ease->reversingMode = d->anim->reversingMode;
361 ease->velocity = d->anim->velocity;
362 ease->userDuration = d->anim->userDuration;
363
364 ease->initialVelocity = ease->trackVelocity;
365
366 if (needsRestart)
367 ease->init();
368 anims.insert(ease);
369 }
370
371 for (int i = d->wrapperGroup->animationCount() - 1; i >= 0 ; --i) {
372 if (!anims.contains(d->wrapperGroup->animationAt(i))) {
373 QSmoothedAnimation *ease = static_cast<QSmoothedAnimation*>(d->wrapperGroup->animationAt(i));
374 d->activeAnimations.remove(ease->target);
375 d->wrapperGroup->takeAnimation(i);
376 delete ease;
377 }
378 }
379}
380
381/*!
382 \qmlproperty enumeration SmoothedAnimation::reversingMode
383
384 Sets how the SmoothedAnimation behaves if an animation direction is reversed.
385
386 Possible values are:
387
388 \list
389 \o SmoothedAnimation.Eased (default) - the animation will smoothly decelerate, and then reverse direction
390 \o SmoothedAnimation.Immediate - the animation will immediately begin accelerating in the reverse direction, beginning with a velocity of 0
391 \o SmoothedAnimation.Sync - the property is immediately set to the target value
392 \endlist
393*/
394QDeclarativeSmoothedAnimation::ReversingMode QDeclarativeSmoothedAnimation::reversingMode() const
395{
396 Q_D(const QDeclarativeSmoothedAnimation);
397 return (QDeclarativeSmoothedAnimation::ReversingMode) d->anim->reversingMode;
398}
399
400void QDeclarativeSmoothedAnimation::setReversingMode(ReversingMode m)
401{
402 Q_D(QDeclarativeSmoothedAnimation);
403 if (d->anim->reversingMode == m)
404 return;
405
406 d->anim->reversingMode = m;
407 emit reversingModeChanged();
408 d->updateRunningAnimations();
409}
410
411/*!
412 \qmlproperty int SmoothedAnimation::duration
413
414 This property holds the animation duration, in msecs, used when tracking the source.
415
416 Setting this to -1 (the default) disables the duration value.
417
418 If the velocity value and the duration value are both enabled, then the animation will
419 use whichever gives the shorter duration.
420*/
421int QDeclarativeSmoothedAnimation::duration() const
422{
423 Q_D(const QDeclarativeSmoothedAnimation);
424 return d->anim->userDuration;
425}
426
427void QDeclarativeSmoothedAnimation::setDuration(int duration)
428{
429 Q_D(QDeclarativeSmoothedAnimation);
430 if (duration != -1)
431 QDeclarativeNumberAnimation::setDuration(duration);
432 if(duration == d->anim->userDuration)
433 return;
434 d->anim->userDuration = duration;
435 d->updateRunningAnimations();
436}
437
438qreal QDeclarativeSmoothedAnimation::velocity() const
439{
440 Q_D(const QDeclarativeSmoothedAnimation);
441 return d->anim->velocity;
442}
443
444/*!
445 \qmlproperty real SmoothedAnimation::velocity
446
447 This property holds the average velocity allowed when tracking the 'to' value.
448
449 The default velocity of SmoothedAnimation is 200 units/second.
450
451 Setting this to -1 disables the velocity value.
452
453 If the velocity value and the duration value are both enabled, then the animation will
454 use whichever gives the shorter duration.
455*/
456void QDeclarativeSmoothedAnimation::setVelocity(qreal v)
457{
458 Q_D(QDeclarativeSmoothedAnimation);
459 if (d->anim->velocity == v)
460 return;
461
462 d->anim->velocity = v;
463 emit velocityChanged();
464 d->updateRunningAnimations();
465}
466
467/*!
468 \qmlproperty int SmoothedAnimation::maximumEasingTime
469
470 This property specifies the maximum time, in msecs, any "eases" during the follow should take.
471 Setting this property causes the velocity to "level out" after at a time. Setting
472 a negative value reverts to the normal mode of easing over the entire animation
473 duration.
474
475 The default value is -1.
476*/
477int QDeclarativeSmoothedAnimation::maximumEasingTime() const
478{
479 Q_D(const QDeclarativeSmoothedAnimation);
480 return d->anim->maximumEasingTime;
481}
482
483void QDeclarativeSmoothedAnimation::setMaximumEasingTime(int v)
484{
485 Q_D(QDeclarativeSmoothedAnimation);
486 if(v == d->anim->maximumEasingTime)
487 return;
488 d->anim->maximumEasingTime = v;
489 emit maximumEasingTimeChanged();
490 d->updateRunningAnimations();
491}
492
493QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.