Skip to content

Commit

Permalink
Use Qt's built-in animation for constrained positioning
Browse files Browse the repository at this point in the history
Works mostly the same, but since the movement is absolute instead of relative it handles interruptions (user panning during animation) differently. I prefer the relative behavior so won't end up using this, but saving it for reference.
  • Loading branch information
jdpurcell committed Feb 25, 2024
1 parent 4234b21 commit 119a22b
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 48 deletions.
55 changes: 18 additions & 37 deletions src/scrollhelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ ScrollHelper::ScrollHelper(QAbstractScrollArea *parent, GetParametersCallback ge
vScrollBar = parent->verticalScrollBar();
this->getParametersCallback = getParametersCallback;

animatedScrollTimer = new QTimer(this);
animatedScrollTimer->setSingleShot(true);
animatedScrollTimer->setTimerType(Qt::PreciseTimer);
animatedScrollTimer->setInterval(10);
connect(animatedScrollTimer, &QTimer::timeout, this, [this]{ handleAnimatedScroll(); });
scrollAnimation = new QVariantAnimation(this);
scrollAnimation->setDuration(animatedScrollDuration);
scrollAnimation->setEasingCurve(QEasingCurve::OutCirc);
connect(scrollAnimation, &QVariantAnimation::valueChanged, this, [this](const QVariant &value) {
setScrollPosition(value.toPoint());
});
}

void ScrollHelper::move(QPointF delta)
Expand Down Expand Up @@ -44,7 +45,7 @@ void ScrollHelper::move(QPointF delta)
vMin,
vMax
);
QPointF scrollLocation = QPointF(hScrollBar->value(), vScrollBar->value()) + lastMoveRoundingError;
QPointF scrollLocation = getScrollPosition() + lastMoveRoundingError;
qreal scrollDeltaX = delta.x();
qreal scrollDeltaY = delta.y();
if (p.shouldConstrain)
Expand Down Expand Up @@ -75,48 +76,35 @@ void ScrollHelper::constrain(bool skipAnimation)
move(QPointF());

if (skipAnimation)
applyScrollDelta(-overscrollDistance);
setScrollPosition(getScrollPosition() - overscrollDistance);
else
beginAnimatedScroll(-overscrollDistance);
}

void ScrollHelper::cancelAnimation()
{
animatedScrollTimer->stop();
scrollAnimation->stop();
}

void ScrollHelper::beginAnimatedScroll(QPoint delta)
{
if (delta.isNull())
return;
animatedScrollTotalDelta = delta;
animatedScrollAppliedDelta = {};
animatedScrollElapsed.start();
animatedScrollTimer->start();
QPoint startPos = getScrollPosition();
scrollAnimation->setStartValue(startPos);
scrollAnimation->setEndValue(startPos + delta);
scrollAnimation->start();
}

void ScrollHelper::handleAnimatedScroll()
QPoint ScrollHelper::getScrollPosition()
{
qreal elapsed = animatedScrollElapsed.elapsed();
if (elapsed >= animatedScrollDuration)
{
applyScrollDelta(animatedScrollTotalDelta - animatedScrollAppliedDelta);
}
else
{
QPoint intermediateDelta = animatedScrollTotalDelta * smoothAnimation(elapsed / animatedScrollDuration);
applyScrollDelta(intermediateDelta - animatedScrollAppliedDelta);
animatedScrollAppliedDelta = intermediateDelta;
animatedScrollTimer->start();
}
return QPoint(hScrollBar->value(), vScrollBar->value());
}

void ScrollHelper::applyScrollDelta(QPoint delta)
void ScrollHelper::setScrollPosition(QPoint pos)
{
if (delta.x() != 0)
hScrollBar->setValue(hScrollBar->value() + delta.x());
if (delta.y() != 0)
vScrollBar->setValue(vScrollBar->value() + delta.y());
hScrollBar->setValue(pos.x());
vScrollBar->setValue(pos.y());
}

void ScrollHelper::calculateScrollRange(int contentDimension, int viewportDimension, int offset, bool shouldCenter, int &minValue, int &maxValue)
Expand Down Expand Up @@ -154,10 +142,3 @@ qreal ScrollHelper::calculateScrollDelta(qreal currentValue, int minValue, int m
}
return proposedDelta;
}

// Converts linear motion from [0,1] into something that looks more natural. Derived from the
// formula for a circle, i.e. the graph of this is literally the top-left quarter of a circle.
qreal ScrollHelper::smoothAnimation(qreal x)
{
return qPow(1.0 - qPow(x - 1.0, 2.0), 0.5);
}
16 changes: 5 additions & 11 deletions src/scrollhelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@
#define SCROLLHELPER_H

#include <QAbstractScrollArea>
#include <QElapsedTimer>
#include <QScrollBar>
#include <QTimer>
#include <QVariantAnimation>

class ScrollHelper : public QObject
{
Expand All @@ -31,27 +30,22 @@ class ScrollHelper : public QObject
private:
void beginAnimatedScroll(QPoint delta);

void handleAnimatedScroll();
QPoint getScrollPosition();

void applyScrollDelta(QPoint delta);
void setScrollPosition(QPoint pos);

static void calculateScrollRange(int contentDimension, int viewportDimension, int offset, bool shouldCenter, int &minValue, int &maxValue);

static qreal calculateScrollDelta(qreal currentValue, int minValue, int maxValue, qreal proposedDelta);

static qreal smoothAnimation(qreal x);

QScrollBar *hScrollBar;
QScrollBar *vScrollBar;
GetParametersCallback getParametersCallback;
QPointF lastMoveRoundingError;
QPoint overscrollDistance;

QTimer *animatedScrollTimer;
QPoint animatedScrollTotalDelta;
QPoint animatedScrollAppliedDelta;
QElapsedTimer animatedScrollElapsed;
const qreal animatedScrollDuration {250.0};
QVariantAnimation *scrollAnimation;
const int animatedScrollDuration {250};
};

#endif // SCROLLHELPER_H

0 comments on commit 119a22b

Please sign in to comment.