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 | |
46 | #include "qgraphicsitemanimation.h" |
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 | |
56 | QT_BEGIN_NAMESPACE |
57 | |
58 | static inline bool check_step_valid(qreal step, const char *method) |
59 | { |
60 | if (!(step >= 0 && step <= 1)) { |
61 | qWarning(msg: "QGraphicsItemAnimation::%s: invalid step = %f", method, step); |
62 | return false; |
63 | } |
64 | return true; |
65 | } |
66 | |
67 | class QGraphicsItemAnimationPrivate |
68 | { |
69 | public: |
70 | inline QGraphicsItemAnimationPrivate() |
71 | : q(nullptr), timeLine(nullptr), item(nullptr), step(0) |
72 | { } |
73 | |
74 | QGraphicsItemAnimation *q; |
75 | |
76 | QPointer<QTimeLine> timeLine; |
77 | QGraphicsItem *item; |
78 | |
79 | QPointF startPos; |
80 | QTransform startTransform; |
81 | |
82 | qreal step; |
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; } |
89 | qreal step; |
90 | qreal value; |
91 | }; |
92 | QList<Pair> xPosition; |
93 | QList<Pair> yPosition; |
94 | QList<Pair> rotation; |
95 | QList<Pair> verticalScale; |
96 | QList<Pair> horizontalScale; |
97 | QList<Pair> verticalShear; |
98 | QList<Pair> horizontalShear; |
99 | QList<Pair> xTranslation; |
100 | QList<Pair> yTranslation; |
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 | }; |
105 | Q_DECLARE_TYPEINFO(QGraphicsItemAnimationPrivate::Pair, Q_PRIMITIVE_TYPE); |
106 | |
107 | qreal QGraphicsItemAnimationPrivate::linearValueForStep(qreal step, const QList<Pair> &source, |
108 | qreal defaultValue) |
109 | { |
110 | if (source.isEmpty()) |
111 | return defaultValue; |
112 | step = qMin<qreal>(a: qMax<qreal>(a: step, b: 0), b: 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 | |
138 | void 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: step, .value: value }; |
145 | |
146 | const QList<Pair>::iterator result = std::lower_bound(first: binList->begin(), last: binList->end(), val: pair); |
147 | if (result == binList->end() || pair < *result) |
148 | binList->insert(before: result, t: pair); |
149 | else |
150 | result->value = value; |
151 | } |
152 | |
153 | /*! |
154 | Constructs an animation object with the given \a parent. |
155 | */ |
156 | QGraphicsItemAnimation::QGraphicsItemAnimation(QObject *parent) |
157 | : QObject(parent), d(new QGraphicsItemAnimationPrivate) |
158 | { |
159 | d->q = this; |
160 | } |
161 | |
162 | /*! |
163 | Destroys the animation object. |
164 | */ |
165 | QGraphicsItemAnimation::~QGraphicsItemAnimation() |
166 | { |
167 | delete d; |
168 | } |
169 | |
170 | /*! |
171 | Returns the item on which the animation object operates. |
172 | |
173 | \sa setItem() |
174 | */ |
175 | QGraphicsItem *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 | */ |
185 | void 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 | */ |
197 | QTimeLine *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 | */ |
208 | void 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(sender: timeLine, SIGNAL(valueChanged(qreal)), receiver: 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 | */ |
225 | QPointF QGraphicsItemAnimation::posAt(qreal step) const |
226 | { |
227 | check_step_valid(step, method: "posAt"); |
228 | return QPointF(d->linearValueForStep(step, source: d->xPosition, defaultValue: d->startPos.x()), |
229 | d->linearValueForStep(step, source: d->yPosition, defaultValue: 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 | */ |
239 | void QGraphicsItemAnimation::setPosAt(qreal step, const QPointF &pos) |
240 | { |
241 | d->insertUniquePair(step, value: pos.x(), binList: &d->xPosition, method: "setPosAt"); |
242 | d->insertUniquePair(step, value: pos.y(), binList: &d->yPosition, method: "setPosAt"); |
243 | } |
244 | |
245 | /*! |
246 | Returns all explicitly inserted positions. |
247 | |
248 | \sa posAt(), setPosAt() |
249 | */ |
250 | QList<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(asize: xPosCount); |
255 | for (int i = 0; i < xPosCount; ++i) |
256 | list.emplace_back(args: d->xPosition.at(i).step, |
257 | args: 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 | */ |
267 | QTransform QGraphicsItemAnimation::transformAt(qreal step) const |
268 | { |
269 | check_step_valid(step, method: "transformAt"); |
270 | |
271 | QTransform transform; |
272 | if (!d->rotation.isEmpty()) |
273 | transform.rotate(a: rotationAt(step)); |
274 | if (!d->verticalScale.isEmpty()) |
275 | transform.scale(sx: horizontalScaleAt(step), sy: verticalScaleAt(step)); |
276 | if (!d->verticalShear.isEmpty()) |
277 | transform.shear(sh: horizontalShearAt(step), sv: verticalShearAt(step)); |
278 | if (!d->xTranslation.isEmpty()) |
279 | transform.translate(dx: xTranslationAt(step), dy: 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 | */ |
288 | qreal QGraphicsItemAnimation::rotationAt(qreal step) const |
289 | { |
290 | check_step_valid(step, method: "rotationAt"); |
291 | return d->linearValueForStep(step, source: 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 | */ |
299 | void QGraphicsItemAnimation::setRotationAt(qreal step, qreal angle) |
300 | { |
301 | d->insertUniquePair(step, value: angle, binList: &d->rotation, method: "setRotationAt"); |
302 | } |
303 | |
304 | /*! |
305 | Returns all explicitly inserted rotations. |
306 | |
307 | \sa rotationAt(), setRotationAt() |
308 | */ |
309 | QList<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(asize: numRotations); |
314 | for (int i = 0; i < numRotations; ++i) |
315 | list.emplace_back(args: d->rotation.at(i).step, args: 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 | */ |
325 | qreal QGraphicsItemAnimation::xTranslationAt(qreal step) const |
326 | { |
327 | check_step_valid(step, method: "xTranslationAt"); |
328 | return d->linearValueForStep(step, source: d->xTranslation); |
329 | } |
330 | |
331 | /*! |
332 | Returns the vertical translation of the item at the specified \a step value. |
333 | |
334 | \sa setTranslationAt() |
335 | */ |
336 | qreal QGraphicsItemAnimation::yTranslationAt(qreal step) const |
337 | { |
338 | check_step_valid(step, method: "yTranslationAt"); |
339 | return d->linearValueForStep(step, source: 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 | */ |
348 | void QGraphicsItemAnimation::setTranslationAt(qreal step, qreal dx, qreal dy) |
349 | { |
350 | d->insertUniquePair(step, value: dx, binList: &d->xTranslation, method: "setTranslationAt"); |
351 | d->insertUniquePair(step, value: dy, binList: &d->yTranslation, method: "setTranslationAt"); |
352 | } |
353 | |
354 | /*! |
355 | Returns all explicitly inserted translations. |
356 | |
357 | \sa xTranslationAt(), yTranslationAt(), setTranslationAt() |
358 | */ |
359 | QList<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(asize: numTranslations); |
364 | for (int i = 0; i < numTranslations; ++i) |
365 | list.emplace_back(args: d->xTranslation.at(i).step, |
366 | args: 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 | */ |
376 | qreal QGraphicsItemAnimation::verticalScaleAt(qreal step) const |
377 | { |
378 | check_step_valid(step, method: "verticalScaleAt"); |
379 | |
380 | return d->linearValueForStep(step, source: d->verticalScale, defaultValue: 1); |
381 | } |
382 | |
383 | /*! |
384 | Returns the horizontal scale for the item at the specified \a step value. |
385 | |
386 | \sa setScaleAt() |
387 | */ |
388 | qreal QGraphicsItemAnimation::horizontalScaleAt(qreal step) const |
389 | { |
390 | check_step_valid(step, method: "horizontalScaleAt"); |
391 | return d->linearValueForStep(step, source: d->horizontalScale, defaultValue: 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 | */ |
400 | void QGraphicsItemAnimation::setScaleAt(qreal step, qreal sx, qreal sy) |
401 | { |
402 | d->insertUniquePair(step, value: sx, binList: &d->horizontalScale, method: "setScaleAt"); |
403 | d->insertUniquePair(step, value: sy, binList: &d->verticalScale, method: "setScaleAt"); |
404 | } |
405 | |
406 | /*! |
407 | Returns all explicitly inserted scales. |
408 | |
409 | \sa verticalScaleAt(), horizontalScaleAt(), setScaleAt() |
410 | */ |
411 | QList<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(asize: numScales); |
416 | for (int i = 0; i < numScales; ++i) |
417 | list.emplace_back(args: d->horizontalScale.at(i).step, |
418 | args: 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 | */ |
429 | qreal QGraphicsItemAnimation::verticalShearAt(qreal step) const |
430 | { |
431 | check_step_valid(step, method: "verticalShearAt"); |
432 | return d->linearValueForStep(step, source: d->verticalShear, defaultValue: 0); |
433 | } |
434 | |
435 | /*! |
436 | Returns the horizontal shear for the item at the specified \a step value. |
437 | |
438 | \sa setShearAt() |
439 | */ |
440 | qreal QGraphicsItemAnimation::horizontalShearAt(qreal step) const |
441 | { |
442 | check_step_valid(step, method: "horizontalShearAt"); |
443 | return d->linearValueForStep(step, source: d->horizontalShear, defaultValue: 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 | */ |
452 | void QGraphicsItemAnimation::setShearAt(qreal step, qreal sh, qreal sv) |
453 | { |
454 | d->insertUniquePair(step, value: sh, binList: &d->horizontalShear, method: "setShearAt"); |
455 | d->insertUniquePair(step, value: sv, binList: &d->verticalShear, method: "setShearAt"); |
456 | } |
457 | |
458 | /*! |
459 | Returns all explicitly inserted shears. |
460 | |
461 | \sa verticalShearAt(), horizontalShearAt(), setShearAt() |
462 | */ |
463 | QList<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(asize: numShears); |
468 | for (int i = 0; i < numShears; ++i) |
469 | list.emplace_back(args: d->horizontalShear.at(i).step, |
470 | args: 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 | */ |
480 | void 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 | */ |
499 | void QGraphicsItemAnimation::setStep(qreal step) |
500 | { |
501 | if (!check_step_valid(step, method: "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(matrix: 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 | */ |
532 | void 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 | */ |
545 | void QGraphicsItemAnimation::afterAnimationStep(qreal step) |
546 | { |
547 | Q_UNUSED(step); |
548 | } |
549 | |
550 | QT_END_NAMESPACE |
551 | |
552 | #include "moc_qgraphicsitemanimation.cpp" |
553 |
Definitions
- check_step_valid
- QGraphicsItemAnimationPrivate
- QGraphicsItemAnimationPrivate
- Pair
- operator <
- operator==
- linearValueForStep
- insertUniquePair
- QGraphicsItemAnimation
- ~QGraphicsItemAnimation
- item
- setItem
- timeLine
- setTimeLine
- posAt
- setPosAt
- posList
- transformAt
- rotationAt
- setRotationAt
- rotationList
- xTranslationAt
- yTranslationAt
- setTranslationAt
- translationList
- verticalScaleAt
- horizontalScaleAt
- setScaleAt
- scaleList
- verticalShearAt
- horizontalShearAt
- setShearAt
- shearList
- clear
- setStep
- beforeAnimationStep
Learn Advanced QML with KDAB
Find out more