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
Should work the same. This is just code cleanup, as I didn't realize until recently that Qt had classes to help with this type of animation.
  • Loading branch information
jdpurcell committed Feb 18, 2024
1 parent 4234b21 commit 918e8d5
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 918e8d5

Please sign in to comment.