|
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/qdeclarativespringanimation_p.h" |
|
43 |
|
44 #include "private/qdeclarativeanimation_p_p.h" |
|
45 #include <qdeclarativeproperty_p.h> |
|
46 |
|
47 #include <QtCore/qdebug.h> |
|
48 |
|
49 #include <private/qobject_p.h> |
|
50 |
|
51 #include <limits.h> |
|
52 #include <math.h> |
|
53 |
|
54 QT_BEGIN_NAMESPACE |
|
55 |
|
56 |
|
57 |
|
58 class QDeclarativeSpringAnimationPrivate : public QDeclarativeAbstractAnimationPrivate |
|
59 { |
|
60 Q_DECLARE_PUBLIC(QDeclarativeSpringAnimation) |
|
61 public: |
|
62 QDeclarativeSpringAnimationPrivate() |
|
63 : currentValue(0), to(0), from(0), maxVelocity(0), lastTime(0) |
|
64 , mass(1.0), spring(0.), damping(0.), velocity(0), epsilon(0.01) |
|
65 , modulus(0.0), useMass(false), haveModulus(false), enabled(true) |
|
66 , fromDefined(false), toDefined(false) |
|
67 , mode(Track), clock(this) {} |
|
68 |
|
69 qreal currentValue; |
|
70 qreal to; |
|
71 qreal from; |
|
72 qreal maxVelocity; |
|
73 qreal velocityms; |
|
74 int lastTime; |
|
75 qreal mass; |
|
76 qreal spring; |
|
77 qreal damping; |
|
78 qreal velocity; |
|
79 qreal epsilon; |
|
80 qreal modulus; |
|
81 |
|
82 bool useMass : 1; |
|
83 bool haveModulus : 1; |
|
84 bool enabled : 1; |
|
85 bool fromDefined : 1; |
|
86 bool toDefined : 1; |
|
87 |
|
88 enum Mode { |
|
89 Track, |
|
90 Velocity, |
|
91 Spring |
|
92 }; |
|
93 Mode mode; |
|
94 |
|
95 void tick(int); |
|
96 void updateMode(); |
|
97 |
|
98 QTickAnimationProxy<QDeclarativeSpringAnimationPrivate, &QDeclarativeSpringAnimationPrivate::tick> clock; |
|
99 }; |
|
100 |
|
101 void QDeclarativeSpringAnimationPrivate::tick(int time) |
|
102 { |
|
103 if (mode == Track) { |
|
104 clock.stop(); |
|
105 return; |
|
106 } |
|
107 |
|
108 int elapsed = time - lastTime; |
|
109 if (!elapsed) |
|
110 return; |
|
111 qreal srcVal = to; |
|
112 |
|
113 bool stop = false; |
|
114 |
|
115 if (haveModulus) { |
|
116 currentValue = fmod(currentValue, modulus); |
|
117 srcVal = fmod(srcVal, modulus); |
|
118 } |
|
119 if (mode == Spring) { |
|
120 if (elapsed < 16) // capped at 62fps. |
|
121 return; |
|
122 // Real men solve the spring DEs using RK4. |
|
123 // We'll do something much simpler which gives a result that looks fine. |
|
124 int count = elapsed / 16; |
|
125 for (int i = 0; i < count; ++i) { |
|
126 qreal diff = srcVal - currentValue; |
|
127 if (haveModulus && qAbs(diff) > modulus / 2) { |
|
128 if (diff < 0) |
|
129 diff += modulus; |
|
130 else |
|
131 diff -= modulus; |
|
132 } |
|
133 if (useMass) |
|
134 velocity = velocity + (spring * diff - damping * velocity) / mass; |
|
135 else |
|
136 velocity = velocity + spring * diff - damping * velocity; |
|
137 if (maxVelocity > 0.) { |
|
138 // limit velocity |
|
139 if (velocity > maxVelocity) |
|
140 velocity = maxVelocity; |
|
141 else if (velocity < -maxVelocity) |
|
142 velocity = -maxVelocity; |
|
143 } |
|
144 currentValue += velocity * 16.0 / 1000.0; |
|
145 if (haveModulus) { |
|
146 currentValue = fmod(currentValue, modulus); |
|
147 if (currentValue < 0.0) |
|
148 currentValue += modulus; |
|
149 } |
|
150 } |
|
151 if (qAbs(velocity) < epsilon && qAbs(srcVal - currentValue) < epsilon) { |
|
152 velocity = 0.0; |
|
153 currentValue = srcVal; |
|
154 stop = true; |
|
155 } |
|
156 lastTime = time - (elapsed - count * 16); |
|
157 } else { |
|
158 qreal moveBy = elapsed * velocityms; |
|
159 qreal diff = srcVal - currentValue; |
|
160 if (haveModulus && qAbs(diff) > modulus / 2) { |
|
161 if (diff < 0) |
|
162 diff += modulus; |
|
163 else |
|
164 diff -= modulus; |
|
165 } |
|
166 if (diff > 0) { |
|
167 currentValue += moveBy; |
|
168 if (haveModulus) |
|
169 currentValue = fmod(currentValue, modulus); |
|
170 if (currentValue > to) { |
|
171 currentValue = to; |
|
172 stop = true; |
|
173 } |
|
174 } else { |
|
175 currentValue -= moveBy; |
|
176 if (haveModulus && currentValue < 0.0) |
|
177 currentValue = fmod(currentValue, modulus) + modulus; |
|
178 if (currentValue < to) { |
|
179 currentValue = to; |
|
180 stop = true; |
|
181 } |
|
182 } |
|
183 lastTime = time; |
|
184 } |
|
185 |
|
186 qreal old_to = to; |
|
187 |
|
188 QDeclarativePropertyPrivate::write(defaultProperty, currentValue, |
|
189 QDeclarativePropertyPrivate::BypassInterceptor | |
|
190 QDeclarativePropertyPrivate::DontRemoveBinding); |
|
191 |
|
192 if (stop && old_to == to) // do not stop if we got restarted |
|
193 clock.stop(); |
|
194 } |
|
195 |
|
196 void QDeclarativeSpringAnimationPrivate::updateMode() |
|
197 { |
|
198 if (spring == 0. && maxVelocity == 0.) |
|
199 mode = Track; |
|
200 else if (spring > 0.) |
|
201 mode = Spring; |
|
202 else |
|
203 mode = Velocity; |
|
204 } |
|
205 |
|
206 /*! |
|
207 \qmlclass SpringAnimation QDeclarativeSpringAnimation |
|
208 \since 4.7 |
|
209 |
|
210 \brief The SpringAnimation element allows a property to track a value in a spring-like motion. |
|
211 |
|
212 SpringAnimation mimics the oscillatory behavior of a spring, with the appropriate \l spring constant to |
|
213 control the acceleration and the \l damping to control how quickly the effect dies away. |
|
214 |
|
215 You can also limit the maximum \l velocity of the animation. |
|
216 |
|
217 The following \l Rectangle moves to the position of the mouse using a |
|
218 SpringAnimation when the mouse is clicked. The use of the \l Behavior |
|
219 on the \c x and \c y values indicates that whenever these values are |
|
220 changed, a SpringAnimation should be applied. |
|
221 |
|
222 \snippet doc/src/snippets/declarative/springanimation.qml 0 |
|
223 |
|
224 Like any other animation element, a SpringAnimation can be applied in a |
|
225 number of ways, including transitions, behaviors and property value |
|
226 sources. The \l PropertyAnimation documentation shows a variety of methods |
|
227 for creating animations. |
|
228 |
|
229 \sa SmoothedAnimation, {QML Animation}, {declarative/animation/basics}{Animation basics example}, {declarative/toys/clocks}{Clocks example} |
|
230 */ |
|
231 |
|
232 QDeclarativeSpringAnimation::QDeclarativeSpringAnimation(QObject *parent) |
|
233 : QDeclarativeAbstractAnimation(*(new QDeclarativeSpringAnimationPrivate),parent) |
|
234 { |
|
235 } |
|
236 |
|
237 QDeclarativeSpringAnimation::~QDeclarativeSpringAnimation() |
|
238 { |
|
239 } |
|
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 |
|
306 /*! |
|
307 \qmlproperty real SpringAnimation::velocity |
|
308 |
|
309 This property holds the maximum velocity allowed when tracking the source. |
|
310 |
|
311 The default value is 0 (no maximum velocity). |
|
312 */ |
|
313 |
|
314 qreal QDeclarativeSpringAnimation::velocity() const |
|
315 { |
|
316 Q_D(const QDeclarativeSpringAnimation); |
|
317 return d->maxVelocity; |
|
318 } |
|
319 |
|
320 void QDeclarativeSpringAnimation::setVelocity(qreal velocity) |
|
321 { |
|
322 Q_D(QDeclarativeSpringAnimation); |
|
323 d->maxVelocity = velocity; |
|
324 d->velocityms = velocity / 1000.0; |
|
325 d->updateMode(); |
|
326 } |
|
327 |
|
328 /*! |
|
329 \qmlproperty real SpringAnimation::spring |
|
330 |
|
331 This property describes how strongly the target is pulled towards the |
|
332 source. The default value is 0 (that is, the spring-like motion is disabled). |
|
333 |
|
334 The useful value range is 0 - 5.0. |
|
335 |
|
336 When this property is set and the \l velocity value is greater than 0, |
|
337 the \l velocity limits the maximum speed. |
|
338 */ |
|
339 qreal QDeclarativeSpringAnimation::spring() const |
|
340 { |
|
341 Q_D(const QDeclarativeSpringAnimation); |
|
342 return d->spring; |
|
343 } |
|
344 |
|
345 void QDeclarativeSpringAnimation::setSpring(qreal spring) |
|
346 { |
|
347 Q_D(QDeclarativeSpringAnimation); |
|
348 d->spring = spring; |
|
349 d->updateMode(); |
|
350 } |
|
351 |
|
352 /*! |
|
353 \qmlproperty real SpringAnimation::damping |
|
354 This property holds the spring damping value. |
|
355 |
|
356 This value describes how quickly the spring-like motion comes to rest. |
|
357 The default value is 0. |
|
358 |
|
359 The useful value range is 0 - 1.0. The lower the value, the faster it |
|
360 comes to rest. |
|
361 */ |
|
362 qreal QDeclarativeSpringAnimation::damping() const |
|
363 { |
|
364 Q_D(const QDeclarativeSpringAnimation); |
|
365 return d->damping; |
|
366 } |
|
367 |
|
368 void QDeclarativeSpringAnimation::setDamping(qreal damping) |
|
369 { |
|
370 Q_D(QDeclarativeSpringAnimation); |
|
371 if (damping > 1.) |
|
372 damping = 1.; |
|
373 |
|
374 d->damping = damping; |
|
375 } |
|
376 |
|
377 |
|
378 /*! |
|
379 \qmlproperty real SpringAnimation::epsilon |
|
380 This property holds the spring epsilon. |
|
381 |
|
382 The epsilon is the rate and amount of change in the value which is close enough |
|
383 to 0 to be considered equal to zero. This will depend on the usage of the value. |
|
384 For pixel positions, 0.25 would suffice. For scale, 0.005 will suffice. |
|
385 |
|
386 The default is 0.01. Tuning this value can provide small performance improvements. |
|
387 */ |
|
388 qreal QDeclarativeSpringAnimation::epsilon() const |
|
389 { |
|
390 Q_D(const QDeclarativeSpringAnimation); |
|
391 return d->epsilon; |
|
392 } |
|
393 |
|
394 void QDeclarativeSpringAnimation::setEpsilon(qreal epsilon) |
|
395 { |
|
396 Q_D(QDeclarativeSpringAnimation); |
|
397 d->epsilon = epsilon; |
|
398 } |
|
399 |
|
400 /*! |
|
401 \qmlproperty real SpringAnimation::modulus |
|
402 This property holds the modulus value. The default value is 0. |
|
403 |
|
404 Setting a \a modulus forces the target value to "wrap around" at the modulus. |
|
405 For example, setting the modulus to 360 will cause a value of 370 to wrap around to 10. |
|
406 */ |
|
407 qreal QDeclarativeSpringAnimation::modulus() const |
|
408 { |
|
409 Q_D(const QDeclarativeSpringAnimation); |
|
410 return d->modulus; |
|
411 } |
|
412 |
|
413 void QDeclarativeSpringAnimation::setModulus(qreal modulus) |
|
414 { |
|
415 Q_D(QDeclarativeSpringAnimation); |
|
416 if (d->modulus != modulus) { |
|
417 d->haveModulus = modulus != 0.0; |
|
418 d->modulus = modulus; |
|
419 emit modulusChanged(); |
|
420 } |
|
421 } |
|
422 |
|
423 /*! |
|
424 \qmlproperty real SpringAnimation::mass |
|
425 This property holds the "mass" of the property being moved. |
|
426 |
|
427 The value is 1.0 by default. |
|
428 |
|
429 A greater mass causes slower movement and a greater spring-like |
|
430 motion when an item comes to rest. |
|
431 */ |
|
432 qreal QDeclarativeSpringAnimation::mass() const |
|
433 { |
|
434 Q_D(const QDeclarativeSpringAnimation); |
|
435 return d->mass; |
|
436 } |
|
437 |
|
438 void QDeclarativeSpringAnimation::setMass(qreal mass) |
|
439 { |
|
440 Q_D(QDeclarativeSpringAnimation); |
|
441 if (d->mass != mass && mass > 0.0) { |
|
442 d->useMass = mass != 1.0; |
|
443 d->mass = mass; |
|
444 emit massChanged(); |
|
445 } |
|
446 } |
|
447 |
|
448 void QDeclarativeSpringAnimation::transition(QDeclarativeStateActions &actions, |
|
449 QDeclarativeProperties &modified, |
|
450 TransitionDirection direction) |
|
451 { |
|
452 Q_D(QDeclarativeSpringAnimation); |
|
453 Q_UNUSED(direction); |
|
454 |
|
455 if (d->clock.state() != QAbstractAnimation::Running) |
|
456 d->lastTime = 0; |
|
457 |
|
458 if (!actions.isEmpty()) { |
|
459 for (int i = 0; i < actions.size(); ++i) { |
|
460 if (!d->toDefined) |
|
461 d->to = actions.at(i).toValue.toReal(); |
|
462 if (!d->fromDefined) |
|
463 d->currentValue = actions.at(i).fromValue.toReal(); |
|
464 if (d->mode != QDeclarativeSpringAnimationPrivate::Track) |
|
465 modified << d->defaultProperty; |
|
466 } |
|
467 } |
|
468 } |
|
469 |
|
470 |
|
471 QAbstractAnimation *QDeclarativeSpringAnimation::qtAnimation() |
|
472 { |
|
473 Q_D(QDeclarativeSpringAnimation); |
|
474 return &d->clock; |
|
475 } |
|
476 |
|
477 QT_END_NAMESPACE |