Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qgraphicsitemanimation.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
4/*!
5 \class QGraphicsItemAnimation
6 \brief The QGraphicsItemAnimation class provides simple animation
7 support for QGraphicsItem.
8 \since 4.2
9 \ingroup graphicsview-api
10 \inmodule QtWidgets
11 \deprecated
12
13 The QGraphicsItemAnimation class animates a QGraphicsItem. You can
14 schedule changes to the item's transformation matrix at
15 specified steps. The QGraphicsItemAnimation class has a
16 current step value. When this value changes the transformations
17 scheduled at that step are performed. The current step of the
18 animation is set with the \c setStep() function.
19
20 QGraphicsItemAnimation will do a simple linear interpolation
21 between the nearest adjacent scheduled changes to calculate the
22 matrix. For instance, if you set the position of an item at values
23 0.0 and 1.0, the animation will show the item moving in a straight
24 line between these positions. The same is true for scaling and
25 rotation.
26
27 It is usual to use the class with a QTimeLine. The timeline's
28 \l{QTimeLine::}{valueChanged()} signal is then connected to the
29 \c setStep() slot. For example, you can set up an item for rotation
30 by calling \c setRotationAt() for different step values.
31 The animations timeline is set with the setTimeLine() function.
32
33 An example animation with a timeline follows:
34
35 \snippet timeline/main.cpp 0
36
37 Note that steps lie between 0.0 and 1.0. It may be necessary to use
38 \l{QTimeLine::}{setUpdateInterval()}. The default update interval
39 is 40 ms. A scheduled transformation cannot be removed when set,
40 so scheduling several transformations of the same kind (e.g.,
41 rotations) at the same step is not recommended.
42
43 \sa QTimeLine, {Graphics View Framework}
44*/
45
47
48#include "qgraphicsitem.h"
49
50#include <QtCore/qtimeline.h>
51#include <QtCore/qpoint.h>
52#include <QtCore/qpointer.h>
53
54#include <algorithm>
55
57
58static inline bool check_step_valid(qreal step, const char *method)
59{
60 if (!(step >= 0 && step <= 1)) {
61 qWarning("QGraphicsItemAnimation::%s: invalid step = %f", method, step);
62 return false;
63 }
64 return true;
65}
66
68{
69public:
71 : q(nullptr), timeLine(nullptr), item(nullptr), step(0)
72 { }
73
75
77 QGraphicsItem *item;
78
80 QTransform startTransform;
81
83
84 struct Pair {
85 bool operator <(const Pair &other) const
86 { return step < other.step; }
87 bool operator==(const Pair &other) const
88 { return step == other.step; }
91 };
101
102 qreal linearValueForStep(qreal step, const QList<Pair> &source, qreal defaultValue = 0);
103 void insertUniquePair(qreal step, qreal value, QList<Pair> *binList, const char *method);
104};
106
107qreal QGraphicsItemAnimationPrivate::linearValueForStep(qreal step, const QList<Pair> &source,
108 qreal defaultValue)
109{
110 if (source.isEmpty())
111 return defaultValue;
112 step = qMin<qreal>(qMax<qreal>(step, 0), 1);
113
114 if (step == 1)
115 return source.back().value;
116
117 qreal stepBefore = 0;
118 qreal stepAfter = 1;
119 qreal valueBefore = source.front().step == 0 ? source.front().value : defaultValue;
120 qreal valueAfter = source.back().value;
121
122 // Find the closest step and value before the given step.
123 for (int i = 0; i < source.size() && step >= source[i].step; ++i) {
124 stepBefore = source[i].step;
125 valueBefore = source[i].value;
126 }
127
128 // Find the closest step and value after the given step.
129 for (int i = source.size() - 1; i >= 0 && step < source[i].step; --i) {
130 stepAfter = source[i].step;
131 valueAfter = source[i].value;
132 }
133
134 // Do a simple linear interpolation.
135 return valueBefore + (valueAfter - valueBefore) * ((step - stepBefore) / (stepAfter - stepBefore));
136}
137
138void QGraphicsItemAnimationPrivate::insertUniquePair(qreal step, qreal value, QList<Pair> *binList,
139 const char *method)
140{
141 if (!check_step_valid(step, method))
142 return;
143
144 const Pair pair = { step, value };
145
146 const QList<Pair>::iterator result = std::lower_bound(binList->begin(), binList->end(), pair);
147 if (result == binList->end() || pair < *result)
148 binList->insert(result, pair);
149 else
150 result->value = value;
151}
152
153/*!
154 Constructs an animation object with the given \a parent.
155*/
156QGraphicsItemAnimation::QGraphicsItemAnimation(QObject *parent)
157 : QObject(parent), d(new QGraphicsItemAnimationPrivate)
158{
159 d->q = this;
160}
161
162/*!
163 Destroys the animation object.
164*/
165QGraphicsItemAnimation::~QGraphicsItemAnimation()
166{
167 delete d;
168}
169
170/*!
171 Returns the item on which the animation object operates.
172
173 \sa setItem()
174*/
175QGraphicsItem *QGraphicsItemAnimation::item() const
176{
177 return d->item;
178}
179
180/*!
181 Sets the specified \a item to be used in the animation.
182
183 \sa item()
184*/
185void QGraphicsItemAnimation::setItem(QGraphicsItem *item)
186{
187 d->item = item;
188 d->startPos = d->item->pos();
189}
190
191/*!
192 Returns the timeline object used to control the rate at which the animation
193 occurs.
194
195 \sa setTimeLine()
196*/
197QTimeLine *QGraphicsItemAnimation::timeLine() const
198{
199 return d->timeLine;
200}
201
202/*!
203 Sets the timeline object used to control the rate of animation to the \a timeLine
204 specified.
205
206 \sa timeLine()
207*/
208void QGraphicsItemAnimation::setTimeLine(QTimeLine *timeLine)
209{
210 if (d->timeLine == timeLine)
211 return;
212 if (d->timeLine)
213 delete d->timeLine;
214 if (!timeLine)
215 return;
216 d->timeLine = timeLine;
217 connect(timeLine, SIGNAL(valueChanged(qreal)), this, SLOT(setStep(qreal)));
218}
219
220/*!
221 Returns the position of the item at the given \a step value.
222
223 \sa setPosAt()
224*/
225QPointF QGraphicsItemAnimation::posAt(qreal step) const
226{
227 check_step_valid(step, "posAt");
228 return QPointF(d->linearValueForStep(step, d->xPosition, d->startPos.x()),
229 d->linearValueForStep(step, d->yPosition, d->startPos.y()));
230}
231
232/*!
233 \fn void QGraphicsItemAnimation::setPosAt(qreal step, const QPointF &point)
234
235 Sets the position of the item at the given \a step value to the \a point specified.
236
237 \sa posAt()
238*/
239void QGraphicsItemAnimation::setPosAt(qreal step, const QPointF &pos)
240{
241 d->insertUniquePair(step, pos.x(), &d->xPosition, "setPosAt");
242 d->insertUniquePair(step, pos.y(), &d->yPosition, "setPosAt");
243}
244
245/*!
246 Returns all explicitly inserted positions.
247
248 \sa posAt(), setPosAt()
249*/
250QList<std::pair<qreal, QPointF> > QGraphicsItemAnimation::posList() const
251{
252 QList<std::pair<qreal, QPointF>> list;
253 const int xPosCount = d->xPosition.size();
254 list.reserve(xPosCount);
255 for (int i = 0; i < xPosCount; ++i)
256 list.emplace_back(d->xPosition.at(i).step,
257 QPointF(d->xPosition.at(i).value, d->yPosition.at(i).value));
258
259 return list;
260}
261
262/*!
263 Returns the transform used for the item at the specified \a step value.
264
265 \since 5.14
266*/
267QTransform QGraphicsItemAnimation::transformAt(qreal step) const
268{
269 check_step_valid(step, "transformAt");
270
271 QTransform transform;
272 if (!d->rotation.isEmpty())
273 transform.rotate(rotationAt(step));
274 if (!d->verticalScale.isEmpty())
275 transform.scale(horizontalScaleAt(step), verticalScaleAt(step));
276 if (!d->verticalShear.isEmpty())
277 transform.shear(horizontalShearAt(step), verticalShearAt(step));
278 if (!d->xTranslation.isEmpty())
279 transform.translate(xTranslationAt(step), yTranslationAt(step));
280 return transform;
281}
282
283/*!
284 Returns the angle at which the item is rotated at the specified \a step value.
285
286 \sa setRotationAt()
287*/
288qreal QGraphicsItemAnimation::rotationAt(qreal step) const
289{
290 check_step_valid(step, "rotationAt");
291 return d->linearValueForStep(step, d->rotation);
292}
293
294/*!
295 Sets the rotation of the item at the given \a step value to the \a angle specified.
296
297 \sa rotationAt()
298*/
299void QGraphicsItemAnimation::setRotationAt(qreal step, qreal angle)
300{
301 d->insertUniquePair(step, angle, &d->rotation, "setRotationAt");
302}
303
304/*!
305 Returns all explicitly inserted rotations.
306
307 \sa rotationAt(), setRotationAt()
308*/
309QList<std::pair<qreal, qreal> > QGraphicsItemAnimation::rotationList() const
310{
311 QList<std::pair<qreal, qreal>> list;
312 const int numRotations = d->rotation.size();
313 list.reserve(numRotations);
314 for (int i = 0; i < numRotations; ++i)
315 list.emplace_back(d->rotation.at(i).step, d->rotation.at(i).value);
316
317 return list;
318}
319
320/*!
321 Returns the horizontal translation of the item at the specified \a step value.
322
323 \sa setTranslationAt()
324*/
325qreal QGraphicsItemAnimation::xTranslationAt(qreal step) const
326{
327 check_step_valid(step, "xTranslationAt");
328 return d->linearValueForStep(step, d->xTranslation);
329}
330
331/*!
332 Returns the vertical translation of the item at the specified \a step value.
333
334 \sa setTranslationAt()
335*/
336qreal QGraphicsItemAnimation::yTranslationAt(qreal step) const
337{
338 check_step_valid(step, "yTranslationAt");
339 return d->linearValueForStep(step, d->yTranslation);
340}
341
342/*!
343 Sets the translation of the item at the given \a step value using the horizontal
344 and vertical coordinates specified by \a dx and \a dy.
345
346 \sa xTranslationAt(), yTranslationAt()
347*/
348void QGraphicsItemAnimation::setTranslationAt(qreal step, qreal dx, qreal dy)
349{
350 d->insertUniquePair(step, dx, &d->xTranslation, "setTranslationAt");
351 d->insertUniquePair(step, dy, &d->yTranslation, "setTranslationAt");
352}
353
354/*!
355 Returns all explicitly inserted translations.
356
357 \sa xTranslationAt(), yTranslationAt(), setTranslationAt()
358*/
359QList<std::pair<qreal, QPointF> > QGraphicsItemAnimation::translationList() const
360{
361 QList<std::pair<qreal, QPointF>> list;
362 const int numTranslations = d->xTranslation.size();
363 list.reserve(numTranslations);
364 for (int i = 0; i < numTranslations; ++i)
365 list.emplace_back(d->xTranslation.at(i).step,
366 QPointF(d->xTranslation.at(i).value, d->yTranslation.at(i).value));
367
368 return list;
369}
370
371/*!
372 Returns the vertical scale for the item at the specified \a step value.
373
374 \sa setScaleAt()
375*/
376qreal QGraphicsItemAnimation::verticalScaleAt(qreal step) const
377{
378 check_step_valid(step, "verticalScaleAt");
379
380 return d->linearValueForStep(step, d->verticalScale, 1);
381}
382
383/*!
384 Returns the horizontal scale for the item at the specified \a step value.
385
386 \sa setScaleAt()
387*/
388qreal QGraphicsItemAnimation::horizontalScaleAt(qreal step) const
389{
390 check_step_valid(step, "horizontalScaleAt");
391 return d->linearValueForStep(step, d->horizontalScale, 1);
392}
393
394/*!
395 Sets the scale of the item at the given \a step value using the horizontal and
396 vertical scale factors specified by \a sx and \a sy.
397
398 \sa verticalScaleAt(), horizontalScaleAt()
399*/
400void QGraphicsItemAnimation::setScaleAt(qreal step, qreal sx, qreal sy)
401{
402 d->insertUniquePair(step, sx, &d->horizontalScale, "setScaleAt");
403 d->insertUniquePair(step, sy, &d->verticalScale, "setScaleAt");
404}
405
406/*!
407 Returns all explicitly inserted scales.
408
409 \sa verticalScaleAt(), horizontalScaleAt(), setScaleAt()
410*/
411QList<std::pair<qreal, QPointF> > QGraphicsItemAnimation::scaleList() const
412{
413 QList<std::pair<qreal, QPointF>> list;
414 const int numScales = d->horizontalScale.size();
415 list.reserve(numScales);
416 for (int i = 0; i < numScales; ++i)
417 list.emplace_back(d->horizontalScale.at(i).step,
418 QPointF(d->horizontalScale.at(i).value,
419 d->verticalScale.at(i).value));
420
421 return list;
422}
423
424/*!
425 Returns the vertical shear for the item at the specified \a step value.
426
427 \sa setShearAt()
428*/
429qreal QGraphicsItemAnimation::verticalShearAt(qreal step) const
430{
431 check_step_valid(step, "verticalShearAt");
432 return d->linearValueForStep(step, d->verticalShear, 0);
433}
434
435/*!
436 Returns the horizontal shear for the item at the specified \a step value.
437
438 \sa setShearAt()
439*/
440qreal QGraphicsItemAnimation::horizontalShearAt(qreal step) const
441{
442 check_step_valid(step, "horizontalShearAt");
443 return d->linearValueForStep(step, d->horizontalShear, 0);
444}
445
446/*!
447 Sets the shear of the item at the given \a step value using the horizontal and
448 vertical shear factors specified by \a sh and \a sv.
449
450 \sa verticalShearAt(), horizontalShearAt()
451*/
452void QGraphicsItemAnimation::setShearAt(qreal step, qreal sh, qreal sv)
453{
454 d->insertUniquePair(step, sh, &d->horizontalShear, "setShearAt");
455 d->insertUniquePair(step, sv, &d->verticalShear, "setShearAt");
456}
457
458/*!
459 Returns all explicitly inserted shears.
460
461 \sa verticalShearAt(), horizontalShearAt(), setShearAt()
462*/
463QList<std::pair<qreal, QPointF> > QGraphicsItemAnimation::shearList() const
464{
465 QList<std::pair<qreal, QPointF>> list;
466 const int numShears = d->horizontalShear.size();
467 list.reserve(numShears);
468 for (int i = 0; i < numShears; ++i)
469 list.emplace_back(d->horizontalShear.at(i).step,
470 QPointF(d->horizontalShear.at(i).value,
471 d->verticalShear.at(i).value));
472
473 return list;
474}
475
476/*!
477 Clears the scheduled transformations used for the animation, but
478 retains the item and timeline.
479*/
480void QGraphicsItemAnimation::clear()
481{
482 d->xPosition.clear();
483 d->yPosition.clear();
484 d->rotation.clear();
485 d->verticalScale.clear();
486 d->horizontalScale.clear();
487 d->verticalShear.clear();
488 d->horizontalShear.clear();
489 d->xTranslation.clear();
490 d->yTranslation.clear();
491}
492
493/*!
494 \fn void QGraphicsItemAnimation::setStep(qreal step)
495
496 Sets the current \a step value for the animation, causing the
497 transformations scheduled at this step to be performed.
498*/
499void QGraphicsItemAnimation::setStep(qreal step)
500{
501 if (!check_step_valid(step, "setStep"))
502 return;
503
504 beforeAnimationStep(step);
505
506 d->step = step;
507 if (d->item) {
508 if (!d->xPosition.isEmpty() || !d->yPosition.isEmpty())
509 d->item->setPos(posAt(step));
510 if (!d->rotation.isEmpty()
511 || !d->verticalScale.isEmpty()
512 || !d->horizontalScale.isEmpty()
513 || !d->verticalShear.isEmpty()
514 || !d->horizontalShear.isEmpty()
515 || !d->xTranslation.isEmpty()
516 || !d->yTranslation.isEmpty()) {
517 d->item->setTransform(d->startTransform * transformAt(step));
518 }
519 }
520
521 afterAnimationStep(step);
522}
523
524/*!
525 \fn void QGraphicsItemAnimation::beforeAnimationStep(qreal step)
526
527 This method is meant to be overridden by subclassed that needs to
528 execute additional code before a new step takes place. The
529 animation \a step is provided for use in cases where the action
530 depends on its value.
531*/
532void QGraphicsItemAnimation::beforeAnimationStep(qreal step)
533{
534 Q_UNUSED(step);
535}
536
537/*!
538 \fn void QGraphicsItemAnimation::afterAnimationStep(qreal step)
539
540 This method is meant to be overridden in subclasses that need to
541 execute additional code after a new step has taken place. The
542 animation \a step is provided for use in cases where the action
543 depends on its value.
544*/
545void QGraphicsItemAnimation::afterAnimationStep(qreal step)
546{
547 Q_UNUSED(step);
548}
549
550QT_END_NAMESPACE
551
552#include "moc_qgraphicsitemanimation.cpp"
void insertUniquePair(qreal step, qreal value, QList< Pair > *binList, const char *method)
qreal linearValueForStep(qreal step, const QList< Pair > &source, qreal defaultValue=0)
\inmodule QtCore\reentrant
Definition qpoint.h:229
static QT_BEGIN_NAMESPACE bool check_step_valid(qreal step, const char *method)
Q_DECLARE_TYPEINFO(QGraphicsItemAnimationPrivate::Pair, Q_PRIMITIVE_TYPE)
bool operator==(const Pair &other) const
bool operator<(const Pair &other) const