diff options
Diffstat (limited to 'src/declarative/util/qdeclarativeeasefollow.cpp')
-rw-r--r-- | src/declarative/util/qdeclarativeeasefollow.cpp | 499 |
1 files changed, 223 insertions, 276 deletions
diff --git a/src/declarative/util/qdeclarativeeasefollow.cpp b/src/declarative/util/qdeclarativeeasefollow.cpp index ee181dd..ce2c496 100644 --- a/src/declarative/util/qdeclarativeeasefollow.cpp +++ b/src/declarative/util/qdeclarativeeasefollow.cpp @@ -40,86 +40,79 @@ ****************************************************************************/ #include "qdeclarativeeasefollow_p.h" +#include "qdeclarativeeasefollow_p_p.h" #include "qdeclarativeanimation_p_p.h" #include <qdeclarativeproperty.h> +#include "qdeclarativeproperty_p.h" + +#include "qdeclarativeglobal_p.h" #include <QtCore/qdebug.h> #include <math.h> +#include <QTimer> + +#define DELAY_STOP_TIMER_INTERVAL 32 QT_BEGIN_NAMESPACE +QSmoothedAnimation::QSmoothedAnimation(QObject *parent) + : QAbstractAnimation(parent), to(0), velocity(200), userDuration(-1), maximumEasingTime(-1), + reversingMode(QDeclarativeSmoothedAnimation::Eased), initialVelocity(0), + trackVelocity(0), initialValue(0), invert(false), finalDuration(-1), lastTime(0) +{ + delayedStopTimer.setInterval(DELAY_STOP_TIMER_INTERVAL); + delayedStopTimer.setSingleShot(true); + connect(&delayedStopTimer, SIGNAL(timeout()), this, SLOT(stop())); +} +void QSmoothedAnimation::restart() +{ + if (state() != QAbstractAnimation::Running) + start(); + else + init(); +} -class QDeclarativeEaseFollowPrivate : public QObjectPrivate +void QSmoothedAnimation::updateState(QAbstractAnimation::State newState, QAbstractAnimation::State /*oldState*/) { - Q_DECLARE_PUBLIC(QDeclarativeEaseFollow) -public: - QDeclarativeEaseFollowPrivate() - : source(0), velocity(200), duration(-1), maximumEasingTime(-1), - reversingMode(QDeclarativeEaseFollow::Eased), initialVelocity(0), - initialValue(0), invert(false), enabled(true), trackVelocity(0), clockOffset(0), - lastTick(0), clock(this) - {} - - qreal source; - qreal velocity; - qreal duration; - qreal maximumEasingTime; - QDeclarativeEaseFollow::ReversingMode reversingMode; - - qreal initialVelocity; - qreal initialValue; - bool invert; - bool enabled; - - qreal trackVelocity; - - QDeclarativeProperty target; - - int clockOffset; - int lastTick; - void tick(int); - void clockStart(); - void clockStop(); - QTickAnimationProxy<QDeclarativeEaseFollowPrivate, &QDeclarativeEaseFollowPrivate::tick> clock; - - void restart(); - - // Parameters for use in tick() - qreal a; // Acceleration - qreal d; // Deceleration - qreal tf; // Total time - qreal tp; // Time at which peak velocity occurs - qreal td; // Time at which decelleration begins - qreal vp; // Velocity at tp - qreal sp; // Displacement at tp - qreal sd; // Displacement at td - qreal vi; // "Normalized" initialvelocity - bool recalc(); -}; - -bool QDeclarativeEaseFollowPrivate::recalc() + if (newState == QAbstractAnimation::Running) + init(); +} + +void QSmoothedAnimation::delayedStop() { - qreal s = source - initialValue; + if (!delayedStopTimer.isActive()) + delayedStopTimer.start(); +} + +int QSmoothedAnimation::duration() const +{ + return -1; +} + +bool QSmoothedAnimation::recalc() +{ + s = to - initialValue; vi = initialVelocity; - s = (invert?-1.0:1.0) * s; - vi = (invert?-1.0:1.0) * vi; + s = (invert? -1.0: 1.0) * s; - if (duration > 0 && velocity > 0) { + if (userDuration > 0 && velocity > 0) { tf = s / velocity; - if (tf > (duration / 1000.)) tf = (duration / 1000.); - } else if (duration > 0) { - tf = duration / 1000.; + if (tf > (userDuration / 1000.)) tf = (userDuration / 1000.); + } else if (userDuration > 0) { + tf = userDuration / 1000.; } else if (velocity > 0) { tf = s / velocity; } else { return false; } + finalDuration = ceil(tf * 1000.0); + if (maximumEasingTime == 0) { a = 0; d = 0; @@ -129,7 +122,6 @@ bool QDeclarativeEaseFollowPrivate::recalc() sp = 0; sd = s; } else if (maximumEasingTime != -1 && tf > (maximumEasingTime / 1000.)) { - qreal met = maximumEasingTime / 1000.; td = tf - met; @@ -138,7 +130,6 @@ bool QDeclarativeEaseFollowPrivate::recalc() qreal c3 = -0.5 * (tf - td) * vi * vi; qreal vp1 = (-c2 + sqrt(c2 * c2 - 4 * c1 * c3)) / (2. * c1); - // qreal vp2 = (-c2 - sqrt(c2 * c2 - 4 * c1 * c3)) / (2. * c1); vp = vp1; a = vp / met; @@ -147,21 +138,16 @@ bool QDeclarativeEaseFollowPrivate::recalc() sp = vi * tp + 0.5 * a * tp * tp; sd = sp + (td - tp) * vp; } else { - qreal c1 = 0.25 * tf * tf; qreal c2 = 0.5 * vi * tf - s; qreal c3 = -0.25 * vi * vi; qreal a1 = (-c2 + sqrt(c2 * c2 - 4 * c1 * c3)) / (2. * c1); - //qreal a2 = (-c2 - sqrt(c2 * c2 - 4 * c1 * c3)) / (2. * c1); qreal tp1 = 0.5 * tf - 0.5 * vi / a1; - //qreal tp2 = 0.5 * tf - 0.5 * vi / a2; qreal vp1 = a1 * tp1 + vi; - //qreal vp2 = a2 * tp2 + vi; qreal sp1 = 0.5 * a1 * tp1 * tp1 + vi * tp1; - //qreal sp2 = 0.5 * a2 * tp2 * tp2 + vi * tp2; a = a1; d = a1; @@ -171,92 +157,103 @@ bool QDeclarativeEaseFollowPrivate::recalc() sp = sp1; sd = sp1; } - - /* - qWarning() << "a:" << a << "tf:" << tf << "tp:" << tp << "vp:" - << vp << "sp:" << sp << "vi:" << vi << "invert:" << invert; - */ return true; } -void QDeclarativeEaseFollowPrivate::clockStart() +qreal QSmoothedAnimation::easeFollow(qreal time_seconds) { - if (clock.state() == QAbstractAnimation::Running) { - clockOffset = lastTick; - return; + qreal value; + if (time_seconds < tp) { + trackVelocity = vi + time_seconds * a; + value = 0.5 * a * time_seconds * time_seconds + vi * time_seconds; + } else if (time_seconds < td) { + time_seconds -= tp; + trackVelocity = vp; + value = sp + time_seconds * vp; + } else if (time_seconds < tf) { + time_seconds -= td; + trackVelocity = vp - time_seconds * a; + value = sd - 0.5 * d * time_seconds * time_seconds + vp * time_seconds; } else { - clockOffset = 0; - lastTick = 0; - clock.start(); + trackVelocity = 0; + value = s; + delayedStop(); } -} -void QDeclarativeEaseFollowPrivate::clockStop() -{ - clockOffset = 0; - lastTick = 0; - clock.stop(); + // to normalize 's' between [0..1], divide 'value' by 's' + return value; } -void QDeclarativeEaseFollowPrivate::tick(int t) +void QSmoothedAnimation::updateCurrentTime(int t) { - lastTick = t; - t -= clockOffset; - - qreal time_seconds = qreal(t) / 1000.; - - qreal out = 0; - if (time_seconds < tp) { - - trackVelocity = vi + time_seconds * a; - trackVelocity = (invert?-1.0:1.0) * trackVelocity; - - qreal value = 0.5 * a * time_seconds * time_seconds + vi * time_seconds; - value = (invert?-1.0:1.0) * value; - target.write(initialValue + value); - out = initialValue + value; - } else if (time_seconds < td) { + qreal time_seconds = qreal(t - lastTime) / 1000.; - time_seconds -= tp; - trackVelocity = (invert?-1.0:1.0) * vp; - qreal value = sp + time_seconds * vp; - value = (invert?-1.0:1.0) * value; + qreal value = easeFollow(time_seconds); + value *= (invert? -1.0: 1.0); + QDeclarativePropertyPrivate::write(target, initialValue + value, + QDeclarativePropertyPrivate::BypassInterceptor + | QDeclarativePropertyPrivate::DontRemoveBinding); +} - target.write(initialValue + value); +void QSmoothedAnimation::init() +{ + if (velocity == 0) { + stop(); + return; + } - out = initialValue + value; - } else if (time_seconds < tf) { + if (delayedStopTimer.isActive()) + delayedStopTimer.stop(); - time_seconds -= td; + initialValue = target.read().toReal(); + lastTime = this->currentTime(); - trackVelocity = vp - time_seconds * a; - trackVelocity = (invert?-1.0:1.0) * trackVelocity; + if (to == initialValue) { + stop(); + return; + } - qreal value = sd - 0.5 * d * time_seconds * time_seconds + vp * time_seconds; - value = (invert?-1.0:1.0) * value; + bool hasReversed = trackVelocity != 0. && + ((trackVelocity > 0) == ((initialValue - to) > 0)); - target.write(initialValue + value); + if (hasReversed) { + switch (reversingMode) { + default: + case QDeclarativeSmoothedAnimation::Eased: + break; + case QDeclarativeSmoothedAnimation::Sync: + QDeclarativePropertyPrivate::write(target, to, + QDeclarativePropertyPrivate::BypassInterceptor + | QDeclarativePropertyPrivate::DontRemoveBinding); + return; + case QDeclarativeSmoothedAnimation::Immediate: + initialVelocity = 0; + delayedStop(); + break; + } + } - out = initialValue + value; - } else { + trackVelocity = initialVelocity; - clock.stop(); + invert = (to < initialValue); - trackVelocity = 0; - target.write(source); + if (!recalc()) { + QDeclarativePropertyPrivate::write(target, to, + QDeclarativePropertyPrivate::BypassInterceptor + | QDeclarativePropertyPrivate::DontRemoveBinding); + stop(); + return; } - - //qWarning() << out << trackVelocity << t << a; } /*! - \qmlclass EaseFollow QDeclarativeEaseFollow + \qmlclass SmoothedAnimation QDeclarativeSmoothedAnimation \since 4.7 - \brief The EaseFollow element allows a property to smoothly track a value. + \brief The SmoothedAnimation element allows a property to smoothly track a value. - The EaseFollow smoothly animates a property's value to a set target value - using an ease in/out quad easing curve. If the target value changes while - the animation is in progress, the easing curves used to animate to the old + The SmoothedAnimation smoothly animates a property's value to a set target value + using an ease in/out quad easing curve. If the animation is restarted + with a different target value, the easing curves used to animate to the old and the new target values are spliced together to avoid any obvious visual glitches. @@ -282,9 +279,9 @@ Rectangle { Rectangle { color: "green" width: 60; height: 60; - x: -5; y: -5; - EaseFollow on x { source: rect1.x - 5; velocity: 200 } - EaseFollow on y { source: rect1.y - 5; velocity: 200 } + x: rect1.x - 5; y: rect1.y - 5; + Behavior on x { SmoothedAnimation { velocity: 200 } } + Behavior on y { SmoothedAnimation { velocity: 200 } } } Rectangle { @@ -301,7 +298,7 @@ Rectangle { } \endcode - The default velocity of EaseFollow is 200 units/second. Note that if the range of the + The default velocity of SmoothedAnimation is 200 units/second. Note that if the range of the value being animated is small, then the velocity will need to be adjusted appropriately. For example, the opacity of an item ranges from 0 - 1.0. To enable a smooth animation in this range the velocity will need to be @@ -311,226 +308,176 @@ Rectangle { \sa SpringFollow */ -QDeclarativeEaseFollow::QDeclarativeEaseFollow(QObject *parent) -: QObject(*(new QDeclarativeEaseFollowPrivate), parent) +QDeclarativeSmoothedAnimation::QDeclarativeSmoothedAnimation(QObject *parent) +: QDeclarativeNumberAnimation(*(new QDeclarativeSmoothedAnimationPrivate), parent) { } -QDeclarativeEaseFollow::~QDeclarativeEaseFollow() +QDeclarativeSmoothedAnimation::~QDeclarativeSmoothedAnimation() { } -/*! - \qmlproperty qreal EaseFollow::source - This property holds the source value which will be tracked. - - Bind to a property in order to track its changes. -*/ -qreal QDeclarativeEaseFollow::sourceValue() const +QDeclarativeSmoothedAnimationPrivate::QDeclarativeSmoothedAnimationPrivate() + : wrapperGroup(new QParallelAnimationGroup), anim(new QSmoothedAnimation) { - Q_D(const QDeclarativeEaseFollow); - return d->source; + Q_Q(QDeclarativeSmoothedAnimation); + QDeclarative_setParent_noEvent(wrapperGroup, q); + QDeclarative_setParent_noEvent(anim, q); } -/*! - \qmlproperty enumeration EaseFollow::reversingMode - - Sets how the EaseFollow behaves if an animation direction is reversed. - - If reversing mode is \c Eased, the animation will smoothly decelerate, and - then reverse direction. If the reversing mode is \c Immediate, the - animation will immediately begin accelerating in the reverse direction, - begining with a velocity of 0. If the reversing mode is \c Sync, the - property is immediately set to the target value. -*/ -QDeclarativeEaseFollow::ReversingMode QDeclarativeEaseFollow::reversingMode() const +QAbstractAnimation* QDeclarativeSmoothedAnimation::qtAnimation() { - Q_D(const QDeclarativeEaseFollow); - return d->reversingMode; + Q_D(QDeclarativeSmoothedAnimation); + return d->wrapperGroup; } -void QDeclarativeEaseFollow::setReversingMode(ReversingMode m) +void QDeclarativeSmoothedAnimation::transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction) { - Q_D(QDeclarativeEaseFollow); - if (d->reversingMode == m) + Q_D(QDeclarativeSmoothedAnimation); + QDeclarativeNumberAnimation::transition(actions, modified, direction); + + if (!d->actions) return; - d->reversingMode = m; - emit reversingModeChanged(); -} + QSet<QAbstractAnimation*> anims; + for (int i = 0; i < d->actions->size(); i++) { + QSmoothedAnimation *ease; + qreal trackVelocity; + bool needsRestart; + if (!d->activeAnimations.contains((*d->actions)[i].property)) { + ease = new QSmoothedAnimation(); + d->wrapperGroup->addAnimation(ease); + d->activeAnimations.insert((*d->actions)[i].property, ease); + trackVelocity = 0.0; + needsRestart = false; + } else { + ease = d->activeAnimations.value((*d->actions)[i].property); + trackVelocity = ease->trackVelocity; + needsRestart = true; + } -void QDeclarativeEaseFollowPrivate::restart() -{ - if (!enabled || velocity == 0) { - clockStop(); - return; - } + ease->target = (*d->actions)[i].property; + ease->to = (*d->actions)[i].toValue.toReal(); - initialValue = target.read().toReal(); + // copying public members from main value holder animation + ease->maximumEasingTime = d->anim->maximumEasingTime; + ease->reversingMode = d->anim->reversingMode; + ease->velocity = d->anim->velocity; + ease->userDuration = d->anim->userDuration; - if (source == initialValue) { - clockStop(); - return; - } + ease->trackVelocity = trackVelocity; + ease->initialVelocity = trackVelocity; - bool hasReversed = trackVelocity != 0. && - ((trackVelocity > 0) == ((initialValue - source) > 0)); + if (needsRestart) + ease->init(); + anims.insert(ease); + } - if (hasReversed) { - switch (reversingMode) { - default: - case QDeclarativeEaseFollow::Eased: - break; - case QDeclarativeEaseFollow::Sync: - target.write(source); - return; - case QDeclarativeEaseFollow::Immediate: - initialVelocity = 0; - clockStop(); - break; + for (int i = d->wrapperGroup->animationCount() - 1; i >= 0 ; --i) { + if (!anims.contains(d->wrapperGroup->animationAt(i))) { + QSmoothedAnimation *ease = static_cast<QSmoothedAnimation*>(d->wrapperGroup->animationAt(i)); + d->activeAnimations.remove(ease->target); + d->wrapperGroup->takeAnimation(i); + delete ease; } } +} - trackVelocity = initialVelocity; - - invert = (source < initialValue); +/*! + \qmlproperty enumeration SmoothedAnimation::reversingMode - if (!recalc()) { - target.write(source); - clockStop(); - return; - } + Sets how the SmoothedAnimation behaves if an animation direction is reversed. - clockStart(); + If reversing mode is \c Eased, the animation will smoothly decelerate, and + then reverse direction. If the reversing mode is \c Immediate, the + animation will immediately begin accelerating in the reverse direction, + begining with a velocity of 0. If the reversing mode is \c Sync, the + property is immediately set to the target value. +*/ +QDeclarativeSmoothedAnimation::ReversingMode QDeclarativeSmoothedAnimation::reversingMode() const +{ + Q_D(const QDeclarativeSmoothedAnimation); + return (QDeclarativeSmoothedAnimation::ReversingMode) d->anim->reversingMode; } -void QDeclarativeEaseFollow::setSourceValue(qreal s) +void QDeclarativeSmoothedAnimation::setReversingMode(ReversingMode m) { - Q_D(QDeclarativeEaseFollow); - - if (d->clock.state() == QAbstractAnimation::Running && d->source == s) + Q_D(QDeclarativeSmoothedAnimation); + if (d->anim->reversingMode == m) return; - d->source = s; - d->initialVelocity = d->trackVelocity; - d->restart(); - - emit sourceChanged(); + d->anim->reversingMode = m; + emit reversingModeChanged(); } /*! - \qmlproperty qreal EaseFollow::duration + \qmlproperty int SmoothedAnimation::duration - This property holds the animation duration used when tracking the source. + This property holds the animation duration, in msecs, used when tracking the source. Setting this to -1 (the default) disables the duration value. */ -qreal QDeclarativeEaseFollow::duration() const +int QDeclarativeSmoothedAnimation::duration() const { - Q_D(const QDeclarativeEaseFollow); - return d->duration; + Q_D(const QDeclarativeSmoothedAnimation); + return d->anim->userDuration; } -void QDeclarativeEaseFollow::setDuration(qreal v) +void QDeclarativeSmoothedAnimation::setDuration(int duration) { - Q_D(QDeclarativeEaseFollow); - if (d->duration == v) - return; - - d->duration = v; - d->trackVelocity = 0; - - if (d->clock.state() == QAbstractAnimation::Running) - d->restart(); - - emit durationChanged(); + Q_D(QDeclarativeSmoothedAnimation); + if (duration != -1) + QDeclarativeNumberAnimation::setDuration(duration); + d->anim->userDuration = duration; } -qreal QDeclarativeEaseFollow::velocity() const +qreal QDeclarativeSmoothedAnimation::velocity() const { - Q_D(const QDeclarativeEaseFollow); - return d->velocity; + Q_D(const QDeclarativeSmoothedAnimation); + return d->anim->velocity; } /*! - \qmlproperty qreal EaseFollow::velocity + \qmlproperty qreal SmoothedAnimation::velocity - This property holds the average velocity allowed when tracking the source. + This property holds the average velocity allowed when tracking the 'to' value. - The default velocity of EaseFollow is 200 units/second. + The default velocity of SmoothedAnimation is 200 units/second. Setting this to -1 disables the velocity value. */ -void QDeclarativeEaseFollow::setVelocity(qreal v) +void QDeclarativeSmoothedAnimation::setVelocity(qreal v) { - Q_D(QDeclarativeEaseFollow); - if (d->velocity == v) + Q_D(QDeclarativeSmoothedAnimation); + if (d->anim->velocity == v) return; - d->velocity = v; - d->trackVelocity = 0; - - if (d->clock.state() == QAbstractAnimation::Running) - d->restart(); - + d->anim->velocity = v; emit velocityChanged(); } /*! - \qmlproperty bool EaseFollow::enabled - This property holds whether the target will track the source. -*/ -bool QDeclarativeEaseFollow::enabled() const -{ - Q_D(const QDeclarativeEaseFollow); - return d->enabled; -} - -void QDeclarativeEaseFollow::setEnabled(bool enabled) -{ - Q_D(QDeclarativeEaseFollow); - if (d->enabled == enabled) - return; - - d->enabled = enabled; - if (enabled) - d->restart(); - else - d->clockStop(); - - emit enabledChanged(); -} - -void QDeclarativeEaseFollow::setTarget(const QDeclarativeProperty &t) -{ - Q_D(QDeclarativeEaseFollow); - d->target = t; -} +\qmlproperty qreal SmoothedAnimation::maximumEasingTime -/*! -\qmlproperty qreal EaseFollow::maximumEasingTime - -This property specifies the maximum time an "eases" during the follow should take. +This property specifies the maximum time, in msecs, an "eases" during the follow should take. Setting this property causes the velocity to "level out" after at a time. Setting a negative value reverts to the normal mode of easing over the entire animation duration. The default value is -1. */ -qreal QDeclarativeEaseFollow::maximumEasingTime() const +int QDeclarativeSmoothedAnimation::maximumEasingTime() const { - Q_D(const QDeclarativeEaseFollow); - return d->maximumEasingTime; + Q_D(const QDeclarativeSmoothedAnimation); + return d->anim->maximumEasingTime; } -void QDeclarativeEaseFollow::setMaximumEasingTime(qreal v) +void QDeclarativeSmoothedAnimation::setMaximumEasingTime(int v) { - Q_D(QDeclarativeEaseFollow); - d->maximumEasingTime = v; - - if (d->clock.state() == QAbstractAnimation::Running) - d->restart(); - + Q_D(QDeclarativeSmoothedAnimation); + d->anim->maximumEasingTime = v; emit maximumEasingTimeChanged(); } |