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 qtquick-animation-properties
63 \brief 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 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() : reverse(false) {}
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 if (data->actions.count()) {
375 QSequentialAnimationGroupJob *topLevelGroup = new QSequentialAnimationGroupJob;
376 QActionAnimation *viaAction = d->via ? new QActionAnimation : 0;
377 QActionAnimation *targetAction = new QActionAnimation;
378 //we'll assume the common case by far is to have children, and always create ag
379 QParallelAnimationGroupJob *ag = new QParallelAnimationGroupJob;
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);
393 ag->appendAnimation(anim);
396 //TODO: simplify/clarify logic
397 bool forwards = direction == QQuickAbstractAnimation::Forward;
399 topLevelGroup->appendAnimation(d->via ? viaAction : targetAction);
400 topLevelGroup->appendAnimation(ag);
402 topLevelGroup->appendAnimation(targetAction);
405 topLevelGroup->appendAnimation(targetAction);
406 topLevelGroup->appendAnimation(ag);
407 topLevelGroup->appendAnimation(d->via ? viaAction : targetAction);
409 return initInstance(topLevelGroup);
418 \qmlclass AnchorAnimation QQuickAnchorAnimation
419 \inqmlmodule QtQuick 2
420 \ingroup qtquick-animation-properties
422 \brief Animates changes in anchor values
424 AnchorAnimation is used to animate an anchor change.
426 In the following snippet we animate the addition of a right anchor to a \l Rectangle:
428 \snippet qml/anchoranimation.qml 0
430 For convenience, when an AnchorAnimation is used in a \l Transition, it will
431 animate any AnchorChanges that have occurred during the state change.
432 This can be overridden by setting a specific target item using the
435 Like any other animation element, an AnchorAnimation can be applied in a
436 number of ways, including transitions, behaviors and property value
437 sources. The \l {QML Animation and Transitions} documentation shows a
438 variety of methods for creating animations.
440 \sa {QML Animation and Transitions}, AnchorChanges
442 QQuickAnchorAnimation::QQuickAnchorAnimation(QObject *parent)
443 : QQuickAbstractAnimation(*(new QQuickAnchorAnimationPrivate), parent)
447 QQuickAnchorAnimation::~QQuickAnchorAnimation()
452 \qmlproperty list<Item> QtQuick2::AnchorAnimation::targets
453 The items to reanchor.
455 If no targets are specified all AnchorChanges will be
456 animated by the AnchorAnimation.
458 QQmlListProperty<QQuickItem> QQuickAnchorAnimation::targets()
460 Q_D(QQuickAnchorAnimation);
461 return QQmlListProperty<QQuickItem>(this, d->targets);
465 \qmlproperty int QtQuick2::AnchorAnimation::duration
466 This property holds the duration of the animation, in milliseconds.
468 The default value is 250.
470 int QQuickAnchorAnimation::duration() const
472 Q_D(const QQuickAnchorAnimation);
476 void QQuickAnchorAnimation::setDuration(int duration)
479 qmlInfo(this) << tr("Cannot set a duration of < 0");
483 Q_D(QQuickAnchorAnimation);
484 if (d->duration == duration)
486 d->duration = duration;
487 emit durationChanged(duration);
491 \qmlproperty enumeration QtQuick2::AnchorAnimation::easing.type
492 \qmlproperty real QtQuick2::AnchorAnimation::easing.amplitude
493 \qmlproperty real QtQuick2::AnchorAnimation::easing.overshoot
494 \qmlproperty real QtQuick2::AnchorAnimation::easing.period
495 \brief Specifies the easing curve used for the animation
497 To specify an easing curve you need to specify at least the type. For some curves you can also specify
498 amplitude, period and/or overshoot. The default easing curve is
502 AnchorAnimation { easing.type: Easing.InOutQuad }
505 See the \l{PropertyAnimation::easing.type} documentation for information
506 about the different types of easing curves.
508 QEasingCurve QQuickAnchorAnimation::easing() const
510 Q_D(const QQuickAnchorAnimation);
514 void QQuickAnchorAnimation::setEasing(const QEasingCurve &e)
516 Q_D(QQuickAnchorAnimation);
521 emit easingChanged(e);
524 QAbstractAnimationJob* QQuickAnchorAnimation::transition(QQuickStateActions &actions,
525 QQmlProperties &modified,
526 TransitionDirection direction,
527 QObject *defaultTarget)
530 Q_UNUSED(defaultTarget);
531 Q_D(QQuickAnchorAnimation);
532 QQuickAnimationPropertyUpdater *data = new QQuickAnimationPropertyUpdater;
533 data->interpolatorType = QMetaType::QReal;
534 data->interpolator = d->interpolator;
535 data->reverse = direction == Backward ? true : false;
536 data->fromSourced = false;
537 data->fromDefined = false;
539 for (int ii = 0; ii < actions.count(); ++ii) {
540 QQuickAction &action = actions[ii];
541 if (action.event && action.event->type() == QQuickActionEvent::AnchorChanges
542 && (d->targets.isEmpty() || d->targets.contains(static_cast<QQuickAnchorChanges*>(action.event)->object()))) {
543 data->actions << static_cast<QQuickAnchorChanges*>(action.event)->additionalActions();
547 QQuickBulkValueAnimator *animator = new QQuickBulkValueAnimator;
548 if (data->actions.count()) {
549 animator->setAnimValue(data);
550 animator->setFromSourcedValue(&data->fromSourced);
555 animator->setDuration(d->duration);
556 animator->setEasingCurve(d->easing);
557 return initInstance(animator);
561 \qmlclass PathAnimation QQuickPathAnimation
562 \inqmlmodule QtQuick 2
563 \ingroup qtquick-animation-properties
566 \brief Animates an item along a path
568 When used in a transition, the path can be specified without start
569 or end points, for example:
574 PathCurve { x: 100; y: 100}
575 PathCurve {} //last element is empty with no end point specified
580 In the above case, the path start will be the item's current position, and the
581 path end will be the item's target position in the target state.
583 \sa {QML Animation and Transitions}, PathInterpolator
585 QQuickPathAnimation::QQuickPathAnimation(QObject *parent)
586 : QQuickAbstractAnimation(*(new QQuickPathAnimationPrivate), parent)
590 QQuickPathAnimation::~QQuickPathAnimation()
592 Q_D(QQuickPathAnimation);
593 QHash<QQuickItem*, QQuickPathAnimationAnimator* >::iterator it;
594 for (it = d->activeAnimations.begin(); it != d->activeAnimations.end(); ++it) {
595 it.value()->clearTemplate();
600 \qmlproperty int QtQuick2::PathAnimation::duration
601 This property holds the duration of the animation, in milliseconds.
603 The default value is 250.
605 int QQuickPathAnimation::duration() const
607 Q_D(const QQuickPathAnimation);
611 void QQuickPathAnimation::setDuration(int duration)
614 qmlInfo(this) << tr("Cannot set a duration of < 0");
618 Q_D(QQuickPathAnimation);
619 if (d->duration == duration)
621 d->duration = duration;
622 emit durationChanged(duration);
626 \qmlproperty enumeration QtQuick2::PathAnimation::easing.type
627 \qmlproperty real QtQuick2::PathAnimation::easing.amplitude
628 \qmlproperty list<real> QtQuick2::PathAnimation::easing.bezierCurve
629 \qmlproperty real QtQuick2::PathAnimation::easing.overshoot
630 \qmlproperty real QtQuick2::PathAnimation::easing.period
631 \brief the easing curve used for the animation.
633 To specify an easing curve you need to specify at least the type. For some curves you can also specify
634 amplitude, period, overshoot or custom bezierCurve data. The default easing curve is \c Easing.Linear.
636 See the \l{PropertyAnimation::easing.type} documentation for information
637 about the different types of easing curves.
639 QEasingCurve QQuickPathAnimation::easing() const
641 Q_D(const QQuickPathAnimation);
642 return d->easingCurve;
645 void QQuickPathAnimation::setEasing(const QEasingCurve &e)
647 Q_D(QQuickPathAnimation);
648 if (d->easingCurve == e)
652 emit easingChanged(e);
656 \qmlproperty Path QtQuick2::PathAnimation::path
657 This property holds the path to animate along.
659 For more information on defining a path see the \l Path documentation.
661 QQuickPath *QQuickPathAnimation::path() const
663 Q_D(const QQuickPathAnimation);
667 void QQuickPathAnimation::setPath(QQuickPath *path)
669 Q_D(QQuickPathAnimation);
678 \qmlproperty Item QtQuick2::PathAnimation::target
679 This property holds the item to animate.
681 QQuickItem *QQuickPathAnimation::target() const
683 Q_D(const QQuickPathAnimation);
687 void QQuickPathAnimation::setTargetObject(QQuickItem *target)
689 Q_D(QQuickPathAnimation);
690 if (d->target == target)
694 emit targetChanged();
698 \qmlproperty enumeration QtQuick2::PathAnimation::orientation
699 This property controls the rotation of the item as it animates along the path.
701 If a value other than \c Fixed is specified, the PathAnimation will rotate the
702 item to achieve the specified orientation as it travels along the path.
705 \li PathAnimation.Fixed (default) - the PathAnimation will not control
706 the rotation of the item.
707 \li PathAnimation.RightFirst - The right side of the item will lead along the path.
708 \li PathAnimation.LeftFirst - The left side of the item will lead along the path.
709 \li PathAnimation.BottomFirst - The bottom of the item will lead along the path.
710 \li PathAnimation.TopFirst - The top of the item will lead along the path.
713 QQuickPathAnimation::Orientation QQuickPathAnimation::orientation() const
715 Q_D(const QQuickPathAnimation);
716 return d->orientation;
719 void QQuickPathAnimation::setOrientation(Orientation orientation)
721 Q_D(QQuickPathAnimation);
722 if (d->orientation == orientation)
725 d->orientation = orientation;
726 emit orientationChanged(d->orientation);
730 \qmlproperty point QtQuick2::PathAnimation::anchorPoint
731 This property holds the anchor point for the item being animated.
733 By default, the upper-left corner of the target (its 0,0 point)
734 will be anchored to (or follow) the path. The anchorPoint property can be used to
735 specify a different point for anchoring. For example, specifying an anchorPoint of
736 5,5 for a 10x10 item means the center of the item will follow the path.
738 QPointF QQuickPathAnimation::anchorPoint() const
740 Q_D(const QQuickPathAnimation);
741 return d->anchorPoint;
744 void QQuickPathAnimation::setAnchorPoint(const QPointF &point)
746 Q_D(QQuickPathAnimation);
747 if (d->anchorPoint == point)
750 d->anchorPoint = point;
751 emit anchorPointChanged(point);
755 \qmlproperty real QtQuick2::PathAnimation::orientationEntryDuration
756 This property holds the duration (in milliseconds) of the transition in to the orientation.
758 If an orientation has been specified for the PathAnimation, and the starting
759 rotation of the item does not match that given by the orientation,
760 orientationEntryDuration can be used to smoothly transition from the item's
761 starting rotation to the rotation given by the path orientation.
763 int QQuickPathAnimation::orientationEntryDuration() const
765 Q_D(const QQuickPathAnimation);
766 return d->entryDuration;
769 void QQuickPathAnimation::setOrientationEntryDuration(int duration)
771 Q_D(QQuickPathAnimation);
772 if (d->entryDuration == duration)
774 d->entryDuration = duration;
775 emit orientationEntryDurationChanged(duration);
779 \qmlproperty real QtQuick2::PathAnimation::orientationExitDuration
780 This property holds the duration (in milliseconds) of the transition out of the orientation.
782 If an orientation and endRotation have been specified for the PathAnimation,
783 orientationExitDuration can be used to smoothly transition from the rotation given
784 by the path orientation to the specified endRotation.
786 int QQuickPathAnimation::orientationExitDuration() const
788 Q_D(const QQuickPathAnimation);
789 return d->exitDuration;
792 void QQuickPathAnimation::setOrientationExitDuration(int duration)
794 Q_D(QQuickPathAnimation);
795 if (d->exitDuration == duration)
797 d->exitDuration = duration;
798 emit orientationExitDurationChanged(duration);
802 \qmlproperty real QtQuick2::PathAnimation::endRotation
803 This property holds the ending rotation for the target.
805 If an orientation has been specified for the PathAnimation,
806 and the path doesn't end with the item at the desired rotation,
807 the endRotation property can be used to manually specify an end
810 This property is typically used with orientationExitDuration, as specifying
811 an endRotation without an orientationExitDuration may cause a jump to
812 the final rotation, rather than a smooth transition.
814 qreal QQuickPathAnimation::endRotation() const
816 Q_D(const QQuickPathAnimation);
817 return d->endRotation.isNull ? qreal(0) : d->endRotation.value;
820 void QQuickPathAnimation::setEndRotation(qreal rotation)
822 Q_D(QQuickPathAnimation);
823 if (!d->endRotation.isNull && d->endRotation == rotation)
826 d->endRotation = rotation;
827 emit endRotationChanged(d->endRotation);
830 QAbstractAnimationJob* QQuickPathAnimation::transition(QQuickStateActions &actions,
831 QQmlProperties &modified,
832 TransitionDirection direction,
833 QObject *defaultTarget)
835 Q_D(QQuickPathAnimation);
837 QQuickItem *target = d->target ? d->target : qobject_cast<QQuickItem*>(defaultTarget);
839 QQuickPathAnimationUpdater prevData;
840 bool havePrevData = false;
841 if (d->activeAnimations.contains(target)) {
843 prevData = *d->activeAnimations[target]->pathUpdater();
846 QList<QQuickItem*> keys = d->activeAnimations.keys();
847 foreach (QQuickItem *item, keys) {
848 QQuickPathAnimationAnimator *anim = d->activeAnimations.value(item);
849 if (anim->state() == QAbstractAnimationJob::Stopped) {
850 anim->clearTemplate();
851 d->activeAnimations.remove(item);
855 QQuickPathAnimationUpdater *data = new QQuickPathAnimationUpdater();
856 QQuickPathAnimationAnimator *pa = new QQuickPathAnimationAnimator(d);
858 d->activeAnimations[target] = pa;
860 data->orientation = d->orientation;
861 data->anchorPoint = d->anchorPoint;
862 data->entryInterval = d->duration ? qreal(d->entryDuration) / d->duration : qreal(0);
863 data->exitInterval = d->duration ? qreal(d->exitDuration) / d->duration : qreal(0);
864 data->endRotation = d->endRotation;
865 data->reverse = direction == Backward ? true : false;
866 data->fromSourced = false;
867 data->fromDefined = (d->path && d->path->hasStartX() && d->path->hasStartY()) ? true : false;
868 data->toDefined = d->path ? d->path->hasEnd() : false;
869 int origModifiedSize = modified.count();
871 for (int i = 0; i < actions.count(); ++i) {
872 QQuickAction &action = actions[i];
875 if (action.specifiedObject == target && action.property.name() == QLatin1String("x")) {
876 data->toX = action.toValue.toReal();
877 modified << action.property;
878 action.fromValue = action.toValue;
880 if (action.specifiedObject == target && action.property.name() == QLatin1String("y")) {
881 data->toY = action.toValue.toReal();
882 modified << action.property;
883 action.fromValue = action.toValue;
887 if (target && d->path &&
888 (modified.count() > origModifiedSize || data->toDefined)) {
889 data->target = target;
890 data->path = d->path;
891 data->path->invalidateSequentialHistory();
894 // get the original start angle that was used (so we can exactly reverse).
895 data->startRotation = prevData.startRotation;
897 // treat interruptions specially, otherwise we end up with strange paths
898 if ((data->reverse || prevData.reverse) && prevData.currentV > 0 && prevData.currentV < 1) {
899 if (!data->fromDefined && !data->toDefined && !prevData.painterPath.isEmpty()) {
900 QPointF pathPos = QQuickPath::sequentialPointAt(prevData.painterPath, prevData.pathLength, prevData.attributePoints, prevData.prevBez, prevData.currentV);
901 if (!prevData.anchorPoint.isNull())
902 pathPos -= prevData.anchorPoint;
903 if (pathPos == data->target->pos()) { //only treat as interruption if we interrupted ourself
904 data->painterPath = prevData.painterPath;
905 data->toDefined = data->fromDefined = data->fromSourced = true;
906 data->prevBez.isValid = false;
907 data->interruptStart = prevData.currentV;
908 data->startRotation = prevData.startRotation;
909 data->pathLength = prevData.pathLength;
910 data->attributePoints = prevData.attributePoints;
915 pa->setFromSourcedValue(&data->fromSourced);
916 pa->setAnimValue(data);
917 pa->setDuration(d->duration);
918 pa->setEasingCurve(d->easingCurve);
919 return initInstance(pa);
921 pa->setFromSourcedValue(0);
929 void QQuickPathAnimationUpdater::setValue(qreal v)
931 v = qMin(qMax(v, (qreal)0.0), (qreal)1.0);;
933 if (interruptStart.isValid()) {
936 qreal end = reverse ? 0.0 : 1.0;
937 v = interruptStart + v * (end-interruptStart);
940 bool atStart = ((reverse && v == 1.0) || (!reverse && v == 0.0));
941 if (!fromSourced && (!fromDefined || !toDefined)) {
942 qreal startX = reverse ? toX + anchorPoint.x() : target->x() + anchorPoint.x();
943 qreal startY = reverse ? toY + anchorPoint.y() : target->y() + anchorPoint.y();
944 qreal endX = reverse ? target->x() + anchorPoint.x() : toX + anchorPoint.x();
945 qreal endY = reverse ? target->y() + anchorPoint.y() : toY + anchorPoint.y();
947 prevBez.isValid = false;
948 painterPath = path->createPath(QPointF(startX, startY), QPointF(endX, endY), QStringList(), pathLength, attributePoints);
953 bool fixed = orientation == QQuickPathAnimation::Fixed;
954 QPointF currentPos = !painterPath.isEmpty() ? path->sequentialPointAt(painterPath, pathLength, attributePoints, prevBez, v, fixed ? 0 : &angle) : path->sequentialPointAt(v, fixed ? 0 : &angle);
956 //adjust position according to anchor point
957 if (!anchorPoint.isNull()) {
958 currentPos -= anchorPoint;
960 if (!anchorPoint.isNull() && !fixed)
961 target->setTransformOriginPoint(anchorPoint);
965 target->setPos(currentPos);
967 //adjust angle according to orientation
969 switch (orientation) {
970 case QQuickPathAnimation::RightFirst:
973 case QQuickPathAnimation::TopFirst:
976 case QQuickPathAnimation::LeftFirst:
977 angle = -angle + 180;
979 case QQuickPathAnimation::BottomFirst:
980 angle = -angle + 270;
987 if (atStart && !reverse) {
988 startRotation = target->rotation();
990 //shortest distance to correct orientation
991 qreal diff = angle - startRotation;
992 while (diff > 180.0) {
993 startRotation.value += 360.0;
996 while (diff < -180.0) {
997 startRotation.value -= 360.0;
1002 //smoothly transition to the desired orientation
1003 //TODO: shortest distance calculations
1004 if (startRotation.isValid()) {
1005 if (reverse && v == 0.0)
1006 angle = startRotation;
1007 else if (v < entryInterval)
1008 angle = angle * v / entryInterval + startRotation * (entryInterval - v) / entryInterval;
1010 if (endRotation.isValid()) {
1011 qreal exitStart = 1 - entryInterval;
1012 if (!reverse && v == 1.0)
1013 angle = endRotation;
1014 else if (v > exitStart)
1015 angle = endRotation * (v - exitStart) / exitInterval + angle * (exitInterval - (v - exitStart)) / exitInterval;
1017 target->setRotation(angle);
1021 NOTE: we don't always reset the transform origin, as it can cause a
1022 visual jump if ending on an angle. This means that in some cases
1023 (anchor point and orientation both specified, and ending at an angle)
1024 the transform origin will always be set after running the path animation.
1026 if ((reverse && v == 0.0) || (!reverse && v == 1.0)) {
1027 if (!anchorPoint.isNull() && !fixed && qFuzzyIsNull(angle))
1028 target->setTransformOriginPoint(QPointF());
1032 QQuickPathAnimationAnimator::QQuickPathAnimationAnimator(QQuickPathAnimationPrivate *priv)
1033 : animationTemplate(priv)
1037 QQuickPathAnimationAnimator::~QQuickPathAnimationAnimator()
1039 if (animationTemplate && pathUpdater()) {
1040 QHash<QQuickItem*, QQuickPathAnimationAnimator* >::iterator it =
1041 animationTemplate->activeAnimations.find(pathUpdater()->target);
1042 if (it != animationTemplate->activeAnimations.end() && it.value() == this)
1043 animationTemplate->activeAnimations.erase(it);