⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 285787 in webkit


Ignore:
Timestamp:
Nov 13, 2021, 11:37:36 PM (5 years ago)
Author:
Simon Fraser
Message:

Run a ScrollAnimationMomentum for the momentum phase of a scroll
https://bugs.webkit.org/show_bug.cgi?id=232897

Reviewed by Tim Horton.
Source/WebCore:

Run an animation for the momentum phase of a scroll, rather than scrolling in response
to wheel events, because it produces a smoother result with fewer dropped frames.

We re-use ScrollAnimationMomentum which is already used for scroll snap, but have to
allow it to yield unclamped coordinates, since using it for momentum can result in
rubber-banding.

When we get the momentum "began" event, we start the ScrollAnimationMomentum
using initial velocity computed from WheelEventDeltaFilter. ScrollingEffectsController then
ignores the reset of the momentum "changed" events.

We have to stop this animation when we receive a "begin" or "mayBegin" event, since
this indicates that the user has put fingers down on the trackpad.

Rename m_dragEndedScrollingVelocity to m_scrollingVelocityForScrollSnap for clarity
(at some point we should merge this with m_scrollingVelocityForMomentumAnimation).

Tests: fast/scrolling/mac/momentum-animator-in-overflow.html

fast/scrolling/mac/momentum-animator.html

  • page/WheelEventDeltaFilter.cpp:

(WebCore::WheelEventDeltaFilter::shouldApplyFilteringForEvent):

  • page/WheelEventDeltaFilter.h:
  • platform/PlatformWheelEvent.cpp:

(WebCore::operator<<):

  • platform/ScrollAnimationMomentum.cpp:

(WebCore::ScrollAnimationMomentum::startAnimatedScrollWithInitialVelocity):
(WebCore::ScrollAnimationMomentum::stop):
(WebCore::ScrollAnimationMomentum::serviceAnimation):

  • platform/ScrollAnimationMomentum.h:
  • platform/ScrollingEffectsController.cpp:

(WebCore::ScrollingEffectsController::ScrollingEffectsController):
(WebCore::ScrollingEffectsController::stopAnimatedNonRubberbandingScroll):
(WebCore::ScrollingEffectsController::startMomentumScrollWithInitialVelocity):

  • platform/ScrollingEffectsController.h:

(WebCore::ScrollingEffectsController::momentumScrollingAnimatorEnabled const):

  • platform/mac/ScrollingEffectsController.mm:

(WebCore::phaseToString):
(WebCore::ScrollingEffectsController::handleWheelEvent):
(WebCore::ScrollingEffectsController::startRubberBandAnimation):
(WebCore::ScrollingEffectsController::startRubberBandAnimationIfNecessary):
(WebCore::ScrollingEffectsController::processWheelEventForScrollSnap):

Source/WTF:

Set the MomentumScrollingAnimatorEnabled internal setting to off by default
while we bring up tests.

  • Scripts/Preferences/WebPreferencesInternal.yaml:

LayoutTests:

Some tests that enable the momentum animator.

  • fast/scrolling/mac/momentum-animator-expected.txt: Added.
  • fast/scrolling/mac/momentum-animator-in-overflow-expected.txt: Added.
  • fast/scrolling/mac/momentum-animator-in-overflow.html: Added.
  • fast/scrolling/mac/momentum-animator.html: Added.
Location:
trunk
Files:
4 added
12 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r285785 r285787  
     12021-11-13  Simon Fraser  <simon.fraser@apple.com>
     2
     3        Run a ScrollAnimationMomentum for the momentum phase of a scroll
     4        https://bugs.webkit.org/show_bug.cgi?id=232897
     5
     6        Reviewed by Tim Horton.
     7
     8        Some tests that enable the momentum animator.
     9
     10        * fast/scrolling/mac/momentum-animator-expected.txt: Added.
     11        * fast/scrolling/mac/momentum-animator-in-overflow-expected.txt: Added.
     12        * fast/scrolling/mac/momentum-animator-in-overflow.html: Added.
     13        * fast/scrolling/mac/momentum-animator.html: Added.
     14
    1152021-11-13  Said Abou-Hallawa  <said@apple.com>
    216
  • trunk/Source/WTF/ChangeLog

    r285731 r285787  
     12021-11-13  Simon Fraser  <simon.fraser@apple.com>
     2
     3        Run a ScrollAnimationMomentum for the momentum phase of a scroll
     4        https://bugs.webkit.org/show_bug.cgi?id=232897
     5
     6        Reviewed by Tim Horton.
     7
     8        Set the MomentumScrollingAnimatorEnabled internal setting to off by default
     9        while we bring up tests.
     10
     11        * Scripts/Preferences/WebPreferencesInternal.yaml:
     12
    1132021-11-12  Chris Dumez  <cdumez@apple.com>
    214
  • trunk/Source/WTF/Scripts/Preferences/WebPreferencesInternal.yaml

    r285549 r285787  
    543543  defaultValue:
    544544    WebKitLegacy:
    545       "PLATFORM(MAC)": true
    546       default: false
    547     WebKit:
    548       "PLATFORM(MAC)": true
    549       default: false
    550     WebCore:
    551       "PLATFORM(MAC)": true
     545      default: false
     546    WebKit:
     547      default: false
     548    WebCore:
    552549      default: false
    553550
  • trunk/Source/WebCore/ChangeLog

    r285786 r285787  
     12021-11-13  Simon Fraser  <simon.fraser@apple.com>
     2
     3        Run a ScrollAnimationMomentum for the momentum phase of a scroll
     4        https://bugs.webkit.org/show_bug.cgi?id=232897
     5
     6        Reviewed by Tim Horton.
     7       
     8        Run an animation for the momentum phase of a scroll, rather than scrolling in response
     9        to wheel events, because it produces a smoother result with fewer dropped frames.
     10       
     11        We re-use ScrollAnimationMomentum which is already used for scroll snap, but have to
     12        allow it to yield unclamped coordinates, since using it for momentum can result in
     13        rubber-banding.
     14       
     15        When we get the momentum "began" event, we start the ScrollAnimationMomentum
     16        using initial velocity computed from WheelEventDeltaFilter. ScrollingEffectsController then
     17        ignores the reset of the momentum "changed" events.
     18
     19        We have to stop this animation when we receive a "begin" or "mayBegin" event, since
     20        this indicates that the user has put fingers down on the trackpad.
     21       
     22        Rename m_dragEndedScrollingVelocity to m_scrollingVelocityForScrollSnap for clarity
     23        (at some point we should merge this with m_scrollingVelocityForMomentumAnimation).
     24
     25        Tests: fast/scrolling/mac/momentum-animator-in-overflow.html
     26               fast/scrolling/mac/momentum-animator.html
     27
     28        * page/WheelEventDeltaFilter.cpp:
     29        (WebCore::WheelEventDeltaFilter::shouldApplyFilteringForEvent):
     30        * page/WheelEventDeltaFilter.h:
     31        * platform/PlatformWheelEvent.cpp:
     32        (WebCore::operator<<):
     33        * platform/ScrollAnimationMomentum.cpp:
     34        (WebCore::ScrollAnimationMomentum::startAnimatedScrollWithInitialVelocity):
     35        (WebCore::ScrollAnimationMomentum::stop):
     36        (WebCore::ScrollAnimationMomentum::serviceAnimation):
     37        * platform/ScrollAnimationMomentum.h:
     38        * platform/ScrollingEffectsController.cpp:
     39        (WebCore::ScrollingEffectsController::ScrollingEffectsController):
     40        (WebCore::ScrollingEffectsController::stopAnimatedNonRubberbandingScroll):
     41        (WebCore::ScrollingEffectsController::startMomentumScrollWithInitialVelocity):
     42        * platform/ScrollingEffectsController.h:
     43        (WebCore::ScrollingEffectsController::momentumScrollingAnimatorEnabled const):
     44        * platform/mac/ScrollingEffectsController.mm:
     45        (WebCore::phaseToString):
     46        (WebCore::ScrollingEffectsController::handleWheelEvent):
     47        (WebCore::ScrollingEffectsController::startRubberBandAnimation):
     48        (WebCore::ScrollingEffectsController::startRubberBandAnimationIfNecessary):
     49        (WebCore::ScrollingEffectsController::processWheelEventForScrollSnap):
     50
    1512021-11-13  Tim Horton  <timothy_horton@apple.com>
    252
  • trunk/Source/WebCore/page/WheelEventDeltaFilter.cpp

    r285375 r285787  
    5555#if ENABLE(KINETIC_SCROLLING)
    5656    // Maybe it's a per-platform decision about which event phases get filtered. Ideally we'd filter momentum events too (but that breaks some diagonal scrolling cases).
     57    // Also, ScrollingEffectsController should ask WheelEventDeltaFilter directly for the filtered velocity, rather than sending the velocity via PlatformWheelEvent.
    5758    auto phase = event.phase();
    5859    return phase == PlatformWheelEventPhase::Began || phase == PlatformWheelEventPhase::Changed;
  • trunk/Source/WebCore/page/WheelEventDeltaFilter.h

    r285375 r285787  
    6363private:
    6464    std::optional<ScrollEventAxis> dominantAxis() const;
    65    
     65
    6666    void reset();
    6767    void updateWithDelta(FloatSize);
  • trunk/Source/WebCore/platform/PlatformWheelEvent.cpp

    r277772 r285787  
    109109    ts << " phase \"" << event.phase() << "\" momentum phase \"" << event.momentumPhase() << "\"";
    110110#endif
     111    ts << " velocity " << event.scrollingVelocity();
    111112
    112113    return ts;
  • trunk/Source/WebCore/platform/ScrollAnimationMomentum.cpp

    r285387 r285787  
    5656    }
    5757
     58    LOG(ScrollAnimations, "ScrollAnimationMomentum::startAnimatedScrollWithInitialVelocity: velocity %.2f,%.2f from %.2f,%.2f to %.2f,%.2f",
     59        initialVelocity.width(), initialVelocity.height(), initialOffset.x(), initialOffset.y(), destinationScrollOffset.x(), destinationScrollOffset.y());
     60
    5861    if (destinationScrollOffset == initialOffset) {
    5962        m_momentumCalculator = nullptr;
     
    8285void ScrollAnimationMomentum::stop()
    8386{
     87    LOG(ScrollAnimations, "ScrollAnimationMomentum::stop: offset %.2f,%.2f", m_currentOffset.x(), m_currentOffset.y());
     88
    8489    m_momentumCalculator = nullptr;
    8590    ScrollAnimation::stop();
     
    98103
    99104    m_client.scrollAnimationDidUpdate(*this, m_currentOffset);
     105
     106    LOG(ScrollAnimations, "ScrollAnimationMomentum::serviceAnimation: offset %.2f,%.2f complete %d", m_currentOffset.x(), m_currentOffset.y(), animationComplete);
    100107
    101108    if (animationComplete)
  • trunk/Source/WebCore/platform/ScrollAnimationMomentum.h

    r285387 r285787  
    4343
    4444private:
     45    ScrollClamping clamping() const final { return ScrollClamping::Unclamped; }
    4546    String debugDescription() const final;
    4647
  • trunk/Source/WebCore/platform/ScrollingEffectsController.cpp

    r285387 r285787  
    5050ScrollingEffectsController::ScrollingEffectsController(ScrollingEffectsControllerClient& client)
    5151    : m_client(client)
     52    , m_momentumScrollingAnimatorEnabled(client.momentumScrollingAnimatorEnabled())
    5253{
    5354}
     
    132133}
    133134
     135void ScrollingEffectsController::stopAnimatedNonRubberbandingScroll()
     136{
     137    LOG_WITH_STREAM(ScrollAnimations, stream << "ScrollingEffectsController " << this << " stopAnimatedNonRubberbandingScroll");
     138
     139    if (!m_currentAnimation)
     140        return;
     141
     142#if HAVE(RUBBER_BANDING)
     143    if (is<ScrollAnimationRubberBand>(m_currentAnimation))
     144        return;
     145#endif
     146
     147    if (is<ScrollAnimationMomentum>(m_currentAnimation)) {
     148        // If the animation is currently triggering rubberbanding, let it run. Ideally we'd check if the animation will cause rubberbanding at any time in the future.
     149        auto currentOffset = m_currentAnimation->currentOffset();
     150        auto extents = m_client.scrollExtents();
     151        auto constrainedOffset = currentOffset.constrainedBetween(extents.minimumScrollOffset(), extents.maximumScrollOffset());
     152        if (currentOffset != constrainedOffset)
     153            return;
     154    }
     155
     156    m_currentAnimation->stop();
     157}
     158
    134159void ScrollingEffectsController::stopAnimatedScroll()
    135160{
     
    152177
    153178    bool started = downcast<ScrollAnimationMomentum>(*m_currentAnimation).startAnimatedScrollWithInitialVelocity(initialOffset, initialVelocity, initialDelta, destinationModifier);
    154     LOG_WITH_STREAM(ScrollAnimations, stream << "ScrollingEffectsController::startMomentumScrollWithInitialVelocity() - animation " << *m_currentAnimation << " started " << started);
     179    LOG_WITH_STREAM(ScrollAnimations, stream << "ScrollingEffectsController::startMomentumScrollWithInitialVelocity() - animation " << *m_currentAnimation << " initialVelocity " << initialVelocity << " initialDelta " << initialDelta << " started " << started);
    155180    return started;
    156181}
  • trunk/Source/WebCore/platform/ScrollingEffectsController.h

    r285526 r285787  
    139139    bool retargetAnimatedScroll(FloatPoint newDestinationOffset);
    140140    bool retargetAnimatedScrollBy(FloatSize);
     141
    141142    void stopAnimatedScroll();
     143    void stopAnimatedNonRubberbandingScroll();
    142144
    143145    void stopKeyboardScrolling();
     
    222224    void startOrStopAnimationCallbacks();
    223225
     226    bool momentumScrollingAnimatorEnabled() const { return m_momentumScrollingAnimatorEnabled; }
     227
    224228    // ScrollAnimationClient
    225229    void scrollAnimationDidUpdate(ScrollAnimation&, const FloatPoint& /* currentOffset */) final;
     
    249253    bool m_isAnimatingKeyboardScrolling { false };
    250254    bool m_inScrollGesture { false };
     255    bool m_momentumScrollingAnimatorEnabled { false };
    251256
    252257#if PLATFORM(MAC)
     
    256261    FloatSize m_momentumVelocity;
    257262
     263    FloatSize m_scrollingVelocityForMomentumAnimation; // Do we need both this, m_scrollingVelocityForScrollSnap and m_momentumVelocity?
     264    FloatSize m_scrollingVelocityForScrollSnap;
     265#if !LOG_DISABLED
     266    FloatPoint m_eventDrivenScrollOffset;
     267#endif
     268
    258269    bool m_momentumScrollInProgress { false };
    259270    bool m_ignoreMomentumScrolls { false };
    260271    bool m_isRubberBanding { false };
    261272
    262     FloatSize m_dragEndedScrollingVelocity;
    263273    std::unique_ptr<ScrollingEffectsControllerTimer> m_statelessSnapTransitionTimer;
    264274
  • trunk/Source/WebCore/platform/mac/ScrollingEffectsController.mm

    r285387 r285787  
    3131#import "ScrollAnimationRubberBand.h"
    3232#import "ScrollExtents.h"
     33#import "WheelEventDeltaFilter.h"
    3334#import "WheelEventTestMonitor.h"
    3435#import <pal/spi/mac/NSScrollViewSPI.h>
     
    111112}
    112113
     114#if !LOG_DISABLED
     115static const char* phaseToString(PlatformWheelEventPhase phase)
     116{
     117    switch (phase) {
     118    case PlatformWheelEventPhase::None: return "none";
     119    case PlatformWheelEventPhase::Began: return "began";
     120    case PlatformWheelEventPhase::Stationary: return "stationary";
     121    case PlatformWheelEventPhase::Changed: return "changed";
     122    case PlatformWheelEventPhase::Ended: return "ended";
     123    case PlatformWheelEventPhase::Cancelled: return "cancelled";
     124    case PlatformWheelEventPhase::MayBegin: return "mayBegin";
     125    }
     126    return "";
     127}
     128#endif
     129
    113130bool ScrollingEffectsController::handleWheelEvent(const PlatformWheelEvent& wheelEvent)
    114131{
     132    if (WheelEventDeltaFilter::shouldApplyFilteringForEvent(wheelEvent))
     133        m_scrollingVelocityForMomentumAnimation = -wheelEvent.scrollingVelocity(); // Note that event delta is reversed from scroll direction.
     134
    115135    if (processWheelEventForScrollSnap(wheelEvent))
    116136        return true;
    117137
    118     if (wheelEvent.phase() == PlatformWheelEventPhase::MayBegin || wheelEvent.phase() == PlatformWheelEventPhase::Cancelled)
    119         return false;
     138    if (wheelEvent.phase() == PlatformWheelEventPhase::MayBegin || wheelEvent.phase() == PlatformWheelEventPhase::Cancelled) {
     139        if (momentumScrollingAnimatorEnabled()) {
     140            LOG(ScrollAnimations, "Event (%s, %s): stopping animated scroll", phaseToString(wheelEvent.phase()), phaseToString(wheelEvent.momentumPhase()));
     141            stopAnimatedScroll();
     142        }
     143        return false;
     144    }
    120145
    121146    if (wheelEvent.phase() == PlatformWheelEventPhase::Began) {
     147        if (momentumScrollingAnimatorEnabled()) {
     148            LOG(ScrollAnimations, "Event (%s, %s): stopping animated scroll", phaseToString(wheelEvent.phase()), phaseToString(wheelEvent.momentumPhase()));
     149            stopAnimatedScroll();
     150        }
     151
    122152        // FIXME: Trying to decide if a gesture is horizontal or vertical at the "began" phase is very error-prone.
    123153        auto horizontalSide = ScrollableArea::targetSideForScrollDelta(-wheelEvent.delta(), ScrollEventAxis::Horizontal);
     
    178208
    179209    auto momentumPhase = wheelEvent.momentumPhase();
    180     if (!m_momentumScrollInProgress && (momentumPhase == PlatformWheelEventPhase::Began || momentumPhase == PlatformWheelEventPhase::Changed))
     210    if (!m_momentumScrollInProgress && (momentumPhase == PlatformWheelEventPhase::Began || momentumPhase == PlatformWheelEventPhase::Changed)) {
    181211        m_momentumScrollInProgress = true;
     212        if (momentumScrollingAnimatorEnabled()) {
     213            startMomentumScrollWithInitialVelocity(m_client.scrollOffset(), m_scrollingVelocityForMomentumAnimation, -wheelEvent.delta(), [](const FloatPoint& targetOffset) { return targetOffset; });
     214#if !LOG_DISABLED
     215            m_eventDrivenScrollOffset = m_client.scrollOffset();
     216#endif
     217        }
     218    }
     219
     220    if (momentumPhase == PlatformWheelEventPhase::Changed && momentumScrollingAnimatorEnabled()) {
     221#if !LOG_DISABLED
     222        m_eventDrivenScrollOffset -= wheelEvent.delta();
     223#endif
     224        LOG(ScrollAnimations, "Event (%s, %s): ignoring - would have scrolled to %.2f,%.2f", phaseToString(wheelEvent.phase()), phaseToString(wheelEvent.momentumPhase()),
     225            m_eventDrivenScrollOffset.x(), m_eventDrivenScrollOffset.y());
     226        return true;
     227    }
    182228
    183229    bool shouldStretch = false;
     
    394440bool ScrollingEffectsController::startRubberBandAnimation(const FloatPoint& targetOffset, const FloatSize& initialVelocity, const FloatSize& initialOverscroll)
    395441{
     442    if (is<ScrollAnimationMomentum>(m_currentAnimation) && m_currentAnimation->isActive()) {
     443        LOG_WITH_STREAM(ScrollAnimations, stream << "ScrollingEffectsController::startRubberBandAnimation() - momentum animation is present");
     444        return false;
     445    }
     446
    396447    if (m_currentAnimation)
    397448        m_currentAnimation->stop();
     
    440491
    441492    auto stretchAmount = m_client.stretchAmount();
    442 
    443     LOG_WITH_STREAM(ScrollAnimations, stream << "ScrollingEffectsController::startRubberBandAnimationIfNecessary() - rubberBandAnimationRunning " << m_isAnimatingRubberBand << " stretchAmount " << stretchAmount << " targetOffset " << targetOffset);
    444 
    445493    if (stretchAmount.isZero() && !willOverscroll)
    446494        return;
     
    460508        initialVelocity.setHeight(0);
    461509
     510    LOG_WITH_STREAM(ScrollAnimations, stream << "ScrollingEffectsController::startRubberBandAnimationIfNecessary() - rubberBandAnimationRunning " << m_isAnimatingRubberBand << " stretchAmount " << stretchAmount << " contrainedOffset " << contrainedOffset << " initialVelocity " << initialVelocity);
    462511    startRubberBandAnimation(contrainedOffset, initialVelocity, stretchAmount);
    463512}
     
    636685        stopScrollSnapAnimation();
    637686        m_scrollSnapState->transitionToUserInteractionState();
    638         m_dragEndedScrollingVelocity = -wheelEvent.scrollingVelocity();
     687        m_scrollingVelocityForScrollSnap = -wheelEvent.scrollingVelocity();
    639688        break;
    640689    case WheelEventStatus::UserScrollEnd:
     
    643692        break;
    644693    case WheelEventStatus::MomentumScrollBegin:
    645         if (m_scrollSnapState->transitionToGlideAnimationState(m_client.scrollExtents(), m_client.pageScaleFactor(), m_client.scrollOffset(), m_dragEndedScrollingVelocity, FloatSize(-wheelEvent.deltaX(), -wheelEvent.deltaY()))) {
     694        if (m_scrollSnapState->transitionToGlideAnimationState(m_client.scrollExtents(), m_client.pageScaleFactor(), m_client.scrollOffset(), m_scrollingVelocityForScrollSnap, -wheelEvent.delta())) {
    646695            startScrollSnapAnimation();
    647696            isMomentumScrolling = true;
    648697        }
    649         m_dragEndedScrollingVelocity = { };
     698        m_scrollingVelocityForScrollSnap = { };
    650699        break;
    651700    case WheelEventStatus::MomentumScrolling:
Note: See TracChangeset for help on using the changeset viewer.