Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qquickspringanimation.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5
7#include <private/qqmlproperty_p.h>
8#include "private/qcontinuinganimationgroupjob_p.h"
9
10#include <QtCore/qdebug.h>
11
12#include <private/qobject_p.h>
13
14#include <cmath>
15
16#define DELAY_STOP_TIMER_INTERVAL 32
17
19
22{
23 Q_DISABLE_COPY(QSpringAnimation)
24public:
26
28 int duration() const override;
29 void restart();
30 void init();
31
36 int dura;
39 enum Mode {
42 Spring
43 };
46
54
55 bool useMass : 1;
56 bool haveModulus : 1;
57 bool skipUpdate : 1;
58 typedef QHash<QQmlProperty, QSpringAnimation*> ActiveAnimationHash;
60
61 void clearTemplate() { animationTemplate = nullptr; }
62
63protected:
64 void updateCurrentTime(int time) override;
66 void debugAnimation(QDebug d) const override;
67
68private:
69 QQuickSpringAnimationPrivate *animationTemplate;
70};
71
106
109 , currentValue(0)
110 , to(0)
111 , velocity(0)
112 , startTime(0)
113 , dura(0)
114 , lastTime(0)
115 , stopTime(-1)
116 , mode(Track)
117 , velocityms(0)
118 , maxVelocity(0)
119 , mass(1.0)
120 , spring(0.)
121 , damping(0.)
122 , epsilon(0.01)
123 , modulus(0.0)
124 , useMass(false)
125 , haveModulus(false)
126 , skipUpdate(false)
127 , animationTemplate(priv)
128{
129}
130
132{
133 if (animationTemplate) {
134 if (target.object()) {
135 auto it = animationTemplate->activeAnimations.constFind(target);
136 if (it != animationTemplate->activeAnimations.cend() && it.value() == this)
137 animationTemplate->activeAnimations.erase(it);
138 } else {
139 //target is no longer valid, need to search linearly
140 for (auto it = animationTemplate->activeAnimations.cbegin(); it != animationTemplate->activeAnimations.cend(); ++it) {
141 if (it.value() == this) {
142 animationTemplate->activeAnimations.erase(it);
143 break;
144 }
145 }
146 }
147 }
148}
149
151{
152 return -1;
153}
154
156{
157 if (isRunning() || (stopTime != -1 && (animationTemplate->elapsed.elapsed() - stopTime) < DELAY_STOP_TIMER_INTERVAL)) {
158 skipUpdate = true;
159 init();
160 } else {
161 skipUpdate = false;
162 //init() will be triggered when group starts
163 }
164}
165
167{
168 lastTime = startTime = 0;
169 stopTime = -1;
170}
171
173{
174 if (skipUpdate) {
175 skipUpdate = false;
176 return;
177 }
178
179 if (mode == Track) {
180 stop();
181 return;
182 }
183
184 int elapsed = time - lastTime;
185
186 if (!elapsed)
187 return;
188
189 int count = elapsed / 16;
190
191 if (mode == Spring) {
192 if (elapsed < 16) // capped at 62fps.
193 return;
194 lastTime = time - (elapsed - count * 16);
195 } else {
196 lastTime = time;
197 }
198
199 qreal srcVal = to;
200
201 bool stopped = false;
202
203 if (haveModulus) {
205 srcVal = fmod(srcVal, modulus);
206 }
207 if (mode == Spring) {
208 // Real men solve the spring DEs using RK4.
209 // We'll do something much simpler which gives a result that looks fine.
210 for (int i = 0; i < count; ++i) {
211 qreal diff = srcVal - currentValue;
212 if (haveModulus && qAbs(diff) > modulus / 2) {
213 if (diff < 0)
214 diff += modulus;
215 else
216 diff -= modulus;
217 }
218 if (useMass)
219 velocity = velocity + (spring * diff - damping * velocity) / mass;
220 else
221 velocity = velocity + spring * diff - damping * velocity;
222 if (maxVelocity > 0.) {
223 // limit velocity
224 if (velocity > maxVelocity)
226 else if (velocity < -maxVelocity)
228 }
229 currentValue += velocity * 16.0 / 1000.0;
230 if (haveModulus) {
232 if (currentValue < 0.0)
234 }
235 }
236 if (qAbs(velocity) < epsilon && qAbs(srcVal - currentValue) < epsilon) {
237 velocity = 0.0;
238 currentValue = srcVal;
239 stopped = true;
240 }
241 } else {
242 qreal moveBy = elapsed * velocityms;
243 qreal diff = srcVal - currentValue;
244 if (haveModulus && qAbs(diff) > modulus / 2) {
245 if (diff < 0)
246 diff += modulus;
247 else
248 diff -= modulus;
249 }
250 if (diff > 0) {
251 currentValue += moveBy;
252 if (haveModulus)
253 currentValue = std::fmod(currentValue, modulus);
254 } else {
255 currentValue -= moveBy;
256 if (haveModulus && currentValue < 0.0)
258 }
259 if (lastTime - startTime >= dura) {
261 stopped = true;
262 }
263 }
264
265 qreal old_to = to;
266
270
271 if (stopped && old_to == to) { // do not stop if we got restarted
272 if (animationTemplate)
273 stopTime = animationTemplate->elapsed.elapsed();
274 stop();
275 }
276}
277
283
285{
286 d << "SpringAnimationJob(" << Qt::hex << (const void *) this << Qt::dec << ")" << "velocity:" << maxVelocity
287 << "spring:" << spring << "damping:" << damping << "epsilon:" << epsilon << "modulus:" << modulus
288 << "mass:" << mass << "target:" << target.object() << "property:" << target.name()
289 << "to:" << to << "current velocity:" << velocity;
290}
291
292
294{
295 if (spring == 0. && maxVelocity == 0.)
297 else if (spring > 0.)
299 else {
303 animation->startTime = animation->lastTime;
305 if (haveModulus && dist > modulus / 2)
306 dist = modulus - fmod(dist, modulus);
307 animation->dura = dist / velocityms;
308 }
309 }
310}
311
345
347{
349 for (QSpringAnimation::ActiveAnimationHashIt it = d->activeAnimations.begin(), end = d->activeAnimations.end(); it != end; ++it)
350 it.value()->clearTemplate();
351}
352
362{
363 Q_D(const QQuickSpringAnimation);
364 return d->maxVelocity;
365}
366
368{
370 d->maxVelocity = velocity;
371 d->velocityms = velocity / 1000.0;
372 d->updateMode();
373}
374
387{
388 Q_D(const QQuickSpringAnimation);
389 return d->spring;
390}
391
393{
395 d->spring = spring;
396 d->updateMode();
397}
398
410{
411 Q_D(const QQuickSpringAnimation);
412 return d->damping;
413}
414
416{
418 if (damping > 1.)
419 damping = 1.;
420
421 d->damping = damping;
422}
423
424
436{
437 Q_D(const QQuickSpringAnimation);
438 return d->epsilon;
439}
440
446
455{
456 Q_D(const QQuickSpringAnimation);
457 return d->modulus;
458}
459
461{
463 if (d->modulus != modulus) {
464 d->haveModulus = modulus != 0.0;
465 d->modulus = modulus;
466 d->updateMode();
468 }
469}
470
481{
482 Q_D(const QQuickSpringAnimation);
483 return d->mass;
484}
485
487{
489 if (d->mass != mass && mass > 0.0) {
490 d->useMass = mass != 1.0;
491 d->mass = mass;
493 }
494}
495
497 QQmlProperties &modified,
499 QObject *defaultTarget)
500{
503
505
506 QQuickStateActions dataActions = QQuickNumberAnimation::createTransitionActions(actions, modified, defaultTarget);
507 if (!dataActions.isEmpty()) {
508 QSet<QAbstractAnimationJob*> anims;
509 for (int i = 0; i < dataActions.size(); ++i) {
511 bool needsRestart = false;
512 const QQmlProperty &property = dataActions.at(i).property;
513 if (d->activeAnimations.contains(property)) {
514 animation = d->activeAnimations[property];
515 needsRestart = true;
516 } else {
518 d->activeAnimations.insert(property, animation);
519 animation->target = property;
520 }
521 wrapperGroup->appendAnimation(initInstance(animation));
522
523 animation->to = dataActions.at(i).toValue.toReal();
524 animation->startTime = 0;
525 animation->velocityms = d->velocityms;
526 animation->mass = d->mass;
527 animation->spring = d->spring;
528 animation->damping = d->damping;
529 animation->epsilon = d->epsilon;
530 animation->modulus = d->modulus;
531 animation->useMass = d->useMass;
532 animation->haveModulus = d->haveModulus;
533 animation->mode = d->mode;
534 animation->dura = -1;
535 animation->maxVelocity = d->maxVelocity;
536
537 if (d->fromIsDefined)
538 animation->currentValue = dataActions.at(i).fromValue.toReal();
539 else
540 animation->currentValue = property.read().toReal();
543 if (d->haveModulus && dist > d->modulus / 2)
544 dist = d->modulus - fmod(dist, d->modulus);
545 animation->dura = dist / animation->velocityms;
546 }
547
548 if (needsRestart)
549 animation->restart();
550 anims.insert(animation);
551 }
552 const auto copy = d->activeAnimations;
553 for (QSpringAnimation *anim : copy) {
554 if (!anims.contains(anim)) {
555 anim->clearTemplate();
556 d->activeAnimations.remove(anim->target);
557 }
558 }
559 }
560 return wrapperGroup;
561}
562
564
565#include "moc_qquickspringanimation_p.cpp"
virtual void debugAnimation(QDebug d) const
virtual void updateState(QAbstractAnimationJob::State newState, QAbstractAnimationJob::State oldState)
virtual void updateCurrentTime(int)
\inmodule QtCore
\inmodule QtCore
qint64 elapsed() const noexcept
Returns the number of milliseconds since this QElapsedTimer was last started.
void start() noexcept
\typealias QElapsedTimer::Duration Synonym for std::chrono::nanoseconds.
iterator begin()
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the first item in the hash.
Definition qhash.h:1212
const_iterator cbegin() const noexcept
Definition qhash.h:1214
const_iterator constFind(const Key &key) const noexcept
Definition qhash.h:1299
iterator Iterator
Qt-style synonym for QHash::iterator.
Definition qhash.h:1288
iterator erase(const_iterator it)
Definition qhash.h:1233
iterator end() noexcept
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the last ...
Definition qhash.h:1216
const_iterator cend() const noexcept
Definition qhash.h:1218
\inmodule QtCore
Definition qobject.h:103
static bool write(QObject *, const QQmlPropertyData &, const QVariant &, const QQmlRefPointer< QQmlContextData > &, QQmlPropertyData::WriteFlags flags={})
The QQmlProperty class abstracts accessing properties on objects created from QML.
QMetaProperty property() const
Returns the \l{QMetaProperty} {Qt property} associated with this QML property.
QAbstractAnimationJob * initInstance(QAbstractAnimationJob *animation)
QQuickStateActions createTransitionActions(QQuickStateActions &actions, QQmlProperties &modified, QObject *defaultTarget=nullptr)
QSpringAnimation::ActiveAnimationHash activeAnimations
QQuickSpringAnimation(QObject *parent=nullptr)
\qmltype SpringAnimation \instantiates QQuickSpringAnimation \inqmlmodule QtQuick\inherits NumberAnim...
void setVelocity(qreal velocity)
QAbstractAnimationJob * transition(QQuickStateActions &actions, QQmlProperties &modified, TransitionDirection direction, QObject *defaultTarget=nullptr) override
iterator begin()
Definition qset.h:136
QSpringAnimation(QQuickSpringAnimationPrivate *=nullptr)
int duration() const override
void updateCurrentTime(int time) override
void updateState(QAbstractAnimationJob::State, QAbstractAnimationJob::State) override
void debugAnimation(QDebug d) const override
QHash< QQmlProperty, QSpringAnimation * > ActiveAnimationHash
ActiveAnimationHash::Iterator ActiveAnimationHashIt
QVariant currentValue
the current value of the animation.
qreal toReal(bool *ok=nullptr) const
Returns the variant as a qreal if the variant has userType() \l QMetaType::Double,...
static auto fromValue(T &&value) noexcept(std::is_nothrow_copy_constructible_v< T > &&Private::CanUseInternalSpace< T >) -> std::enable_if_t< std::conjunction_v< std::is_copy_constructible< T >, std::is_destructible< T > >, QVariant >
Definition qvariant.h:536
QSet< QString >::iterator it
direction
void newState(QList< State > &states, const char *token, const char *lexem, bool pre)
Combined button and popup list for selecting options.
QTextStream & hex(QTextStream &stream)
Calls QTextStream::setIntegerBase(16) on stream and returns stream.
QTextStream & dec(QTextStream &stream)
Calls QTextStream::setIntegerBase(10) on stream and returns stream.
static jboolean copy(JNIEnv *, jobject)
qint64 startTime
static const QMetaObjectPrivate * priv(const uint *data)
constexpr T qAbs(const T &t)
Definition qnumeric.h:328
GLenum mode
GLuint GLuint end
GLenum GLenum GLsizei count
GLenum target
static const qreal epsilon
#define DELAY_STOP_TIMER_INTERVAL
static double elapsed(qint64 after, qint64 before)
#define Q_AUTOTEST_EXPORT
static QT_BEGIN_NAMESPACE void init(QTextBoundaryFinder::BoundaryType type, QStringView str, QCharAttributes *attributes)
#define emit
#define Q_UNUSED(x)
double qreal
Definition qtypes.h:187
std::uniform_real_distribution dist(1, 2.5)
[2]
QPropertyAnimation animation
[0]