1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qquickanimation_p.h"
43 #include "qquickanimation_p_p.h"
44 #include "qquickstateoperations_p.h"
46 #include <private/qdeclarativeproperty_p.h>
47 #include <private/qdeclarativepath_p.h>
49 #include <QtDeclarative/qdeclarativeinfo.h>
50 #include <QtCore/qmath.h>
51 #include <QtCore/qsequentialanimationgroup.h>
52 #include <QtCore/qparallelanimationgroup.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/src/snippets/declarative/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 : QDeclarativeAnimationGroup(*(new QQuickParentAnimationPrivate), parent)
98 Q_D(QQuickParentAnimation);
99 d->topLevelGroup = new QSequentialAnimationGroup;
100 QDeclarative_setParent_noEvent(d->topLevelGroup, this);
102 d->startAction = new QActionAnimation;
103 QDeclarative_setParent_noEvent(d->startAction, d->topLevelGroup);
104 d->topLevelGroup->addAnimation(d->startAction);
106 d->ag = new QParallelAnimationGroup;
107 QDeclarative_setParent_noEvent(d->ag, d->topLevelGroup);
108 d->topLevelGroup->addAnimation(d->ag);
110 d->endAction = new QActionAnimation;
111 QDeclarative_setParent_noEvent(d->endAction, d->topLevelGroup);
112 d->topLevelGroup->addAnimation(d->endAction);
115 QQuickParentAnimation::~QQuickParentAnimation()
120 \qmlproperty Item QtQuick2::ParentAnimation::target
121 The item to reparent.
123 When used in a transition, if no target is specified, all
124 ParentChange occurrences are animated by the ParentAnimation.
126 QQuickItem *QQuickParentAnimation::target() const
128 Q_D(const QQuickParentAnimation);
132 void QQuickParentAnimation::setTarget(QQuickItem *target)
134 Q_D(QQuickParentAnimation);
135 if (target == d->target)
139 emit targetChanged();
143 \qmlproperty Item QtQuick2::ParentAnimation::newParent
144 The new parent to animate to.
146 If the ParentAnimation is defined within a \l Transition or \l Behavior,
147 this value defaults to the value defined in the end state of the
148 \l Transition, or the value of the property change that triggered the
151 QQuickItem *QQuickParentAnimation::newParent() const
153 Q_D(const QQuickParentAnimation);
157 void QQuickParentAnimation::setNewParent(QQuickItem *newParent)
159 Q_D(QQuickParentAnimation);
160 if (newParent == d->newParent)
163 d->newParent = newParent;
164 emit newParentChanged();
168 \qmlproperty Item QtQuick2::ParentAnimation::via
169 The item to reparent via. This provides a way to do an unclipped animation
170 when both the old parent and new parent are clipped.
180 QQuickItem *QQuickParentAnimation::via() const
182 Q_D(const QQuickParentAnimation);
186 void QQuickParentAnimation::setVia(QQuickItem *via)
188 Q_D(QQuickParentAnimation);
196 //### mirrors same-named function in QQuickItem
197 QPointF QQuickParentAnimationPrivate::computeTransformOrigin(QQuickItem::TransformOrigin origin, qreal width, qreal height) const
201 case QQuickItem::TopLeft:
202 return QPointF(0, 0);
203 case QQuickItem::Top:
204 return QPointF(width / 2., 0);
205 case QQuickItem::TopRight:
206 return QPointF(width, 0);
207 case QQuickItem::Left:
208 return QPointF(0, height / 2.);
209 case QQuickItem::Center:
210 return QPointF(width / 2., height / 2.);
211 case QQuickItem::Right:
212 return QPointF(width, height / 2.);
213 case QQuickItem::BottomLeft:
214 return QPointF(0, height);
215 case QQuickItem::Bottom:
216 return QPointF(width / 2., height);
217 case QQuickItem::BottomRight:
218 return QPointF(width, height);
222 void QQuickParentAnimation::transition(QDeclarativeStateActions &actions,
223 QDeclarativeProperties &modified,
224 TransitionDirection direction)
226 Q_D(QQuickParentAnimation);
228 struct QQuickParentAnimationData : public QAbstractAnimationAction
230 QQuickParentAnimationData() {}
231 ~QQuickParentAnimationData() { qDeleteAll(pc); }
233 QDeclarativeStateActions actions;
234 //### reverse should probably apply on a per-action basis
236 QList<QQuickParentChange *> pc;
237 virtual void doAction()
239 for (int ii = 0; ii < actions.count(); ++ii) {
240 const QDeclarativeAction &action = actions.at(ii);
242 action.event->reverse();
244 action.event->execute();
249 QQuickParentAnimationData *data = new QQuickParentAnimationData;
250 QQuickParentAnimationData *viaData = new QQuickParentAnimationData;
252 bool hasExplicit = false;
253 if (d->target && d->newParent) {
254 data->reverse = false;
255 QDeclarativeAction myAction;
256 QQuickParentChange *pc = new QQuickParentChange;
257 pc->setObject(d->target);
258 pc->setParent(d->newParent);
261 data->actions << myAction;
264 viaData->reverse = false;
265 QDeclarativeAction myVAction;
266 QQuickParentChange *vpc = new QQuickParentChange;
267 vpc->setObject(d->target);
268 vpc->setParent(d->via);
269 myVAction.event = vpc;
271 viaData->actions << myVAction;
273 //### once actions have concept of modified,
274 // loop to match appropriate ParentChanges and mark as modified
278 for (int i = 0; i < actions.size(); ++i) {
279 QDeclarativeAction &action = actions[i];
280 if (action.event && action.event->typeName() == QLatin1String("ParentChange")
281 && (!d->target || static_cast<QQuickParentChange*>(action.event)->object() == d->target)) {
283 QQuickParentChange *pc = static_cast<QQuickParentChange*>(action.event);
284 QDeclarativeAction myAction = action;
285 data->reverse = action.reverseEvent;
287 //### this logic differs from PropertyAnimation
288 // (probably a result of modified vs. done)
290 QQuickParentChange *epc = new QQuickParentChange;
291 epc->setObject(static_cast<QQuickParentChange*>(action.event)->object());
292 epc->setParent(d->newParent);
293 myAction.event = epc;
295 data->actions << myAction;
298 action.actionDone = true;
299 data->actions << myAction;
303 viaData->reverse = false;
304 QDeclarativeAction myAction;
305 QQuickParentChange *vpc = new QQuickParentChange;
306 vpc->setObject(pc->object());
307 vpc->setParent(d->via);
308 myAction.event = vpc;
310 viaData->actions << myAction;
311 QDeclarativeAction dummyAction;
312 QDeclarativeAction &xAction = pc->xIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction;
313 QDeclarativeAction &yAction = pc->yIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction;
314 QDeclarativeAction &sAction = pc->scaleIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction;
315 QDeclarativeAction &rAction = pc->rotationIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction;
316 QQuickItem *target = pc->object();
317 QQuickItem *targetParent = action.reverseEvent ? pc->originalParent() : pc->parent();
319 //### this mirrors the logic in QQuickParentChange.
321 const QTransform &transform = targetParent->itemTransform(d->via, &ok);
322 if (transform.type() >= QTransform::TxShear || !ok) {
323 qmlInfo(this) << QQuickParentAnimation::tr("Unable to preserve appearance under complex transform");
329 bool isRotate = (transform.type() == QTransform::TxRotate) || (transform.m11() < 0);
330 if (ok && !isRotate) {
331 if (transform.m11() == transform.m22())
332 scale = transform.m11();
334 qmlInfo(this) << QQuickParentAnimation::tr("Unable to preserve appearance under non-uniform scale");
337 } else if (ok && isRotate) {
338 if (transform.m11() == transform.m22())
339 scale = qSqrt(transform.m11()*transform.m11() + transform.m12()*transform.m12());
341 qmlInfo(this) << QQuickParentAnimation::tr("Unable to preserve appearance under non-uniform scale");
346 rotation = atan2(transform.m12()/scale, transform.m11()/scale) * 180/M_PI;
348 qmlInfo(this) << QQuickParentAnimation::tr("Unable to preserve appearance under scale of 0");
353 const QPointF &point = transform.map(QPointF(xAction.toValue.toReal(),yAction.toValue.toReal()));
356 if (ok && target->transformOrigin() != QQuickItem::TopLeft) {
357 qreal w = target->width();
358 qreal h = target->height();
359 if (pc->widthIsSet() && i < actions.size() - 1)
360 w = actions[++i].toValue.toReal();
361 if (pc->heightIsSet() && i < actions.size() - 1)
362 h = actions[++i].toValue.toReal();
363 const QPointF &transformOrigin
364 = d->computeTransformOrigin(target->transformOrigin(), w,h);
365 qreal tempxt = transformOrigin.x();
366 qreal tempyt = transformOrigin.y();
368 t.translate(-tempxt, -tempyt);
370 t.scale(scale, scale);
371 t.translate(tempxt, tempyt);
372 const QPointF &offset = t.map(QPointF(0,0));
378 //qDebug() << x << y << rotation << scale;
381 sAction.toValue = sAction.toValue.toReal() * scale;
382 rAction.toValue = rAction.toValue.toReal() + rotation;
388 if (data->actions.count()) {
389 if (direction == QDeclarativeAbstractAnimation::Forward) {
390 d->startAction->setAnimAction(d->via ? viaData : data, QActionAnimation::DeleteWhenStopped);
391 d->endAction->setAnimAction(d->via ? data : 0, QActionAnimation::DeleteWhenStopped);
393 d->endAction->setAnimAction(d->via ? viaData : data, QActionAnimation::DeleteWhenStopped);
394 d->startAction->setAnimAction(d->via ? data : 0, QActionAnimation::DeleteWhenStopped);
401 //take care of any child animations
402 bool valid = d->defaultProperty.isValid();
403 for (int ii = 0; ii < d->animations.count(); ++ii) {
405 d->animations.at(ii)->setDefaultTarget(d->defaultProperty);
406 d->animations.at(ii)->transition(actions, modified, direction);
411 QAbstractAnimation *QQuickParentAnimation::qtAnimation()
413 Q_D(QQuickParentAnimation);
414 return d->topLevelGroup;
418 \qmlclass AnchorAnimation QQuickAnchorAnimation
419 \inqmlmodule QtQuick 2
420 \ingroup qml-animation-transition
422 \brief The AnchorAnimation element 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 doc/src/snippets/declarative/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 : QDeclarativeAbstractAnimation(*(new QQuickAnchorAnimationPrivate), parent)
445 Q_D(QQuickAnchorAnimation);
446 d->va = new QDeclarativeBulkValueAnimator;
447 QDeclarative_setParent_noEvent(d->va, this);
450 QQuickAnchorAnimation::~QQuickAnchorAnimation()
454 QAbstractAnimation *QQuickAnchorAnimation::qtAnimation()
456 Q_D(QQuickAnchorAnimation);
461 \qmlproperty list<Item> QtQuick2::AnchorAnimation::targets
462 The items to reanchor.
464 If no targets are specified all AnchorChanges will be
465 animated by the AnchorAnimation.
467 QDeclarativeListProperty<QQuickItem> QQuickAnchorAnimation::targets()
469 Q_D(QQuickAnchorAnimation);
470 return QDeclarativeListProperty<QQuickItem>(this, d->targets);
474 \qmlproperty int QtQuick2::AnchorAnimation::duration
475 This property holds the duration of the animation, in milliseconds.
477 The default value is 250.
479 int QQuickAnchorAnimation::duration() const
481 Q_D(const QQuickAnchorAnimation);
482 return d->va->duration();
485 void QQuickAnchorAnimation::setDuration(int duration)
488 qmlInfo(this) << tr("Cannot set a duration of < 0");
492 Q_D(QQuickAnchorAnimation);
493 if (d->va->duration() == duration)
495 d->va->setDuration(duration);
496 emit durationChanged(duration);
500 \qmlproperty enumeration QtQuick2::AnchorAnimation::easing.type
501 \qmlproperty real QtQuick2::AnchorAnimation::easing.amplitude
502 \qmlproperty real QtQuick2::AnchorAnimation::easing.overshoot
503 \qmlproperty real QtQuick2::AnchorAnimation::easing.period
504 \brief the easing curve used for the animation.
506 To specify an easing curve you need to specify at least the type. For some curves you can also specify
507 amplitude, period and/or overshoot. The default easing curve is
511 AnchorAnimation { easing.type: Easing.InOutQuad }
514 See the \l{PropertyAnimation::easing.type} documentation for information
515 about the different types of easing curves.
517 QEasingCurve QQuickAnchorAnimation::easing() const
519 Q_D(const QQuickAnchorAnimation);
520 return d->va->easingCurve();
523 void QQuickAnchorAnimation::setEasing(const QEasingCurve &e)
525 Q_D(QQuickAnchorAnimation);
526 if (d->va->easingCurve() == e)
529 d->va->setEasingCurve(e);
530 emit easingChanged(e);
533 void QQuickAnchorAnimation::transition(QDeclarativeStateActions &actions,
534 QDeclarativeProperties &modified,
535 TransitionDirection direction)
538 Q_D(QQuickAnchorAnimation);
539 QDeclarativeAnimationPropertyUpdater *data = new QDeclarativeAnimationPropertyUpdater;
540 data->interpolatorType = QMetaType::QReal;
541 data->interpolator = d->interpolator;
543 data->reverse = direction == Backward ? true : false;
544 data->fromSourced = false;
545 data->fromDefined = false;
547 for (int ii = 0; ii < actions.count(); ++ii) {
548 QDeclarativeAction &action = actions[ii];
549 if (action.event && action.event->typeName() == QLatin1String("AnchorChanges")
550 && (d->targets.isEmpty() || d->targets.contains(static_cast<QQuickAnchorChanges*>(action.event)->object()))) {
551 data->actions << static_cast<QQuickAnchorChanges*>(action.event)->additionalActions();
555 if (data->actions.count()) {
556 if (!d->rangeIsSet) {
557 d->va->setStartValue(qreal(0));
558 d->va->setEndValue(qreal(1));
559 d->rangeIsSet = true;
561 d->va->setAnimValue(data, QAbstractAnimation::DeleteWhenStopped);
562 d->va->setFromSourcedValue(&data->fromSourced);
568 QQuickPathAnimation::QQuickPathAnimation(QObject *parent)
569 : QDeclarativeAbstractAnimation(*(new QQuickPathAnimationPrivate), parent)
571 Q_D(QQuickPathAnimation);
572 d->pa = new QDeclarativeBulkValueAnimator;
573 QDeclarative_setParent_noEvent(d->pa, this);
576 QQuickPathAnimation::~QQuickPathAnimation()
580 int QQuickPathAnimation::duration() const
582 Q_D(const QQuickPathAnimation);
583 return d->pa->duration();
586 void QQuickPathAnimation::setDuration(int duration)
589 qmlInfo(this) << tr("Cannot set a duration of < 0");
593 Q_D(QQuickPathAnimation);
594 if (d->pa->duration() == duration)
596 d->pa->setDuration(duration);
597 emit durationChanged(duration);
600 QEasingCurve QQuickPathAnimation::easing() const
602 Q_D(const QQuickPathAnimation);
603 return d->pa->easingCurve();
606 void QQuickPathAnimation::setEasing(const QEasingCurve &e)
608 Q_D(QQuickPathAnimation);
609 if (d->pa->easingCurve() == e)
612 d->pa->setEasingCurve(e);
613 emit easingChanged(e);
616 QDeclarativePath *QQuickPathAnimation::path() const
618 Q_D(const QQuickPathAnimation);
622 void QQuickPathAnimation::setPath(QDeclarativePath *path)
624 Q_D(QQuickPathAnimation);
632 QQuickItem *QQuickPathAnimation::target() const
634 Q_D(const QQuickPathAnimation);
638 void QQuickPathAnimation::setTarget(QQuickItem *target)
640 Q_D(QQuickPathAnimation);
641 if (d->target == target)
645 emit targetChanged();
648 QQuickPathAnimation::Orientation QQuickPathAnimation::orientation() const
650 Q_D(const QQuickPathAnimation);
651 return d->orientation;
654 void QQuickPathAnimation::setOrientation(Orientation orientation)
656 Q_D(QQuickPathAnimation);
657 if (d->orientation == orientation)
660 d->orientation = orientation;
661 emit orientationChanged(d->orientation);
664 QPointF QQuickPathAnimation::anchorPoint() const
666 Q_D(const QQuickPathAnimation);
667 return d->anchorPoint;
670 void QQuickPathAnimation::setAnchorPoint(const QPointF &point)
672 Q_D(QQuickPathAnimation);
673 if (d->anchorPoint == point)
676 d->anchorPoint = point;
677 emit anchorPointChanged(point);
680 qreal QQuickPathAnimation::orientationEntryInterval() const
682 Q_D(const QQuickPathAnimation);
683 return d->entryInterval;
686 void QQuickPathAnimation::setOrientationEntryInterval(qreal interval)
688 Q_D(QQuickPathAnimation);
689 if (d->entryInterval == interval)
691 d->entryInterval = interval;
692 emit orientationEntryIntervalChanged(interval);
695 qreal QQuickPathAnimation::orientationExitInterval() const
697 Q_D(const QQuickPathAnimation);
698 return d->exitInterval;
701 void QQuickPathAnimation::setOrientationExitInterval(qreal interval)
703 Q_D(QQuickPathAnimation);
704 if (d->exitInterval == interval)
706 d->exitInterval = interval;
707 emit orientationExitIntervalChanged(interval);
710 qreal QQuickPathAnimation::endRotation() const
712 Q_D(const QQuickPathAnimation);
713 return d->endRotation.isNull ? qreal(0) : d->endRotation.value;
716 void QQuickPathAnimation::setEndRotation(qreal rotation)
718 Q_D(QQuickPathAnimation);
719 if (!d->endRotation.isNull && d->endRotation == rotation)
722 d->endRotation = rotation;
723 emit endRotationChanged(d->endRotation);
727 QAbstractAnimation *QQuickPathAnimation::qtAnimation()
729 Q_D(QQuickPathAnimation);
733 void QQuickPathAnimation::transition(QDeclarativeStateActions &actions,
734 QDeclarativeProperties &modified,
735 TransitionDirection direction)
737 Q_D(QQuickPathAnimation);
738 QQuickPathAnimationUpdater *data = new QQuickPathAnimationUpdater;
740 data->orientation = d->orientation;
741 data->anchorPoint = d->anchorPoint;
742 data->entryInterval = d->entryInterval;
743 data->exitInterval = d->exitInterval;
744 data->endRotation = d->endRotation;
745 data->reverse = direction == Backward ? true : false;
746 data->fromSourced = false;
747 data->fromDefined = (d->path && d->path->hasStartX() && d->path->hasStartY()) ? true : false;
748 data->toDefined = d->path ? d->path->hasEnd() : false;
749 int origModifiedSize = modified.count();
751 for (int i = 0; i < actions.count(); ++i) {
752 QDeclarativeAction &action = actions[i];
755 if (action.specifiedObject == d->target && action.property.name() == QLatin1String("x")) {
756 data->toX = action.toValue.toReal();
757 modified << action.property;
758 action.fromValue = action.toValue;
760 if (action.specifiedObject == d->target && action.property.name() == QLatin1String("y")) {
761 data->toY = action.toValue.toReal();
762 modified << action.property;
763 action.fromValue = action.toValue;
767 if (d->target && d->path &&
768 (modified.count() > origModifiedSize || data->toDefined)) {
769 data->target = d->target;
770 data->path = d->path;
771 data->path->invalidateSequentialHistory();
772 if (!d->rangeIsSet) {
773 d->pa->setStartValue(qreal(0));
774 d->pa->setEndValue(qreal(1));
775 d->rangeIsSet = true;
778 NOTE: The following block relies on the fact that the previous value hasn't
779 yet been deleted, and has the same target, etc, which may be a bit fragile.
781 if (d->pa->getAnimValue()) {
782 QQuickPathAnimationUpdater *prevData = static_cast<QQuickPathAnimationUpdater*>(d->pa->getAnimValue());
784 // get the original start angle that was used (so we can exactly reverse).
785 data->startRotation = prevData->startRotation;
787 // treat interruptions specially, otherwise we end up with strange paths
788 if ((data->reverse || prevData->reverse) && prevData->currentV > 0 && prevData->currentV < 1) {
789 if (!data->fromDefined && !data->toDefined && !prevData->painterPath.isEmpty()) {
790 QPointF pathPos = QDeclarativePath::sequentialPointAt(prevData->painterPath, prevData->pathLength, prevData->attributePoints, prevData->prevBez, prevData->currentV);
791 if (!prevData->anchorPoint.isNull())
792 pathPos -= prevData->anchorPoint;
793 if (pathPos == data->target->pos()) { //only treat as interruption if we interrupted ourself
794 data->painterPath = prevData->painterPath;
795 data->toDefined = data->fromDefined = data->fromSourced = true;
796 data->prevBez.isValid = false;
797 data->interruptStart = prevData->currentV;
798 data->startRotation = prevData->startRotation;
799 data->pathLength = prevData->pathLength;
800 data->attributePoints = prevData->attributePoints;
805 d->pa->setFromSourcedValue(&data->fromSourced);
806 d->pa->setAnimValue(data, QAbstractAnimation::DeleteWhenStopped);
808 d->pa->setFromSourcedValue(0);
809 d->pa->setAnimValue(0, QAbstractAnimation::DeleteWhenStopped);
814 void QQuickPathAnimationUpdater::setValue(qreal v)
816 if (interruptStart.isValid()) {
819 qreal end = reverse ? 0.0 : 1.0;
820 v = interruptStart + v * (end-interruptStart);
823 bool atStart = ((reverse && v == 1.0) || (!reverse && v == 0.0));
824 if (!fromSourced && (!fromDefined || !toDefined)) {
825 qreal startX = reverse ? toX + anchorPoint.x() : target->x() + anchorPoint.x();
826 qreal startY = reverse ? toY + anchorPoint.y() : target->y() + anchorPoint.y();
827 qreal endX = reverse ? target->x() + anchorPoint.x() : toX + anchorPoint.x();
828 qreal endY = reverse ? target->y() + anchorPoint.y() : toY + anchorPoint.y();
830 prevBez.isValid = false;
831 painterPath = path->createPath(QPointF(startX, startY), QPointF(endX, endY), QStringList(), pathLength, attributePoints);
836 bool fixed = orientation == QQuickPathAnimation::Fixed;
837 QPointF currentPos = !painterPath.isEmpty() ? path->sequentialPointAt(painterPath, pathLength, attributePoints, prevBez, v, fixed ? 0 : &angle) : path->sequentialPointAt(v, fixed ? 0 : &angle);
839 //adjust position according to anchor point
840 if (!anchorPoint.isNull()) {
841 currentPos -= anchorPoint;
843 if (!anchorPoint.isNull() && !fixed)
844 target->setTransformOriginPoint(anchorPoint);
848 //### could cache properties rather than reconstructing each time
849 QDeclarativePropertyPrivate::write(QDeclarativeProperty(target, QStringLiteral("x")), currentPos.x(), QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding);
850 QDeclarativePropertyPrivate::write(QDeclarativeProperty(target, QStringLiteral("y")), currentPos.y(), QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding);
852 //adjust angle according to orientation
854 switch (orientation) {
855 case QQuickPathAnimation::RightFirst:
858 case QQuickPathAnimation::TopFirst:
861 case QQuickPathAnimation::LeftFirst:
862 angle = -angle + 180;
864 case QQuickPathAnimation::BottomFirst:
865 angle = -angle + 270;
872 if (atStart && !reverse) {
873 startRotation = target->rotation();
875 //shortest distance to correct orientation
876 qreal diff = angle - startRotation;
877 while (diff > 180.0) {
878 startRotation.value += 360.0;
881 while (diff < -180.0) {
882 startRotation.value -= 360.0;
887 //smoothly transition to the desired orientation
888 if (startRotation.isValid()) {
889 if (reverse && v == 0.0)
890 angle = startRotation;
891 else if (v < entryInterval)
892 angle = angle * v / entryInterval + startRotation * (entryInterval - v) / entryInterval;
894 if (endRotation.isValid()) {
895 qreal exitStart = 1 - exitInterval;
896 if (!reverse && v == 1.0)
898 else if (v > exitStart)
899 angle = endRotation * (v - exitStart) / exitInterval + angle * (exitInterval - (v - exitStart)) / exitInterval;
901 QDeclarativePropertyPrivate::write(QDeclarativeProperty(target, QStringLiteral("rotation")), angle, QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding);
905 NOTE: we don't always reset the transform origin, as it can cause a
906 visual jump if ending on an angle. This means that in some cases
907 (anchor point and orientation both specified, and ending at an angle)
908 the transform origin will always be set after running the path animation.
910 if ((reverse && v == 0.0) || (!reverse && v == 1.0)) {
911 if (!anchorPoint.isNull() && !fixed && qFuzzyIsNull(angle))
912 target->setTransformOriginPoint(QPointF());