From 119a22bc8b5b263356dfefd1a41c17d57337c8e5 Mon Sep 17 00:00:00 2001 From: "J.D. Purcell" Date: Sun, 18 Feb 2024 15:57:33 -0500 Subject: [PATCH] Use Qt's built-in animation for constrained positioning 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. --- src/scrollhelper.cpp | 55 +++++++++++++++----------------------------- src/scrollhelper.h | 16 ++++--------- 2 files changed, 23 insertions(+), 48 deletions(-) diff --git a/src/scrollhelper.cpp b/src/scrollhelper.cpp index 661a927d..70ea7071 100644 --- a/src/scrollhelper.cpp +++ b/src/scrollhelper.cpp @@ -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) @@ -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) @@ -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) @@ -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); -} diff --git a/src/scrollhelper.h b/src/scrollhelper.h index 6e3193cd..dc78464b 100644 --- a/src/scrollhelper.h +++ b/src/scrollhelper.h @@ -2,9 +2,8 @@ #define SCROLLHELPER_H #include -#include #include -#include +#include class ScrollHelper : public QObject { @@ -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