1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qquickitemanimation_p.h"
43 #include "qquickitemanimation_p_p.h"
44 #include "qquickstateoperations_p.h"
46 #include <private/qqmlproperty_p.h>
47 #include <private/qquickpath_p.h>
49 #include <QtQml/qqmlinfo.h>
50 #include <QtCore/qmath.h>
51 #include "private/qsequentialanimationgroupjob_p.h"
52 #include "private/qparallelanimationgroupjob_p.h"
53 #include <QtGui/qtransform.h>
58 \qmlclass ParentAnimation QQuickParentAnimation
59 \inqmlmodule QtQuick 2
60 \ingroup qml-animation-transition
63 \brief The ParentAnimation element animates changes in parent values.
65 ParentAnimation is used to animate a parent change for an \l Item.
67 For example, the following ParentChange changes \c blueRect to become
68 a child of \c redRect when it is clicked. The inclusion of the
69 ParentAnimation, which defines a NumberAnimation to be applied during
70 the transition, ensures the item animates smoothly as it moves to
73 \snippet doc/snippets/qml/parentanimation.qml 0
75 A ParentAnimation can contain any number of animations. These animations will
76 be run in parallel; to run them sequentially, define them within a
79 In some cases, such as when reparenting between items with clipping enabled, it is useful
80 to animate the parent change via another item that does not have clipping
81 enabled. Such an item can be set using the \l via property.
83 For convenience, when a ParentAnimation is used in a \l Transition, it will
84 animate any ParentChange that has occurred during the state change.
85 This can be overridden by setting a specific target item using the
88 Like any other animation element, a ParentAnimation can be applied in a
89 number of ways, including transitions, behaviors and property value
90 sources. The \l {QML Animation and Transitions} documentation shows a
91 variety of methods for creating animations.
93 \sa {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example}
95 QQuickParentAnimation::QQuickParentAnimation(QObject *parent)
96 : QQuickAnimationGroup(*(new QQuickParentAnimationPrivate), parent)
100 QQuickParentAnimation::~QQuickParentAnimation()
105 \qmlproperty Item QtQuick2::ParentAnimation::target
106 The item to reparent.
108 When used in a transition, if no target is specified, all
109 ParentChange occurrences are animated by the ParentAnimation.
111 QQuickItem *QQuickParentAnimation::target() const
113 Q_D(const QQuickParentAnimation);
117 void QQuickParentAnimation::setTargetObject(QQuickItem *target)
119 Q_D(QQuickParentAnimation);
120 if (target == d->target)
124 emit targetChanged();
128 \qmlproperty Item QtQuick2::ParentAnimation::newParent
129 The new parent to animate to.
131 If the ParentAnimation is defined within a \l Transition or \l Behavior,
132 this value defaults to the value defined in the end state of the
133 \l Transition, or the value of the property change that triggered the
136 QQuickItem *QQuickParentAnimation::newParent() const
138 Q_D(const QQuickParentAnimation);
142 void QQuickParentAnimation::setNewParent(QQuickItem *newParent)
144 Q_D(QQuickParentAnimation);
145 if (newParent == d->newParent)
148 d->newParent = newParent;
149 emit newParentChanged();
153 \qmlproperty Item QtQuick2::ParentAnimation::via
154 The item to reparent via. This provides a way to do an unclipped animation
155 when both the old parent and new parent are clipped.
165 QQuickItem *QQuickParentAnimation::via() const
167 Q_D(const QQuickParentAnimation);
171 void QQuickParentAnimation::setVia(QQuickItem *via)
173 Q_D(QQuickParentAnimation);
181 //### mirrors same-named function in QQuickItem
182 QPointF QQuickParentAnimationPrivate::computeTransformOrigin(QQuickItem::TransformOrigin origin, qreal width, qreal height) const
186 case QQuickItem::TopLeft:
187 return QPointF(0, 0);
188 case QQuickItem::Top:
189 return QPointF(width / 2., 0);
190 case QQuickItem::TopRight:
191 return QPointF(width, 0);
192 case QQuickItem::Left:
193 return QPointF(0, height / 2.);
194 case QQuickItem::Center:
195 return QPointF(width / 2., height / 2.);
196 case QQuickItem::Right:
197 return QPointF(width, height / 2.);
198 case QQuickItem::BottomLeft:
199 return QPointF(0, height);
200 case QQuickItem::Bottom:
201 return QPointF(width / 2., height);
202 case QQuickItem::BottomRight:
203 return QPointF(width, height);
207 QAbstractAnimationJob* QQuickParentAnimation::transition(QQuickStateActions &actions,
208 QQmlProperties &modified,
209 TransitionDirection direction,
210 QObject *defaultTarget)
212 Q_D(QQuickParentAnimation);
214 struct QQuickParentAnimationData : public QAbstractAnimationAction
216 QQuickParentAnimationData() {}
217 ~QQuickParentAnimationData() { qDeleteAll(pc); }
219 QQuickStateActions actions;
220 //### reverse should probably apply on a per-action basis
222 QList<QQuickParentChange *> pc;
223 virtual void doAction()
225 for (int ii = 0; ii < actions.count(); ++ii) {
226 const QQuickAction &action = actions.at(ii);
228 action.event->reverse();
230 action.event->execute();
235 QQuickParentAnimationData *data = new QQuickParentAnimationData;
236 QQuickParentAnimationData *viaData = new QQuickParentAnimationData;
238 bool hasExplicit = false;
239 if (d->target && d->newParent) {
240 data->reverse = false;
241 QQuickAction myAction;
242 QQuickParentChange *pc = new QQuickParentChange;
243 pc->setObject(d->target);
244 pc->setParent(d->newParent);
247 data->actions << myAction;
250 viaData->reverse = false;
251 QQuickAction myVAction;
252 QQuickParentChange *vpc = new QQuickParentChange;
253 vpc->setObject(d->target);
254 vpc->setParent(d->via);
255 myVAction.event = vpc;
257 viaData->actions << myVAction;
259 //### once actions have concept of modified,
260 // loop to match appropriate ParentChanges and mark as modified
264 for (int i = 0; i < actions.size(); ++i) {
265 QQuickAction &action = actions[i];
266 if (action.event && action.event->type() == QQuickActionEvent::ParentChange
267 && (!d->target || static_cast<QQuickParentChange*>(action.event)->object() == d->target)) {
269 QQuickParentChange *pc = static_cast<QQuickParentChange*>(action.event);
270 QQuickAction myAction = action;
271 data->reverse = action.reverseEvent;
273 //### this logic differs from PropertyAnimation
274 // (probably a result of modified vs. done)
276 QQuickParentChange *epc = new QQuickParentChange;
277 epc->setObject(static_cast<QQuickParentChange*>(action.event)->object());
278 epc->setParent(d->newParent);
279 myAction.event = epc;
281 data->actions << myAction;
284 action.actionDone = true;
285 data->actions << myAction;
289 viaData->reverse = false;
290 QQuickAction myAction;
291 QQuickParentChange *vpc = new QQuickParentChange;
292 vpc->setObject(pc->object());
293 vpc->setParent(d->via);
294 myAction.event = vpc;
296 viaData->actions << myAction;
297 QQuickAction dummyAction;
298 QQuickAction &xAction = pc->xIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction;
299 QQuickAction &yAction = pc->yIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction;
300 QQuickAction &sAction = pc->scaleIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction;
301 QQuickAction &rAction = pc->rotationIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction;
302 QQuickItem *target = pc->object();
303 QQuickItem *targetParent = action.reverseEvent ? pc->originalParent() : pc->parent();
305 //### this mirrors the logic in QQuickParentChange.
307 const QTransform &transform = targetParent->itemTransform(d->via, &ok);
308 if (transform.type() >= QTransform::TxShear || !ok) {
309 qmlInfo(this) << QQuickParentAnimation::tr("Unable to preserve appearance under complex transform");
315 bool isRotate = (transform.type() == QTransform::TxRotate) || (transform.m11() < 0);
316 if (ok && !isRotate) {
317 if (transform.m11() == transform.m22())
318 scale = transform.m11();
320 qmlInfo(this) << QQuickParentAnimation::tr("Unable to preserve appearance under non-uniform scale");
323 } else if (ok && isRotate) {
324 if (transform.m11() == transform.m22())
325 scale = qSqrt(transform.m11()*transform.m11() + transform.m12()*transform.m12());
327 qmlInfo(this) << QQuickParentAnimation::tr("Unable to preserve appearance under non-uniform scale");
332 rotation = atan2(transform.m12()/scale, transform.m11()/scale) * 180/M_PI;
334 qmlInfo(this) << QQuickParentAnimation::tr("Unable to preserve appearance under scale of 0");
339 const QPointF &point = transform.map(QPointF(xAction.toValue.toReal(),yAction.toValue.toReal()));
342 if (ok && target->transformOrigin() != QQuickItem::TopLeft) {
343 qreal w = target->width();
344 qreal h = target->height();
345 if (pc->widthIsSet() && i < actions.size() - 1)
346 w = actions[++i].toValue.toReal();
347 if (pc->heightIsSet() && i < actions.size() - 1)
348 h = actions[++i].toValue.toReal();
349 const QPointF &transformOrigin
350 = d->computeTransformOrigin(target->transformOrigin(), w,h);
351 qreal tempxt = transformOrigin.x();
352 qreal tempyt = transformOrigin.y();
354 t.translate(-tempxt, -tempyt);
356 t.scale(scale, scale);
357 t.translate(tempxt, tempyt);
358 const QPointF &offset = t.map(QPointF(0,0));
364 //qDebug() << x << y << rotation << scale;
367 sAction.toValue = sAction.toValue.toReal() * scale;
368 rAction.toValue = rAction.toValue.toReal() + rotation;
374 QSequentialAnimationGroupJob *topLevelGroup = new QSequentialAnimationGroupJob;
375 QActionAnimation *viaAction = d->via ? new QActionAnimation : 0;
376 QActionAnimation *targetAction = new QActionAnimation;
377 //we'll assume the common case by far is to have children, and always create ag
378 QParallelAnimationGroupJob *ag = new QParallelAnimationGroupJob;
380 if (data->actions.count()) {
382 viaAction->setAnimAction(viaData);
383 targetAction->setAnimAction(data);
385 //take care of any child animations
386 bool valid = d->defaultProperty.isValid();
387 QAbstractAnimationJob* anim;
388 for (int ii = 0; ii < d->animations.count(); ++ii) {
390 d->animations.at(ii)->setDefaultTarget(d->defaultProperty);
391 anim = d->animations.at(ii)->transition(actions, modified, direction, defaultTarget);
392 ag->appendAnimation(anim);
395 //TODO: simplify/clarify logic
396 bool forwards = direction == QQuickAbstractAnimation::Forward;
398 topLevelGroup->appendAnimation(d->via ? viaAction : targetAction);
399 topLevelGroup->appendAnimation(ag);
401 topLevelGroup->appendAnimation(targetAction);
404 topLevelGroup->appendAnimation(targetAction);
405 topLevelGroup->appendAnimation(ag);
406 topLevelGroup->appendAnimation(d->via ? viaAction : targetAction);
413 return initInstance(topLevelGroup);
417 \qmlclass AnchorAnimation QQuickAnchorAnimation
418 \inqmlmodule QtQuick 2
419 \ingroup qml-animation-transition
421 \brief The AnchorAnimation element animates changes in anchor values.
423 AnchorAnimation is used to animate an anchor change.
425 In the following snippet we animate the addition of a right anchor to a \l Rectangle:
427 \snippet doc/snippets/qml/anchoranimation.qml 0
429 For convenience, when an AnchorAnimation is used in a \l Transition, it will
430 animate any AnchorChanges that have occurred during the state change.
431 This can be overridden by setting a specific target item using the
434 Like any other animation element, an AnchorAnimation can be applied in a
435 number of ways, including transitions, behaviors and property value
436 sources. The \l {QML Animation and Transitions} documentation shows a
437 variety of methods for creating animations.
439 \sa {QML Animation and Transitions}, AnchorChanges
441 QQuickAnchorAnimation::QQuickAnchorAnimation(QObject *parent)
442 : QQuickAbstractAnimation(*(new QQuickAnchorAnimationPrivate), parent)
446 QQuickAnchorAnimation::~QQuickAnchorAnimation()
451 \qmlproperty list<Item> QtQuick2::AnchorAnimation::targets
452 The items to reanchor.
454 If no targets are specified all AnchorChanges will be
455 animated by the AnchorAnimation.
457 QQmlListProperty<QQuickItem> QQuickAnchorAnimation::targets()
459 Q_D(QQuickAnchorAnimation);
460 return QQmlListProperty<QQuickItem>(this, d->targets);
464 \qmlproperty int QtQuick2::AnchorAnimation::duration
465 This property holds the duration of the animation, in milliseconds.
467 The default value is 250.
469 int QQuickAnchorAnimation::duration() const
471 Q_D(const QQuickAnchorAnimation);
475 void QQuickAnchorAnimation::setDuration(int duration)
478 qmlInfo(this) << tr("Cannot set a duration of < 0");
482 Q_D(QQuickAnchorAnimation);
483 if (d->duration == duration)
485 d->duration = duration;
486 emit durationChanged(duration);
490 \qmlproperty enumeration QtQuick2::AnchorAnimation::easing.type
491 \qmlproperty real QtQuick2::AnchorAnimation::easing.amplitude
492 \qmlproperty real QtQuick2::AnchorAnimation::easing.overshoot
493 \qmlproperty real QtQuick2::AnchorAnimation::easing.period
494 \brief the easing curve used for the animation.
496 To specify an easing curve you need to specify at least the type. For some curves you can also specify
497 amplitude, period and/or overshoot. The default easing curve is
501 AnchorAnimation { easing.type: Easing.InOutQuad }
504 See the \l{PropertyAnimation::easing.type} documentation for information
505 about the different types of easing curves.
507 QEasingCurve QQuickAnchorAnimation::easing() const
509 Q_D(const QQuickAnchorAnimation);
513 void QQuickAnchorAnimation::setEasing(const QEasingCurve &e)
515 Q_D(QQuickAnchorAnimation);
520 emit easingChanged(e);
523 QAbstractAnimationJob* QQuickAnchorAnimation::transition(QQuickStateActions &actions,
524 QQmlProperties &modified,
525 TransitionDirection direction,
526 QObject *defaultTarget)
529 Q_UNUSED(defaultTarget);
530 Q_D(QQuickAnchorAnimation);
531 QQuickAnimationPropertyUpdater *data = new QQuickAnimationPropertyUpdater;
532 data->interpolatorType = QMetaType::QReal;
533 data->interpolator = d->interpolator;
534 data->reverse = direction == Backward ? true : false;
535 data->fromSourced = false;
536 data->fromDefined = false;
538 for (int ii = 0; ii < actions.count(); ++ii) {
539 QQuickAction &action = actions[ii];
540 if (action.event && action.event->type() == QQuickActionEvent::AnchorChanges
541 && (d->targets.isEmpty() || d->targets.contains(static_cast<QQuickAnchorChanges*>(action.event)->object()))) {
542 data->actions << static_cast<QQuickAnchorChanges*>(action.event)->additionalActions();
546 QQuickBulkValueAnimator *animator = new QQuickBulkValueAnimator;
547 if (data->actions.count()) {
548 animator->setAnimValue(data);
549 animator->setFromSourcedValue(&data->fromSourced);
554 animator->setDuration(d->duration);
555 animator->setEasingCurve(d->easing);
556 return initInstance(animator);
560 \qmlclass PathAnimation QQuickPathAnimation
561 \inqmlmodule QtQuick 2
562 \ingroup qml-animation-transition
565 \brief The PathAnimation element animates an item along a path.
567 When used in a transition, the path can be specified without start
568 or end points, for example:
573 PathCurve { x: 100; y: 100}
574 PathCurve {} //last element is empty with no end point specified
579 In the above case, the path start will be the item's current position, and the
580 path end will be the item's target position in the target state.
582 \sa {QML Animation and Transitions}, PathInterpolator
584 QQuickPathAnimation::QQuickPathAnimation(QObject *parent)
585 : QQuickAbstractAnimation(*(new QQuickPathAnimationPrivate), parent)
589 QQuickPathAnimation::~QQuickPathAnimation()
591 Q_D(QQuickPathAnimation);
592 QHash<QQuickItem*, QQuickPathAnimationAnimator* >::iterator it;
593 for (it = d->activeAnimations.begin(); it != d->activeAnimations.end(); ++it) {
594 it.value()->clearTemplate();
599 \qmlproperty int QtQuick2::PathAnimation::duration
600 This property holds the duration of the animation, in milliseconds.
602 The default value is 250.
604 int QQuickPathAnimation::duration() const
606 Q_D(const QQuickPathAnimation);
610 void QQuickPathAnimation::setDuration(int duration)
613 qmlInfo(this) << tr("Cannot set a duration of < 0");
617 Q_D(QQuickPathAnimation);
618 if (d->duration == duration)
620 d->duration = duration;
621 emit durationChanged(duration);
625 \qmlproperty enumeration QtQuick2::PathAnimation::easing.type
626 \qmlproperty real QtQuick2::PathAnimation::easing.amplitude
627 \qmlproperty list<real> QtQuick2::PathAnimation::easing.bezierCurve
628 \qmlproperty real QtQuick2::PathAnimation::easing.overshoot
629 \qmlproperty real QtQuick2::PathAnimation::easing.period
630 \brief the easing curve used for the animation.
632 To specify an easing curve you need to specify at least the type. For some curves you can also specify
633 amplitude, period, overshoot or custom bezierCurve data. The default easing curve is \c Easing.Linear.
635 See the \l{PropertyAnimation::easing.type} documentation for information
636 about the different types of easing curves.
638 QEasingCurve QQuickPathAnimation::easing() const
640 Q_D(const QQuickPathAnimation);
641 return d->easingCurve;
644 void QQuickPathAnimation::setEasing(const QEasingCurve &e)
646 Q_D(QQuickPathAnimation);
647 if (d->easingCurve == e)
651 emit easingChanged(e);
655 \qmlproperty Path QtQuick2::PathAnimation::path
656 This property holds the path to animate along.
658 For more information on defining a path see the \l Path documentation.
660 QQuickPath *QQuickPathAnimation::path() const
662 Q_D(const QQuickPathAnimation);
666 void QQuickPathAnimation::setPath(QQuickPath *path)
668 Q_D(QQuickPathAnimation);
677 \qmlproperty Item QtQuick2::PathAnimation::target
678 This property holds the item to animate.
680 QQuickItem *QQuickPathAnimation::target() const
682 Q_D(const QQuickPathAnimation);
686 void QQuickPathAnimation::setTargetObject(QQuickItem *target)
688 Q_D(QQuickPathAnimation);
689 if (d->target == target)
693 emit targetChanged();
697 \qmlproperty enumeration QtQuick2::PathAnimation::orientation
698 This property controls the rotation of the item as it animates along the path.
700 If a value other than \c Fixed is specified, the PathAnimation will rotate the
701 item to achieve the specified orientation as it travels along the path.
704 \li PathAnimation.Fixed (default) - the PathAnimation will not control
705 the rotation of the item.
706 \li PathAnimation.RightFirst - The right side of the item will lead along the path.
707 \li PathAnimation.LeftFirst - The left side of the item will lead along the path.
708 \li PathAnimation.BottomFirst - The bottom of the item will lead along the path.
709 \li PathAnimation.TopFirst - The top of the item will lead along the path.
712 QQuickPathAnimation::Orientation QQuickPathAnimation::orientation() const
714 Q_D(const QQuickPathAnimation);
715 return d->orientation;
718 void QQuickPathAnimation::setOrientation(Orientation orientation)
720 Q_D(QQuickPathAnimation);
721 if (d->orientation == orientation)
724 d->orientation = orientation;
725 emit orientationChanged(d->orientation);
729 \qmlproperty point QtQuick2::PathAnimation::anchorPoint
730 This property holds the anchor point for the item being animated.
732 By default, the upper-left corner of the target (its 0,0 point)
733 will be anchored to (or follow) the path. The anchorPoint property can be used to
734 specify a different point for anchoring. For example, specifying an anchorPoint of
735 5,5 for a 10x10 item means the center of the item will follow the path.
737 QPointF QQuickPathAnimation::anchorPoint() const
739 Q_D(const QQuickPathAnimation);
740 return d->anchorPoint;
743 void QQuickPathAnimation::setAnchorPoint(const QPointF &point)
745 Q_D(QQuickPathAnimation);
746 if (d->anchorPoint == point)
749 d->anchorPoint = point;
750 emit anchorPointChanged(point);
754 \qmlproperty real QtQuick2::PathAnimation::orientationEntryDuration
755 This property holds the duration (in milliseconds) of the transition in to the orientation.
757 If an orientation has been specified for the PathAnimation, and the starting
758 rotation of the item does not match that given by the orientation,
759 orientationEntryDuration can be used to smoothly transition from the item's
760 starting rotation to the rotation given by the path orientation.
762 int QQuickPathAnimation::orientationEntryDuration() const
764 Q_D(const QQuickPathAnimation);
765 return d->entryDuration;
768 void QQuickPathAnimation::setOrientationEntryDuration(int duration)
770 Q_D(QQuickPathAnimation);
771 if (d->entryDuration == duration)
773 d->entryDuration = duration;
774 emit orientationEntryDurationChanged(duration);
778 \qmlproperty real QtQuick2::PathAnimation::orientationExitDuration
779 This property holds the duration (in milliseconds) of the transition out of the orientation.
781 If an orientation and endRotation have been specified for the PathAnimation,
782 orientationExitDuration can be used to smoothly transition from the rotation given
783 by the path orientation to the specified endRotation.
785 int QQuickPathAnimation::orientationExitDuration() const
787 Q_D(const QQuickPathAnimation);
788 return d->exitDuration;
791 void QQuickPathAnimation::setOrientationExitDuration(int duration)
793 Q_D(QQuickPathAnimation);
794 if (d->exitDuration == duration)
796 d->exitDuration = duration;
797 emit orientationExitDurationChanged(duration);
801 \qmlproperty real QtQuick2::PathAnimation::endRotation
802 This property holds the ending rotation for the target.
804 If an orientation has been specified for the PathAnimation,
805 and the path doesn't end with the item at the desired rotation,
806 the endRotation property can be used to manually specify an end
809 This property is typically used with orientationExitDuration, as specifying
810 an endRotation without an orientationExitDuration may cause a jump to
811 the final rotation, rather than a smooth transition.
813 qreal QQuickPathAnimation::endRotation() const
815 Q_D(const QQuickPathAnimation);
816 return d->endRotation.isNull ? qreal(0) : d->endRotation.value;
819 void QQuickPathAnimation::setEndRotation(qreal rotation)
821 Q_D(QQuickPathAnimation);
822 if (!d->endRotation.isNull && d->endRotation == rotation)
825 d->endRotation = rotation;
826 emit endRotationChanged(d->endRotation);
829 QAbstractAnimationJob* QQuickPathAnimation::transition(QQuickStateActions &actions,
830 QQmlProperties &modified,
831 TransitionDirection direction,
832 QObject *defaultTarget)
834 Q_D(QQuickPathAnimation);
836 QQuickItem *target = d->target ? d->target : qobject_cast<QQuickItem*>(defaultTarget);
838 QQuickPathAnimationUpdater prevData;
839 bool havePrevData = false;
840 if (d->activeAnimations.contains(target)) {
842 prevData = *d->activeAnimations[target]->pathUpdater();
845 QList<QQuickItem*> keys = d->activeAnimations.keys();
846 foreach (QQuickItem *item, keys) {
847 QQuickPathAnimationAnimator *anim = d->activeAnimations.value(item);
848 if (anim->state() == QAbstractAnimationJob::Stopped) {
849 anim->clearTemplate();
850 d->activeAnimations.remove(item);
854 QQuickPathAnimationUpdater *data = new QQuickPathAnimationUpdater();
855 QQuickPathAnimationAnimator *pa = new QQuickPathAnimationAnimator(d);
857 d->activeAnimations[target] = pa;
859 data->orientation = d->orientation;
860 data->anchorPoint = d->anchorPoint;
861 data->entryInterval = d->duration ? qreal(d->entryDuration) / d->duration : qreal(0);
862 data->exitInterval = d->duration ? qreal(d->exitDuration) / d->duration : qreal(0);
863 data->endRotation = d->endRotation;
864 data->reverse = direction == Backward ? true : false;
865 data->fromSourced = false;
866 data->fromDefined = (d->path && d->path->hasStartX() && d->path->hasStartY()) ? true : false;
867 data->toDefined = d->path ? d->path->hasEnd() : false;
868 int origModifiedSize = modified.count();
870 for (int i = 0; i < actions.count(); ++i) {
871 QQuickAction &action = actions[i];
874 if (action.specifiedObject == target && action.property.name() == QLatin1String("x")) {
875 data->toX = action.toValue.toReal();
876 modified << action.property;
877 action.fromValue = action.toValue;
879 if (action.specifiedObject == target && action.property.name() == QLatin1String("y")) {
880 data->toY = action.toValue.toReal();
881 modified << action.property;
882 action.fromValue = action.toValue;
886 if (target && d->path &&
887 (modified.count() > origModifiedSize || data->toDefined)) {
888 data->target = target;
889 data->path = d->path;
890 data->path->invalidateSequentialHistory();
893 // get the original start angle that was used (so we can exactly reverse).
894 data->startRotation = prevData.startRotation;
896 // treat interruptions specially, otherwise we end up with strange paths
897 if ((data->reverse || prevData.reverse) && prevData.currentV > 0 && prevData.currentV < 1) {
898 if (!data->fromDefined && !data->toDefined && !prevData.painterPath.isEmpty()) {
899 QPointF pathPos = QQuickPath::sequentialPointAt(prevData.painterPath, prevData.pathLength, prevData.attributePoints, prevData.prevBez, prevData.currentV);
900 if (!prevData.anchorPoint.isNull())
901 pathPos -= prevData.anchorPoint;
902 if (pathPos == data->target->pos()) { //only treat as interruption if we interrupted ourself
903 data->painterPath = prevData.painterPath;
904 data->toDefined = data->fromDefined = data->fromSourced = true;
905 data->prevBez.isValid = false;
906 data->interruptStart = prevData.currentV;
907 data->startRotation = prevData.startRotation;
908 data->pathLength = prevData.pathLength;
909 data->attributePoints = prevData.attributePoints;
914 pa->setFromSourcedValue(&data->fromSourced);
915 pa->setAnimValue(data);
917 pa->setFromSourcedValue(0);
923 pa->setDuration(d->duration);
924 pa->setEasingCurve(d->easingCurve);
925 return initInstance(pa);
928 void QQuickPathAnimationUpdater::setValue(qreal v)
930 v = qMin(qMax(v, (qreal)0.0), (qreal)1.0);;
932 if (interruptStart.isValid()) {
935 qreal end = reverse ? 0.0 : 1.0;
936 v = interruptStart + v * (end-interruptStart);
939 bool atStart = ((reverse && v == 1.0) || (!reverse && v == 0.0));
940 if (!fromSourced && (!fromDefined || !toDefined)) {
941 qreal startX = reverse ? toX + anchorPoint.x() : target->x() + anchorPoint.x();
942 qreal startY = reverse ? toY + anchorPoint.y() : target->y() + anchorPoint.y();
943 qreal endX = reverse ? target->x() + anchorPoint.x() : toX + anchorPoint.x();
944 qreal endY = reverse ? target->y() + anchorPoint.y() : toY + anchorPoint.y();
946 prevBez.isValid = false;
947 painterPath = path->createPath(QPointF(startX, startY), QPointF(endX, endY), QStringList(), pathLength, attributePoints);
952 bool fixed = orientation == QQuickPathAnimation::Fixed;
953 QPointF currentPos = !painterPath.isEmpty() ? path->sequentialPointAt(painterPath, pathLength, attributePoints, prevBez, v, fixed ? 0 : &angle) : path->sequentialPointAt(v, fixed ? 0 : &angle);
955 //adjust position according to anchor point
956 if (!anchorPoint.isNull()) {
957 currentPos -= anchorPoint;
959 if (!anchorPoint.isNull() && !fixed)
960 target->setTransformOriginPoint(anchorPoint);
964 target->setPos(currentPos);
966 //adjust angle according to orientation
968 switch (orientation) {
969 case QQuickPathAnimation::RightFirst:
972 case QQuickPathAnimation::TopFirst:
975 case QQuickPathAnimation::LeftFirst:
976 angle = -angle + 180;
978 case QQuickPathAnimation::BottomFirst:
979 angle = -angle + 270;
986 if (atStart && !reverse) {
987 startRotation = target->rotation();
989 //shortest distance to correct orientation
990 qreal diff = angle - startRotation;
991 while (diff > 180.0) {
992 startRotation.value += 360.0;
995 while (diff < -180.0) {
996 startRotation.value -= 360.0;
1001 //smoothly transition to the desired orientation
1002 //TODO: shortest distance calculations
1003 if (startRotation.isValid()) {
1004 if (reverse && v == 0.0)
1005 angle = startRotation;
1006 else if (v < entryInterval)
1007 angle = angle * v / entryInterval + startRotation * (entryInterval - v) / entryInterval;
1009 if (endRotation.isValid()) {
1010 qreal exitStart = 1 - entryInterval;
1011 if (!reverse && v == 1.0)
1012 angle = endRotation;
1013 else if (v > exitStart)
1014 angle = endRotation * (v - exitStart) / exitInterval + angle * (exitInterval - (v - exitStart)) / exitInterval;
1016 target->setRotation(angle);
1020 NOTE: we don't always reset the transform origin, as it can cause a
1021 visual jump if ending on an angle. This means that in some cases
1022 (anchor point and orientation both specified, and ending at an angle)
1023 the transform origin will always be set after running the path animation.
1025 if ((reverse && v == 0.0) || (!reverse && v == 1.0)) {
1026 if (!anchorPoint.isNull() && !fixed && qFuzzyIsNull(angle))
1027 target->setTransformOriginPoint(QPointF());
1031 QQuickPathAnimationAnimator::QQuickPathAnimationAnimator(QQuickPathAnimationPrivate *priv)
1032 : animationTemplate(priv)
1036 QQuickPathAnimationAnimator::~QQuickPathAnimationAnimator()
1038 if (animationTemplate && pathUpdater()) {
1039 QHash<QQuickItem*, QQuickPathAnimationAnimator* >::iterator it =
1040 animationTemplate->activeAnimations.find(pathUpdater()->target);
1041 if (it != animationTemplate->activeAnimations.end() && it.value() == this)
1042 animationTemplate->activeAnimations.erase(it);