Changeset 181087 in webkit
- Timestamp:
- Mar 5, 2015 11:32:27 AM (9 years ago)
- Location:
- trunk/Source/WebCore
- Files:
-
- 10 edited
- 2 moved
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebCore/ChangeLog
r181085 r181087 1 2015-03-05 Brent Fulgham <bfulgham@apple.com> 2 3 Move AxisScrollSnapAnimator logic to ScrollController 4 https://bugs.webkit.org/show_bug.cgi?id=142293 5 <rdar://problem/20039867> 6 7 Reviewed by Dean Jackson. 8 9 No change in function. 10 11 Move the animation logic out of 'AxisScrollSnapAnimator' into 'ScrollController'. Rename the remaining 12 bits of 'AxisScrollSnapAnimator' as 'ScrollSnapAnimatorState'. Remove a number of delegate methods required 13 by 'AxisScrollSnapAnimatorClient' that are no longer needed. 14 15 Also, break up some of the Scroll Snap Point math to be a little easier to understand. 16 17 * WebCore.xcodeproj/project.pbxproj: Rename 'platform/mac/AxisScrollSnapAnimator.{h,mm}' -> 'platform/cocoa/ScrollSnapAnimatorState.h' 18 * page/scrolling/mac/ScrollingTreeFrameScrollingNodeMac.h: Mark 'scrollOffsetOnAxis' as const. 19 * page/scrolling/mac/ScrollingTreeFrameScrollingNodeMac.mm: 20 (WebCore::ScrollingTreeFrameScrollingNodeMac::scrollOffsetOnAxis): Make 'const' 21 * platform/ScrollAnimator.cpp: 22 (WebCore::ScrollAnimator::scrollOffsetOnAxis): Make 'const' 23 * platform/ScrollAnimator.h: 24 * platform/cocoa/ScrollController.h: No longer subclass from AxisScrollSnapAnimatorClient. 25 * platform/cocoa/ScrollController.mm: 26 (WebCore::ScrollController::scrollSnapPointState): Added accessors (const and non-const) for the individual 27 Scroll Snap State of each axis. 28 (WebCore::toWheelEventStatus): Moved from AxisScrollSnapAnimator. 29 (WebCore::ScrollController::processWheelEventForScrollSnapOnAxis): Added helper function containing the snap wheel handler code 30 from AxisScrollSnapAnimator. 31 (WebCore::ScrollController::shouldOverrideWheelEvent): Moved from AxisScrollSnapAnimator. 32 (WebCore::ScrollController::processWheelEventForScrollSnap): Update to use new methods moved from AxisScrollSnapAnimator. 33 (WebCore::ScrollController::updateScrollAnimatorsAndTimers): Update for new ScrollSnapAnimatorState class. 34 (WebCore::ScrollController::updateScrollSnapPoints): Ditto. 35 (WebCore::ScrollController::startScrollSnapTimer): Call client (delegate) method. 36 (WebCore::ScrollController::stopScrollSnapTimer): Ditto. 37 (WebCore::ScrollController::horizontalScrollSnapTimerFired): Call new 'scrollSnapAnimationUpdate' method passing the 38 correct axis to animate. 39 (WebCore::ScrollController::verticalScrollSnapTimerFired): Ditto. 40 (WebCore::ScrollController::scrollSnapAnimationUpdate): Moved from AxisScrollSnapAnimator. 41 (WebCore::projectedInertialScrollDistance): Moved from AxisScrollSnapAnimator. 42 (WebCore::ScrollController::initializeGlideParameters): Ditto. 43 (WebCore::ScrollController::beginScrollSnapAnimation): Ditto. 44 (WebCore::ScrollController::endScrollSnapAnimation): Ditto. 45 (WebCore::snapProgress): Created a new function for this calculation to make reasoning about the 'computeSnapDelta' and 46 'computeGlideDelta' easier. 47 (WebCore::clampedSnapMagnitude): Ditto. 48 (WebCore::ScrollController::computeSnapDelta): Moved from AxisScrollSnapAnimator. 49 (WebCore::snapGlide): Created a new function for this calculation to make reasoning about the 'computeGlideDelta' easier. 50 (WebCore::ScrollController::computeGlideDelta): Moved from AxisScrollSnapAnimator. 51 (WebCore::ScrollController::scrollOffsetOnAxis): Deleted. 52 (WebCore::ScrollController::immediateScrollOnAxis): Deleted. 53 * platform/cocoa/ScrollSnapAnimatorState.h: Copied from platform/mac/AxisScrollSnapAnimator.h. 54 (WebCore::AxisScrollSnapAnimatorClient::~AxisScrollSnapAnimatorClient): Deleted. 55 * platform/cocoa/ScrollSnapAnimatorState.mm: Copied from platform/mac/AxisScrollSnapAnimator.mm. 56 (WebCore::ScrollSnapAnimatorState::ScrollSnapAnimatorState): 57 (WebCore::ScrollSnapAnimatorState::pushInitialWheelDelta): 58 (WebCore::ScrollSnapAnimatorState::averageInitialWheelDelta): 59 (WebCore::ScrollSnapAnimatorState::clearInitialWheelDeltaWindow): 60 (WebCore::toWheelEventStatus): Deleted. 61 (WebCore::projectedInertialScrollDistance): Deleted. 62 (WebCore::AxisScrollSnapAnimator::AxisScrollSnapAnimator): Deleted. 63 (WebCore::AxisScrollSnapAnimator::handleWheelEvent): Deleted. 64 (WebCore::AxisScrollSnapAnimator::shouldOverrideWheelEvent): Deleted. 65 (WebCore::AxisScrollSnapAnimator::scrollSnapAnimationUpdate): Deleted. 66 (WebCore::AxisScrollSnapAnimator::beginScrollSnapAnimation): Deleted. 67 (WebCore::AxisScrollSnapAnimator::endScrollSnapAnimation): Deleted. 68 (WebCore::AxisScrollSnapAnimator::computeSnapDelta): Deleted. 69 (WebCore::AxisScrollSnapAnimator::computeGlideDelta): Deleted. 70 (WebCore::AxisScrollSnapAnimator::initializeGlideParameters): Deleted. 71 (WebCore::AxisScrollSnapAnimator::pushInitialWheelDelta): Deleted. 72 (WebCore::AxisScrollSnapAnimator::averageInitialWheelDelta): Deleted. 73 (WebCore::AxisScrollSnapAnimator::clearInitialWheelDeltaWindow): Deleted. 74 * platform/mac/AxisScrollSnapAnimator.h: Removed. 75 * platform/mac/AxisScrollSnapAnimator.mm: Removed. 76 * platform/mac/ScrollAnimatorMac.h: 77 * platform/mac/ScrollAnimatorMac.mm: 78 (WebCore::ScrollAnimatorMac::pinnedInDirection): Removed. 79 1 80 2015-03-04 Dean Jackson <dino@apple.com> 2 81 -
trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj
r181058 r181087 6329 6329 F45C231D1995B73B00A6E2E3 /* AxisScrollSnapOffsets.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F45C231B1995B73B00A6E2E3 /* AxisScrollSnapOffsets.cpp */; }; 6330 6330 F45C231E1995B73B00A6E2E3 /* AxisScrollSnapOffsets.h in Headers */ = {isa = PBXBuildFile; fileRef = F45C231C1995B73B00A6E2E3 /* AxisScrollSnapOffsets.h */; settings = {ATTRIBUTES = (Private, ); }; }; 6331 F478755419983AFF0024A287 /* AxisScrollSnapAnimator.h in Headers */ = {isa = PBXBuildFile; fileRef = F478755219983AFF0024A287 /* AxisScrollSnapAnimator.h */; settings = {ATTRIBUTES = (Private, ); }; };6332 F478755519983AFF0024A287 /* AxisScrollSnapAnimator.mm in Sources */ = {isa = PBXBuildFile; fileRef = F478755319983AFF0024A287 /* AxisScrollSnapAnimator.mm */; };6331 F478755419983AFF0024A287 /* ScrollSnapAnimatorState.h in Headers */ = {isa = PBXBuildFile; fileRef = F478755219983AFF0024A287 /* ScrollSnapAnimatorState.h */; settings = {ATTRIBUTES = (Private, ); }; }; 6332 F478755519983AFF0024A287 /* ScrollSnapAnimatorState.mm in Sources */ = {isa = PBXBuildFile; fileRef = F478755319983AFF0024A287 /* ScrollSnapAnimatorState.mm */; }; 6333 6333 F47A5E3E195B8C8A00483100 /* StyleScrollSnapPoints.h in Headers */ = {isa = PBXBuildFile; fileRef = F47A5E3B195B8C8A00483100 /* StyleScrollSnapPoints.h */; settings = {ATTRIBUTES = (Private, ); }; }; 6334 6334 F47A5E3F195B8E4800483100 /* StyleScrollSnapPoints.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F47A5E3A195B8C8A00483100 /* StyleScrollSnapPoints.cpp */; }; … … 13903 13903 F45C231B1995B73B00A6E2E3 /* AxisScrollSnapOffsets.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AxisScrollSnapOffsets.cpp; sourceTree = "<group>"; }; 13904 13904 F45C231C1995B73B00A6E2E3 /* AxisScrollSnapOffsets.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AxisScrollSnapOffsets.h; sourceTree = "<group>"; }; 13905 F478755219983AFF0024A287 /* AxisScrollSnapAnimator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AxisScrollSnapAnimator.h; sourceTree = "<group>"; };13906 F478755319983AFF0024A287 /* AxisScrollSnapAnimator.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AxisScrollSnapAnimator.mm; sourceTree = "<group>"; };13905 F478755219983AFF0024A287 /* ScrollSnapAnimatorState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScrollSnapAnimatorState.h; sourceTree = "<group>"; }; 13906 F478755319983AFF0024A287 /* ScrollSnapAnimatorState.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ScrollSnapAnimatorState.mm; sourceTree = "<group>"; }; 13907 13907 F47A5E3A195B8C8A00483100 /* StyleScrollSnapPoints.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StyleScrollSnapPoints.cpp; path = style/StyleScrollSnapPoints.cpp; sourceTree = "<group>"; }; 13908 13908 F47A5E3B195B8C8A00483100 /* StyleScrollSnapPoints.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StyleScrollSnapPoints.h; path = style/StyleScrollSnapPoints.h; sourceTree = "<group>"; }; … … 16578 16578 isa = PBXGroup; 16579 16579 children = ( 16580 F478755219983AFF0024A287 /* AxisScrollSnapAnimator.h */,16581 F478755319983AFF0024A287 /* AxisScrollSnapAnimator.mm */,16582 16580 65A640F00533BB1F0085E777 /* BlockExceptions.h */, 16583 16581 65F80697054D9F86008BF776 /* BlockExceptions.mm */, … … 18716 18714 isa = PBXGroup; 18717 18715 children = ( 18716 F478755219983AFF0024A287 /* ScrollSnapAnimatorState.h */, 18717 F478755319983AFF0024A287 /* ScrollSnapAnimatorState.mm */, 18718 18718 A14090FA1AA51E1D0091191A /* ContentFilterUnblockHandlerCocoa.mm */, 18719 18719 5D8C4DBD1428222C0026CE72 /* DisplaySleepDisablerCocoa.cpp */, … … 23622 23622 CD336F6217F9F64700DDDCD0 /* AVTrackPrivateAVFObjCImpl.h in Headers */, 23623 23623 070363E6181A1CDC00C074A5 /* AVVideoCaptureSource.h in Headers */, 23624 F478755419983AFF0024A287 /* AxisScrollSnapAnimator.h in Headers */,23624 F478755419983AFF0024A287 /* ScrollSnapAnimatorState.h in Headers */, 23625 23625 F45C231E1995B73B00A6E2E3 /* AxisScrollSnapOffsets.h in Headers */, 23626 23626 29A812380FBB9C1D00510293 /* AXObjectCache.h in Headers */, … … 27360 27360 CD336F6117F9F64700DDDCD0 /* AVTrackPrivateAVFObjCImpl.mm in Sources */, 27361 27361 070363E7181A1CDC00C074A5 /* AVVideoCaptureSource.mm in Sources */, 27362 F478755519983AFF0024A287 /* AxisScrollSnapAnimator.mm in Sources */,27362 F478755519983AFF0024A287 /* ScrollSnapAnimatorState.mm in Sources */, 27363 27363 F45C231D1995B73B00A6E2E3 /* AxisScrollSnapOffsets.cpp in Sources */, 27364 27364 37F57ACF1A5072DD00876F98 /* AXObjectCache.cpp in Sources */, -
trunk/Source/WebCore/page/scrolling/mac/ScrollingTreeFrameScrollingNodeMac.h
r180974 r181087 81 81 82 82 #if ENABLE(CSS_SCROLL_SNAP) && PLATFORM(MAC) 83 LayoutUnit scrollOffsetOnAxis(ScrollEventAxis) override;83 LayoutUnit scrollOffsetOnAxis(ScrollEventAxis) const override; 84 84 void immediateScrollOnAxis(ScrollEventAxis, float delta) override; 85 85 #endif -
trunk/Source/WebCore/page/scrolling/mac/ScrollingTreeFrameScrollingNodeMac.mm
r180987 r181087 542 542 543 543 #if ENABLE(CSS_SCROLL_SNAP) && PLATFORM(MAC) 544 LayoutUnit ScrollingTreeFrameScrollingNodeMac::scrollOffsetOnAxis(ScrollEventAxis axis) 544 LayoutUnit ScrollingTreeFrameScrollingNodeMac::scrollOffsetOnAxis(ScrollEventAxis axis) const 545 545 { 546 546 const FloatPoint& currentPosition = scrollPosition(); -
trunk/Source/WebCore/platform/ScrollAnimator.cpp
r180974 r181087 179 179 } 180 180 181 LayoutUnit ScrollAnimator::scrollOffsetOnAxis(ScrollEventAxis axis) 181 LayoutUnit ScrollAnimator::scrollOffsetOnAxis(ScrollEventAxis axis) const 182 182 { 183 183 return axis == ScrollEventAxis::Horizontal ? m_currentPosX : m_currentPosY; -
trunk/Source/WebCore/platform/ScrollAnimator.h
r180974 r181087 121 121 bool processWheelEventForScrollSnap(const PlatformWheelEvent&); 122 122 void updateScrollAnimatorsAndTimers(); 123 LayoutUnit scrollOffsetOnAxis(ScrollEventAxis) override;123 LayoutUnit scrollOffsetOnAxis(ScrollEventAxis) const override; 124 124 void immediateScrollOnAxis(ScrollEventAxis, float delta) override; 125 125 #endif -
trunk/Source/WebCore/platform/cocoa/ScrollController.h
r180987 r181087 36 36 37 37 #if ENABLE(CSS_SCROLL_SNAP) 38 #include " AxisScrollSnapAnimator.h"38 #include "ScrollSnapAnimatorState.h" 39 39 #endif 40 40 … … 77 77 78 78 #if ENABLE(CSS_SCROLL_SNAP) && PLATFORM(MAC) 79 virtual LayoutUnit scrollOffsetOnAxis(ScrollEventAxis) = 0;79 virtual LayoutUnit scrollOffsetOnAxis(ScrollEventAxis) const = 0; 80 80 virtual void immediateScrollOnAxis(ScrollEventAxis, float delta) = 0; 81 81 virtual void startScrollSnapTimer(ScrollEventAxis) … … 92 92 }; 93 93 94 #if ENABLE(CSS_SCROLL_SNAP) && PLATFORM(MAC)95 class ScrollController : public AxisScrollSnapAnimatorClient {96 #else97 94 class ScrollController { 98 #endif99 95 WTF_MAKE_NONCOPYABLE(ScrollController); 100 96 … … 123 119 void horizontalScrollSnapTimerFired(); 124 120 void verticalScrollSnapTimerFired(); 125 void startScrollSnapTimer(ScrollEventAxis) override;126 void stopScrollSnapTimer(ScrollEventAxis) override;121 void startScrollSnapTimer(ScrollEventAxis); 122 void stopScrollSnapTimer(ScrollEventAxis); 127 123 128 LayoutUnit scrollOffsetOnAxis(ScrollEventAxis) override; 129 void immediateScrollOnAxis(ScrollEventAxis, float delta) override; 124 LayoutUnit scrollOffsetOnAxis(ScrollEventAxis) const; 125 void processWheelEventForScrollSnapOnAxis(ScrollEventAxis, const PlatformWheelEvent&); 126 bool shouldOverrideWheelEvent(ScrollEventAxis, const PlatformWheelEvent&) const; 127 128 void beginScrollSnapAnimation(ScrollEventAxis, ScrollSnapState); 129 void scrollSnapAnimationUpdate(ScrollEventAxis); 130 void endScrollSnapAnimation(ScrollEventAxis, ScrollSnapState); 131 132 void initializeGlideParameters(ScrollEventAxis, bool); 133 float computeSnapDelta(ScrollEventAxis) const; 134 float computeGlideDelta(ScrollEventAxis) const; 135 136 ScrollSnapAnimatorState& scrollSnapPointState(ScrollEventAxis); 137 const ScrollSnapAnimatorState& scrollSnapPointState(ScrollEventAxis) const; 130 138 #endif 131 139 … … 146 154 #if ENABLE(CSS_SCROLL_SNAP) && PLATFORM(MAC) 147 155 // FIXME: Find a way to consolidate both timers into one variable. 148 std::unique_ptr< AxisScrollSnapAnimator> m_horizontalScrollSnapAnimator;149 std::unique_ptr< AxisScrollSnapAnimator> m_verticalScrollSnapAnimator;156 std::unique_ptr<ScrollSnapAnimatorState> m_horizontalScrollSnapState; 157 std::unique_ptr<ScrollSnapAnimatorState> m_verticalScrollSnapState; 150 158 RunLoop::Timer<ScrollController> m_horizontalScrollSnapTimer; 151 159 RunLoop::Timer<ScrollController> m_verticalScrollSnapTimer; -
trunk/Source/WebCore/platform/cocoa/ScrollController.mm
r180987 r181087 33 33 34 34 #if ENABLE(CSS_SCROLL_SNAP) 35 #include " AxisScrollSnapAnimator.h"35 #include "ScrollSnapAnimatorState.h" 36 36 #include "ScrollableArea.h" 37 37 #endif … … 68 68 static const float rubberbandDirectionLockStretchRatio = 1; 69 69 static const float rubberbandMinimumRequiredDeltaBeforeStretch = 10; 70 71 #if ENABLE(CSS_SCROLL_SNAP) && PLATFORM(MAC) 72 static const float snapMagnitudeMax = 25; 73 static const float snapMagnitudeMin = 5; 74 static const float snapThresholdHigh = 1000; 75 static const float snapThresholdLow = 50; 76 77 static const float inertialScrollPredictionFactor = 16.7; 78 static const float initialToFinalMomentumFactor = 1.0 / 40.0; 79 80 static const float glideBoostMultiplier = 3.5; 81 82 static const float maxTargetWheelDelta = 7; 83 static const float minTargetWheelDelta = 3.5; 84 #endif 85 86 enum class WheelEventStatus { 87 UserScrollBegin, 88 UserScrolling, 89 UserScrollEnd, 90 InertialScrollBegin, 91 InertialScrolling, 92 InertialScrollEnd, 93 Unknown 94 }; 70 95 71 96 static float elasticDeltaForTimeDelta(float initialPosition, float initialVelocity, float elapsedTime) … … 424 449 425 450 #if ENABLE(CSS_SCROLL_SNAP) && PLATFORM(MAC) 451 ScrollSnapAnimatorState& ScrollController::scrollSnapPointState(ScrollEventAxis axis) 452 { 453 ASSERT(axis != ScrollEventAxis::Horizontal || m_horizontalScrollSnapState); 454 ASSERT(axis != ScrollEventAxis::Vertical || m_verticalScrollSnapState); 455 456 return (axis == ScrollEventAxis::Horizontal) ? *m_horizontalScrollSnapState : *m_verticalScrollSnapState; 457 } 458 459 const ScrollSnapAnimatorState& ScrollController::scrollSnapPointState(ScrollEventAxis axis) const 460 { 461 ASSERT(axis != ScrollEventAxis::Horizontal || m_horizontalScrollSnapState); 462 ASSERT(axis != ScrollEventAxis::Vertical || m_verticalScrollSnapState); 463 464 return (axis == ScrollEventAxis::Horizontal) ? *m_horizontalScrollSnapState : *m_verticalScrollSnapState; 465 } 466 467 static inline WheelEventStatus toWheelEventStatus(PlatformWheelEventPhase phase, PlatformWheelEventPhase momentumPhase) 468 { 469 if (phase == PlatformWheelEventPhaseNone) { 470 switch (momentumPhase) { 471 case PlatformWheelEventPhaseBegan: 472 return WheelEventStatus::InertialScrollBegin; 473 474 case PlatformWheelEventPhaseChanged: 475 return WheelEventStatus::InertialScrolling; 476 477 case PlatformWheelEventPhaseEnded: 478 return WheelEventStatus::InertialScrollEnd; 479 480 default: 481 return WheelEventStatus::Unknown; 482 } 483 } 484 if (momentumPhase == PlatformWheelEventPhaseNone) { 485 switch (phase) { 486 case PlatformWheelEventPhaseBegan: 487 case PlatformWheelEventPhaseMayBegin: 488 return WheelEventStatus::UserScrollBegin; 489 490 case PlatformWheelEventPhaseChanged: 491 return WheelEventStatus::UserScrolling; 492 493 case PlatformWheelEventPhaseEnded: 494 case PlatformWheelEventPhaseCancelled: 495 return WheelEventStatus::UserScrollEnd; 496 497 default: 498 return WheelEventStatus::Unknown; 499 } 500 } 501 return WheelEventStatus::Unknown; 502 } 503 504 void ScrollController::processWheelEventForScrollSnapOnAxis(ScrollEventAxis axis, const PlatformWheelEvent& event) 505 { 506 ScrollSnapAnimatorState& snapState = scrollSnapPointState(axis); 507 508 float wheelDelta = axis == ScrollEventAxis::Horizontal ? -event.deltaX() : -event.deltaY(); 509 WheelEventStatus wheelStatus = toWheelEventStatus(event.phase(), event.momentumPhase()); 510 511 switch (wheelStatus) { 512 case WheelEventStatus::UserScrollBegin: 513 case WheelEventStatus::UserScrolling: 514 endScrollSnapAnimation(axis, ScrollSnapState::UserInteraction); 515 break; 516 517 case WheelEventStatus::UserScrollEnd: 518 beginScrollSnapAnimation(axis, ScrollSnapState::Snapping); 519 break; 520 521 case WheelEventStatus::InertialScrollBegin: 522 // Begin tracking wheel deltas for glide prediction. 523 endScrollSnapAnimation(axis, ScrollSnapState::UserInteraction); 524 snapState.pushInitialWheelDelta(wheelDelta); 525 snapState.m_beginTrackingWheelDeltaOffset = m_client->scrollOffsetOnAxis(axis); 526 break; 527 528 case WheelEventStatus::InertialScrolling: 529 // This check for DestinationReached ensures that we don't receive another set of momentum events after ending the last glide. 530 if (snapState.m_currentState != ScrollSnapState::Gliding && snapState.m_currentState != ScrollSnapState::DestinationReached) { 531 if (snapState.m_numWheelDeltasTracked < snapState.wheelDeltaWindowSize) 532 snapState.pushInitialWheelDelta(wheelDelta); 533 534 if (snapState.m_numWheelDeltasTracked == snapState.wheelDeltaWindowSize) 535 beginScrollSnapAnimation(axis, ScrollSnapState::Gliding); 536 } 537 break; 538 539 case WheelEventStatus::InertialScrollEnd: 540 beginScrollSnapAnimation(axis, ScrollSnapState::Snapping); 541 snapState.clearInitialWheelDeltaWindow(); 542 snapState.m_shouldOverrideWheelEvent = false; 543 break; 544 545 case WheelEventStatus::Unknown: 546 ASSERT_NOT_REACHED(); 547 break; 548 } 549 } 550 551 bool ScrollController::shouldOverrideWheelEvent(ScrollEventAxis axis, const PlatformWheelEvent& event) const 552 { 553 const ScrollSnapAnimatorState& snapState = scrollSnapPointState(axis); 554 555 return snapState.m_shouldOverrideWheelEvent && toWheelEventStatus(event.phase(), event.momentumPhase()) == WheelEventStatus::InertialScrolling; 556 } 557 426 558 bool ScrollController::processWheelEventForScrollSnap(const PlatformWheelEvent& wheelEvent) 427 559 { 428 if (m_verticalScrollSnap Animator) {429 m_verticalScrollSnapAnimator->handleWheelEvent(wheelEvent);430 if ( m_verticalScrollSnapAnimator->shouldOverrideWheelEvent(wheelEvent))560 if (m_verticalScrollSnapState) { 561 processWheelEventForScrollSnapOnAxis(ScrollEventAxis::Vertical, wheelEvent); 562 if (shouldOverrideWheelEvent(ScrollEventAxis::Vertical, wheelEvent)) 431 563 return false; 432 564 } 433 if (m_horizontalScrollSnap Animator) {434 m_horizontalScrollSnapAnimator->handleWheelEvent(wheelEvent);435 if ( m_horizontalScrollSnapAnimator->shouldOverrideWheelEvent(wheelEvent))565 if (m_horizontalScrollSnapState) { 566 processWheelEventForScrollSnapOnAxis(ScrollEventAxis::Horizontal, wheelEvent); 567 if (shouldOverrideWheelEvent(ScrollEventAxis::Horizontal, wheelEvent)) 436 568 return false; 437 569 } … … 444 576 // FIXME: Currently, scroll snap animators are recreated even though the snap offsets alone can be updated. 445 577 if (scrollableArea.horizontalSnapOffsets()) 446 m_horizontalScrollSnap Animator = std::make_unique<AxisScrollSnapAnimator>(this, *scrollableArea.horizontalSnapOffsets(), ScrollEventAxis::Horizontal);447 else if (m_horizontalScrollSnap Animator)448 m_horizontalScrollSnap Animator= nullptr;578 m_horizontalScrollSnapState = std::make_unique<ScrollSnapAnimatorState>(ScrollEventAxis::Horizontal, *scrollableArea.horizontalSnapOffsets()); 579 else if (m_horizontalScrollSnapState) 580 m_horizontalScrollSnapState = nullptr; 449 581 450 582 if (scrollableArea.verticalSnapOffsets()) 451 m_verticalScrollSnap Animator = std::make_unique<AxisScrollSnapAnimator>(this, *scrollableArea.verticalSnapOffsets(), ScrollEventAxis::Vertical);452 else if (m_verticalScrollSnap Animator)453 m_verticalScrollSnap Animator= nullptr;583 m_verticalScrollSnapState = std::make_unique<ScrollSnapAnimatorState>(ScrollEventAxis::Vertical, *scrollableArea.verticalSnapOffsets()); 584 else if (m_verticalScrollSnapState) 585 m_verticalScrollSnapState = nullptr; 454 586 } 455 587 … … 458 590 // FIXME: Currently, scroll snap animators are recreated even though the snap offsets alone can be updated. 459 591 if (axis == ScrollEventAxis::Horizontal) 460 m_horizontalScrollSnap Animator = std::make_unique<AxisScrollSnapAnimator>(this, snapPoints, ScrollEventAxis::Horizontal);592 m_horizontalScrollSnapState = std::make_unique<ScrollSnapAnimatorState>(ScrollEventAxis::Horizontal, snapPoints); 461 593 462 594 if (axis == ScrollEventAxis::Vertical) 463 m_verticalScrollSnap Animator = std::make_unique<AxisScrollSnapAnimator>(this, snapPoints, ScrollEventAxis::Vertical);595 m_verticalScrollSnapState = std::make_unique<ScrollSnapAnimatorState>(ScrollEventAxis::Vertical, snapPoints); 464 596 } 465 597 … … 467 599 { 468 600 RunLoop::Timer<ScrollController>& scrollSnapTimer = axis == ScrollEventAxis::Horizontal ? m_horizontalScrollSnapTimer : m_verticalScrollSnapTimer; 469 if (!scrollSnapTimer.isActive()) 601 if (!scrollSnapTimer.isActive()) { 602 m_client->startScrollSnapTimer(axis); 470 603 scrollSnapTimer.startRepeating(1.0 / 60.0); 604 } 471 605 } 472 606 473 607 void ScrollController::stopScrollSnapTimer(ScrollEventAxis axis) 474 608 { 609 m_client->stopScrollSnapTimer(axis); 475 610 RunLoop::Timer<ScrollController>& scrollSnapTimer = axis == ScrollEventAxis::Horizontal ? m_horizontalScrollSnapTimer : m_verticalScrollSnapTimer; 476 611 scrollSnapTimer.stop(); … … 479 614 void ScrollController::horizontalScrollSnapTimerFired() 480 615 { 481 if (m_horizontalScrollSnapAnimator) 482 m_horizontalScrollSnapAnimator->scrollSnapAnimationUpdate(); 616 scrollSnapAnimationUpdate(ScrollEventAxis::Horizontal); 483 617 } 484 618 485 619 void ScrollController::verticalScrollSnapTimerFired() 486 620 { 487 if (m_verticalScrollSnapAnimator) 488 m_verticalScrollSnapAnimator->scrollSnapAnimationUpdate(); 489 } 490 491 LayoutUnit ScrollController::scrollOffsetOnAxis(ScrollEventAxis axis) 492 { 493 return m_client->scrollOffsetOnAxis(axis); 494 } 495 496 void ScrollController::immediateScrollOnAxis(ScrollEventAxis axis, float delta) 497 { 498 m_client->immediateScrollOnAxis(axis, delta); 621 scrollSnapAnimationUpdate(ScrollEventAxis::Vertical); 622 } 623 624 void ScrollController::scrollSnapAnimationUpdate(ScrollEventAxis axis) 625 { 626 if (axis == ScrollEventAxis::Horizontal && !m_horizontalScrollSnapState) 627 return; 628 629 if (axis == ScrollEventAxis::Vertical && !m_verticalScrollSnapState) 630 return; 631 632 ScrollSnapAnimatorState& snapState = scrollSnapPointState(axis); 633 if (snapState.m_currentState == ScrollSnapState::DestinationReached) 634 return; 635 636 ASSERT(snapState.m_currentState == ScrollSnapState::Gliding || snapState.m_currentState == ScrollSnapState::Snapping); 637 float delta = snapState.m_currentState == ScrollSnapState::Snapping ? computeSnapDelta(axis) : computeGlideDelta(axis); 638 if (delta) 639 m_client->immediateScrollOnAxis(axis, delta); 640 else 641 endScrollSnapAnimation(axis, ScrollSnapState::DestinationReached); 642 } 643 644 static inline float projectedInertialScrollDistance(float initialWheelDelta) 645 { 646 // FIXME: Experiments with inertial scrolling show a fairly consistent linear relationship between initial wheel delta and total distance scrolled. 647 // In the future, we'll want to find a more accurate way of inertial scroll prediction. 648 return inertialScrollPredictionFactor * initialWheelDelta; 649 } 650 651 void ScrollController::initializeGlideParameters(ScrollEventAxis axis, bool shouldIncreaseInitialWheelDelta) 652 { 653 ScrollSnapAnimatorState& snapState = scrollSnapPointState(axis); 654 655 // FIXME: Glide boost is a hacky way to speed up natural scrolling velocity. We should find a better way to accomplish this. 656 if (shouldIncreaseInitialWheelDelta) 657 snapState.m_glideInitialWheelDelta *= glideBoostMultiplier; 658 659 // FIXME: There must be a better way to determine a good target delta than multiplying by a factor and clamping to min/max values. 660 float targetFinalWheelDelta = initialToFinalMomentumFactor * (snapState.m_glideInitialWheelDelta < 0 ? -snapState.m_glideInitialWheelDelta : snapState.m_glideInitialWheelDelta); 661 targetFinalWheelDelta = (snapState.m_glideInitialWheelDelta > 0 ? 1 : -1) * std::min(std::max(targetFinalWheelDelta, minTargetWheelDelta), maxTargetWheelDelta); 662 snapState.m_glideMagnitude = (snapState.m_glideInitialWheelDelta + targetFinalWheelDelta) / 2; 663 snapState.m_glidePhaseShift = acos((snapState.m_glideInitialWheelDelta - targetFinalWheelDelta) / (snapState.m_glideInitialWheelDelta + targetFinalWheelDelta)); 664 } 665 666 void ScrollController::beginScrollSnapAnimation(ScrollEventAxis axis, ScrollSnapState newState) 667 { 668 ASSERT(newState == ScrollSnapState::Gliding || newState == ScrollSnapState::Snapping); 669 670 ScrollSnapAnimatorState& snapState = scrollSnapPointState(axis); 671 672 LayoutUnit offset = m_client->scrollOffsetOnAxis(axis); 673 float initialWheelDelta = newState == ScrollSnapState::Gliding ? snapState.averageInitialWheelDelta() : 0; 674 LayoutUnit projectedScrollDestination = newState == ScrollSnapState::Gliding ? snapState.m_beginTrackingWheelDeltaOffset + LayoutUnit(projectedInertialScrollDistance(initialWheelDelta)) : offset; 675 if (snapState.m_snapOffsets.isEmpty()) 676 return; 677 678 projectedScrollDestination = std::min(std::max(projectedScrollDestination, snapState.m_snapOffsets.first()), snapState.m_snapOffsets.last()); 679 snapState.m_initialOffset = offset; 680 snapState.m_targetOffset = closestSnapOffset<LayoutUnit, float>(snapState.m_snapOffsets, projectedScrollDestination, initialWheelDelta); 681 if (snapState.m_initialOffset == snapState.m_targetOffset) 682 return; 683 684 snapState.m_currentState = newState; 685 if (newState == ScrollSnapState::Gliding) { 686 snapState.m_shouldOverrideWheelEvent = true; 687 snapState.m_glideInitialWheelDelta = initialWheelDelta; 688 bool glideRequiresBoost; 689 if (initialWheelDelta > 0) 690 glideRequiresBoost = projectedScrollDestination - offset < snapState.m_targetOffset - projectedScrollDestination; 691 else 692 glideRequiresBoost = offset - projectedScrollDestination < projectedScrollDestination - snapState.m_targetOffset; 693 694 initializeGlideParameters(axis, glideRequiresBoost); 695 snapState.clearInitialWheelDeltaWindow(); 696 } 697 startScrollSnapTimer(axis); 698 } 699 700 void ScrollController::endScrollSnapAnimation(ScrollEventAxis axis, ScrollSnapState newState) 701 { 702 ASSERT(newState == ScrollSnapState::DestinationReached || newState == ScrollSnapState::UserInteraction); 703 704 ScrollSnapAnimatorState& snapState = scrollSnapPointState(axis); 705 706 if (snapState.m_currentState == ScrollSnapState::Gliding) 707 snapState.clearInitialWheelDeltaWindow(); 708 709 snapState.m_currentState = newState; 710 stopScrollSnapTimer(axis); 711 } 712 713 static inline float snapProgress(const LayoutUnit& offset, const ScrollSnapAnimatorState& snapState) 714 { 715 const float distanceTraveled = static_cast<float>(offset - snapState.m_initialOffset); 716 const float totalDistance = static_cast<float>(snapState.m_targetOffset - snapState.m_initialOffset); 717 718 return distanceTraveled / totalDistance; 719 } 720 721 static inline float clampedSnapMagnitude(float thresholdedDistance) 722 { 723 return snapMagnitudeMin + (snapMagnitudeMax - snapMagnitudeMin) * (thresholdedDistance - snapThresholdLow) / (snapThresholdHigh - snapThresholdLow); 724 } 725 726 // Computes the amount to scroll by when performing a "snap" operation, i.e. when a user releases the trackpad without flicking. The snap delta 727 // is a function of progress t, where t is equal to DISTANCE_TRAVELED / TOTAL_DISTANCE, DISTANCE_TRAVELED is the distance from the initialOffset 728 // to the current offset, and TOTAL_DISTANCE is the distance from initialOffset to targetOffset. The snapping equation is as follows: 729 // delta(t) = MAGNITUDE * sin(PI * t). MAGNITUDE indicates the top speed reached near the middle of the animation (t = 0.5), and is a linear 730 // relationship of the distance traveled, clamped by arbitrary min and max values. 731 float ScrollController::computeSnapDelta(ScrollEventAxis axis) const 732 { 733 const ScrollSnapAnimatorState& snapState = scrollSnapPointState(axis); 734 735 LayoutUnit offset = m_client->scrollOffsetOnAxis(axis); 736 bool canComputeSnap = (snapState.m_initialOffset <= offset && offset < snapState.m_targetOffset) || (snapState.m_targetOffset < offset && offset <= snapState.m_initialOffset); 737 if (snapState.m_currentState != ScrollSnapState::Snapping || !canComputeSnap) 738 return 0; 739 740 float progress = snapProgress(offset, snapState); 741 742 // Threshold the distance before computing magnitude, so only distances within a certain range are considered. 743 int sign = snapState.m_initialOffset < snapState.m_targetOffset ? 1 : -1; 744 float thresholdedDistance = std::min(std::max<float>((snapState.m_targetOffset - snapState.m_initialOffset) * sign, snapThresholdLow), snapThresholdHigh); 745 746 float magnitude = clampedSnapMagnitude(thresholdedDistance); 747 748 float rawSnapDelta = std::max<float>(1, magnitude * std::sin(piFloat * progress)); 749 if ((snapState.m_targetOffset < offset && offset - rawSnapDelta < snapState.m_targetOffset) || (snapState.m_targetOffset > offset && offset + rawSnapDelta > snapState.m_targetOffset)) 750 return snapState.m_targetOffset - offset; 751 752 return sign * rawSnapDelta; 753 } 754 755 static inline float snapGlide(float progress, const ScrollSnapAnimatorState& snapState) 756 { 757 // FIXME: We might want to investigate why -m_glidePhaseShift results in the behavior we want. 758 return ceil(snapState.m_glideMagnitude * (1.0f + std::cos(piFloat * progress - snapState.m_glidePhaseShift))); 759 } 760 761 // Computes the amount to scroll by when performing a "glide" operation, i.e. when a user releases the trackpad with an initial velocity. Here, 762 // we want the scroll offset to animate directly to the snap point. 763 // 764 // The snap delta is a function of progress t, where: (1) t is equal to DISTANCE_TRAVELED / TOTAL_DISTANCE, (2) DISTANCE_TRAVELED is the distance 765 // from the initialOffset to the current offset, and (3) TOTAL_DISTANCE is the distance from initialOffset to targetOffset. 766 // 767 // The general model of our gliding equation is delta(t) = MAGNITUDE * (1 + cos(PI * t + PHASE_SHIFT)). This was determined after examining the 768 // momentum velocity curve as a function of progress. To compute MAGNITUDE and PHASE_SHIFT, we use initial velocity V0 and the final velocity VF, 769 // both as wheel deltas (pixels per timestep). VF should be a small value (< 10) chosen based on the initial velocity and TOTAL_DISTANCE. 770 // We also enforce the following constraints for the gliding equation: 771 // 1. delta(0) = V0, since we want the initial velocity of the gliding animation to match the user's scroll velocity. The exception to this is 772 // when the glide velocity is not enough to naturally reach the next snap point, and thus requires a boost (see initializeGlideParameters) 773 // 2. delta(1) = VF, since at t=1, the animation has completed and we want the last wheel delta to match the final velocity VF. Note that this 774 // doesn't guarantee that the final velocity will be exactly VF. However, assuming that the initial velocity is much less than TOTAL_DISTANCE, 775 // the last wheel delta will be very close, if not the same, as VF. 776 // For MAGNITUDE = (V0 + VF) / 2 and PHASE_SHIFT = arccos((V0 - VF) / (V0 + VF)), observe that delta(0) and delta(1) evaluate respectively to V0 777 // and VF. Thus, we can express our gliding equation all in terms of V0, VF and t. 778 float ScrollController::computeGlideDelta(ScrollEventAxis axis) const 779 { 780 const ScrollSnapAnimatorState& snapState = scrollSnapPointState(axis); 781 782 LayoutUnit offset = m_client->scrollOffsetOnAxis(axis); 783 bool canComputeGlide = (snapState.m_initialOffset <= offset && offset < snapState.m_targetOffset) || (snapState.m_targetOffset < offset && offset <= snapState.m_initialOffset); 784 if (snapState.m_currentState != ScrollSnapState::Gliding || !canComputeGlide) 785 return 0; 786 787 const float progress = snapProgress(offset, snapState); 788 const float rawGlideDelta = snapGlide(progress, snapState); 789 790 float glideDelta = snapState.m_initialOffset < snapState.m_targetOffset ? std::max<float>(rawGlideDelta, 1) : std::min<float>(rawGlideDelta, -1); 791 if ((snapState.m_initialOffset < snapState.m_targetOffset && offset + glideDelta > snapState.m_targetOffset) || (snapState.m_initialOffset > snapState.m_targetOffset && offset + glideDelta < snapState.m_targetOffset)) 792 return snapState.m_targetOffset - offset; 793 794 return glideDelta; 499 795 } 500 796 #endif -
trunk/Source/WebCore/platform/cocoa/ScrollSnapAnimatorState.h
r181001 r181087 24 24 */ 25 25 26 #ifndef AxisScrollSnapAnimator_h27 #define AxisScrollSnapAnimator_h26 #ifndef ScrollSnapAnimatorState_h 27 #define ScrollSnapAnimatorState_h 28 28 29 29 #if ENABLE(CSS_SCROLL_SNAP) … … 43 43 }; 44 44 45 enum class WheelEventStatus { 46 UserScrollBegin, 47 UserScrolling, 48 UserScrollEnd, 49 InertialScrollBegin, 50 InertialScrolling, 51 InertialScrollEnd, 52 Unknown 53 }; 45 struct ScrollSnapAnimatorState { 46 ScrollSnapAnimatorState(ScrollEventAxis, const Vector<LayoutUnit>&); 54 47 55 class AxisScrollSnapAnimatorClient {56 protected:57 virtual ~AxisScrollSnapAnimatorClient() { }58 59 public:60 virtual LayoutUnit scrollOffsetOnAxis(ScrollEventAxis) = 0;61 virtual void immediateScrollOnAxis(ScrollEventAxis, float velocity) = 0;62 virtual void startScrollSnapTimer(ScrollEventAxis) = 0;63 virtual void stopScrollSnapTimer(ScrollEventAxis) = 0;64 };65 66 class AxisScrollSnapAnimator {67 public:68 AxisScrollSnapAnimator(AxisScrollSnapAnimatorClient*, const Vector<LayoutUnit>&, ScrollEventAxis);69 void handleWheelEvent(const PlatformWheelEvent&);70 bool shouldOverrideWheelEvent(const PlatformWheelEvent&) const;71 void scrollSnapAnimationUpdate();72 73 private:74 void beginScrollSnapAnimation(ScrollSnapState);75 void endScrollSnapAnimation(ScrollSnapState);76 77 float computeSnapDelta() const;78 float computeGlideDelta() const;79 80 void initializeGlideParameters(bool);81 48 void pushInitialWheelDelta(float); 82 49 float averageInitialWheelDelta() const; … … 85 52 static const int wheelDeltaWindowSize = 3; 86 53 87 AxisScrollSnapAnimatorClient* m_client;88 54 Vector<LayoutUnit> m_snapOffsets; 89 55 ScrollEventAxis m_axis; … … 106 72 #endif // ENABLE(CSS_SCROLL_SNAP) 107 73 108 #endif // AxisScrollSnapAnimator_h74 #endif // ScrollSnapAnimatorState_h -
trunk/Source/WebCore/platform/cocoa/ScrollSnapAnimatorState.mm
r181001 r181087 25 25 26 26 #include "config.h" 27 #include " AxisScrollSnapAnimator.h"27 #include "ScrollSnapAnimatorState.h" 28 28 29 29 #if ENABLE(CSS_SCROLL_SNAP) … … 31 31 namespace WebCore { 32 32 33 const float inertialScrollPredictionFactor = 16.7; 34 const float snapMagnitudeMax = 25; 35 const float snapMagnitudeMin = 5; 36 const float snapThresholdHigh = 1000; 37 const float snapThresholdLow = 50; 38 const float glideBoostMultiplier = 3.5; 39 const float maxTargetWheelDelta = 7; 40 const float minTargetWheelDelta = 3.5; 41 const float initialToFinalMomentumFactor = 1.0 / 40.0; 42 43 static inline WheelEventStatus toWheelEventStatus(PlatformWheelEventPhase phase, PlatformWheelEventPhase momentumPhase) 44 { 45 if (phase == PlatformWheelEventPhaseNone) { 46 switch (momentumPhase) { 47 case PlatformWheelEventPhaseBegan: 48 return WheelEventStatus::InertialScrollBegin; 49 50 case PlatformWheelEventPhaseChanged: 51 return WheelEventStatus::InertialScrolling; 52 53 case PlatformWheelEventPhaseEnded: 54 return WheelEventStatus::InertialScrollEnd; 55 56 default: 57 return WheelEventStatus::Unknown; 58 } 59 } 60 if (momentumPhase == PlatformWheelEventPhaseNone) { 61 switch (phase) { 62 case PlatformWheelEventPhaseBegan: 63 case PlatformWheelEventPhaseMayBegin: 64 return WheelEventStatus::UserScrollBegin; 65 66 case PlatformWheelEventPhaseChanged: 67 return WheelEventStatus::UserScrolling; 68 69 case PlatformWheelEventPhaseEnded: 70 case PlatformWheelEventPhaseCancelled: 71 return WheelEventStatus::UserScrollEnd; 72 73 default: 74 return WheelEventStatus::Unknown; 75 } 76 } 77 return WheelEventStatus::Unknown; 78 } 79 80 static inline float projectedInertialScrollDistance(float initialWheelDelta) 81 { 82 // FIXME: Experiments with inertial scrolling show a fairly consistent linear relationship between initial wheel delta and total distance scrolled. 83 // In the future, we'll want to find a more accurate way of inertial scroll prediction. 84 return inertialScrollPredictionFactor * initialWheelDelta; 85 } 86 87 AxisScrollSnapAnimator::AxisScrollSnapAnimator(AxisScrollSnapAnimatorClient* client, const Vector<LayoutUnit>& snapOffsets, ScrollEventAxis axis) 88 : m_client(client) 89 , m_snapOffsets(snapOffsets) 33 ScrollSnapAnimatorState::ScrollSnapAnimatorState(ScrollEventAxis axis, const Vector<LayoutUnit>& snapOffsets) 34 : m_snapOffsets(snapOffsets) 90 35 , m_axis(axis) 91 36 , m_currentState(ScrollSnapState::DestinationReached) … … 101 46 } 102 47 103 void AxisScrollSnapAnimator::handleWheelEvent(const PlatformWheelEvent& event) 104 { 105 float wheelDelta = m_axis == ScrollEventAxis::Horizontal ? -event.deltaX() : -event.deltaY(); 106 WheelEventStatus wheelStatus = toWheelEventStatus(event.phase(), event.momentumPhase()); 107 108 switch (wheelStatus) { 109 case WheelEventStatus::UserScrollBegin: 110 case WheelEventStatus::UserScrolling: 111 endScrollSnapAnimation(ScrollSnapState::UserInteraction); 112 break; 113 114 case WheelEventStatus::UserScrollEnd: 115 beginScrollSnapAnimation(ScrollSnapState::Snapping); 116 break; 117 118 case WheelEventStatus::InertialScrollBegin: 119 // Begin tracking wheel deltas for glide prediction. 120 endScrollSnapAnimation(ScrollSnapState::UserInteraction); 121 pushInitialWheelDelta(wheelDelta); 122 m_beginTrackingWheelDeltaOffset = m_client->scrollOffsetOnAxis(m_axis); 123 break; 124 125 case WheelEventStatus::InertialScrolling: 126 // This check for DestinationReached ensures that we don't receive another set of momentum events after ending the last glide. 127 if (m_currentState != ScrollSnapState::Gliding && m_currentState != ScrollSnapState::DestinationReached) { 128 if (m_numWheelDeltasTracked < wheelDeltaWindowSize) 129 pushInitialWheelDelta(wheelDelta); 130 131 if (m_numWheelDeltasTracked == wheelDeltaWindowSize) 132 beginScrollSnapAnimation(ScrollSnapState::Gliding); 133 } 134 break; 135 136 case WheelEventStatus::InertialScrollEnd: 137 beginScrollSnapAnimation(ScrollSnapState::Snapping); 138 clearInitialWheelDeltaWindow(); 139 m_shouldOverrideWheelEvent = false; 140 break; 141 142 case WheelEventStatus::Unknown: 143 ASSERT_NOT_REACHED(); 144 break; 145 } 146 } 147 148 bool AxisScrollSnapAnimator::shouldOverrideWheelEvent(const PlatformWheelEvent& event) const 149 { 150 return m_shouldOverrideWheelEvent && toWheelEventStatus(event.phase(), event.momentumPhase()) == WheelEventStatus::InertialScrolling; 151 } 152 153 void AxisScrollSnapAnimator::scrollSnapAnimationUpdate() 154 { 155 if (m_currentState == ScrollSnapState::DestinationReached) 156 return; 157 158 ASSERT(m_currentState == ScrollSnapState::Gliding || m_currentState == ScrollSnapState::Snapping); 159 float delta = m_currentState == ScrollSnapState::Snapping ? computeSnapDelta() : computeGlideDelta(); 160 if (delta) 161 m_client->immediateScrollOnAxis(m_axis, delta); 162 else 163 endScrollSnapAnimation(ScrollSnapState::DestinationReached); 164 } 165 166 void AxisScrollSnapAnimator::beginScrollSnapAnimation(ScrollSnapState newState) 167 { 168 ASSERT(newState == ScrollSnapState::Gliding || newState == ScrollSnapState::Snapping); 169 LayoutUnit offset = m_client->scrollOffsetOnAxis(m_axis); 170 float initialWheelDelta = newState == ScrollSnapState::Gliding ? averageInitialWheelDelta() : 0; 171 LayoutUnit projectedScrollDestination = newState == ScrollSnapState::Gliding ? m_beginTrackingWheelDeltaOffset + LayoutUnit(projectedInertialScrollDistance(initialWheelDelta)) : offset; 172 if (m_snapOffsets.isEmpty()) 173 return; 174 175 projectedScrollDestination = std::min(std::max(projectedScrollDestination, m_snapOffsets.first()), m_snapOffsets.last()); 176 m_initialOffset = offset; 177 m_targetOffset = closestSnapOffset<LayoutUnit, float>(m_snapOffsets, projectedScrollDestination, initialWheelDelta); 178 if (m_initialOffset == m_targetOffset) 179 return; 180 181 m_currentState = newState; 182 if (newState == ScrollSnapState::Gliding) { 183 m_shouldOverrideWheelEvent = true; 184 m_glideInitialWheelDelta = initialWheelDelta; 185 bool glideRequiresBoost; 186 if (initialWheelDelta > 0) 187 glideRequiresBoost = projectedScrollDestination - offset < m_targetOffset - projectedScrollDestination; 188 else 189 glideRequiresBoost = offset - projectedScrollDestination < projectedScrollDestination - m_targetOffset; 190 191 initializeGlideParameters(glideRequiresBoost); 192 clearInitialWheelDeltaWindow(); 193 } 194 m_client->startScrollSnapTimer(m_axis); 195 } 196 197 void AxisScrollSnapAnimator::endScrollSnapAnimation(ScrollSnapState newState) 198 { 199 ASSERT(newState == ScrollSnapState::DestinationReached || newState == ScrollSnapState::UserInteraction); 200 if (m_currentState == ScrollSnapState::Gliding) 201 clearInitialWheelDeltaWindow(); 202 203 m_currentState = newState; 204 m_client->stopScrollSnapTimer(m_axis); 205 } 206 207 // Computes the amount to scroll by when performing a "snap" operation, i.e. when a user releases the trackpad without flicking. The snap delta 208 // is a function of progress t, where t is equal to DISTANCE_TRAVELED / TOTAL_DISTANCE, DISTANCE_TRAVELED is the distance from the initialOffset 209 // to the current offset, and TOTAL_DISTANCE is the distance from initialOffset to targetOffset. The snapping equation is as follows: 210 // delta(t) = MAGNITUDE * sin(PI * t). MAGNITUDE indicates the top speed reached near the middle of the animation (t = 0.5), and is a linear 211 // relationship of the distance traveled, clamped by arbitrary min and max values. 212 float AxisScrollSnapAnimator::computeSnapDelta() const 213 { 214 LayoutUnit offset = m_client->scrollOffsetOnAxis(m_axis); 215 bool canComputeSnap = (m_initialOffset <= offset && offset < m_targetOffset) || (m_targetOffset < offset && offset <= m_initialOffset); 216 if (m_currentState != ScrollSnapState::Snapping || !canComputeSnap) 217 return 0; 218 219 int sign = m_initialOffset < m_targetOffset ? 1 : -1; 220 float progress = ((float)(offset - m_initialOffset)) / (m_targetOffset - m_initialOffset); 221 // Threshold the distance before computing magnitude, so only distances within a certain range are considered. 222 float thresholdedDistance = std::min(std::max<float>((m_targetOffset - m_initialOffset) * sign, snapThresholdLow), snapThresholdHigh); 223 float magnitude = snapMagnitudeMin + (snapMagnitudeMax - snapMagnitudeMin) * (thresholdedDistance - snapThresholdLow) / (snapThresholdHigh - snapThresholdLow); 224 float rawSnapAmount = std::max<float>(1, magnitude * sin(piFloat * progress)); 225 if ((m_targetOffset < offset && offset - rawSnapAmount < m_targetOffset) || (m_targetOffset > offset && offset + rawSnapAmount > m_targetOffset)) 226 return m_targetOffset - offset; 227 228 return sign * rawSnapAmount; 229 } 230 231 // Computes the amount to scroll by when performing a "glide" operation, i.e. when a user releases the trackpad with an initial velocity. Here, 232 // we want the scroll offset to animate directly to the snap point. The snap delta is a function of progress t, where t is equal to 233 // DISTANCE_TRAVELED / TOTAL_DISTANCE, DISTANCE_TRAVELED is the distance from the initialOffset to the current offset, and TOTAL_DISTANCE is 234 // the distance from initialOffset to targetOffset. 235 // The general model of our gliding equation is delta(t) = MAGNITUDE * (1 + cos(PI * t + PHASE_SHIFT)). This was determined after examining the 236 // momentum velocity curve as a function of progress. To compute MAGNITUDE and PHASE_SHIFT, we use initial velocity V0 and the final velocity VF, 237 // both as wheel deltas (pixels per timestep). VF should be a small value (< 10) chosen based on the initial velocity and TOTAL_DISTANCE. 238 // We also enforce the following constraints for the gliding equation: 239 // 1. delta(0) = V0, since we want the initial velocity of the gliding animation to match the user's scroll velocity. The exception to this is 240 // when the glide velocity is not enough to naturally reach the next snap point, and thus requires a boost (see initializeGlideParameters) 241 // 2. delta(1) = VF, since at t=1, the animation has completed and we want the last wheel delta to match the final velocity VF. Note that this 242 // doesn't guarantee that the final velocity will be exactly VF. However, assuming that the initial velocity is much less than TOTAL_DISTANCE, 243 // the last wheel delta will be very close, if not the same, as VF. 244 // For MAGNITUDE = (V0 + VF) / 2 and PHASE_SHIFT = arccos((V0 - VF) / (V0 + VF)), observe that delta(0) and delta(1) evaluate respectively to V0 245 // and VF. Thus, we can express our gliding equation all in terms of V0, VF and t. 246 float AxisScrollSnapAnimator::computeGlideDelta() const 247 { 248 LayoutUnit offset = m_client->scrollOffsetOnAxis(m_axis); 249 bool canComputeGlide = (m_initialOffset <= offset && offset < m_targetOffset) || (m_targetOffset < offset && offset <= m_initialOffset); 250 if (m_currentState != ScrollSnapState::Gliding || !canComputeGlide) 251 return 0; 252 253 float progress = ((float)(offset - m_initialOffset)) / (m_targetOffset - m_initialOffset); 254 // FIXME: We might want to investigate why -m_glidePhaseShift results in the behavior we want. 255 float shift = ceil(m_glideMagnitude * (1 + cos(piFloat * progress - m_glidePhaseShift))); 256 shift = m_initialOffset < m_targetOffset ? std::max<float>(shift, 1) : std::min<float>(shift, -1); 257 if ((m_initialOffset < m_targetOffset && offset + shift > m_targetOffset) || (m_initialOffset > m_targetOffset && offset + shift < m_targetOffset)) 258 return m_targetOffset - offset; 259 260 return shift; 261 } 262 263 void AxisScrollSnapAnimator::initializeGlideParameters(bool shouldIncreaseInitialWheelDelta) 264 { 265 // FIXME: Glide boost is a hacky way to speed up natural scrolling velocity. We should find a better way to accomplish this. 266 if (shouldIncreaseInitialWheelDelta) 267 m_glideInitialWheelDelta *= glideBoostMultiplier; 268 269 // FIXME: There must be a better way to determine a good target delta than multiplying by a factor and clamping to min/max values. 270 float targetFinalWheelDelta = initialToFinalMomentumFactor * (m_glideInitialWheelDelta < 0 ? -m_glideInitialWheelDelta : m_glideInitialWheelDelta); 271 targetFinalWheelDelta = (m_glideInitialWheelDelta > 0 ? 1 : -1) * std::min(std::max(targetFinalWheelDelta, minTargetWheelDelta), maxTargetWheelDelta); 272 m_glideMagnitude = (m_glideInitialWheelDelta + targetFinalWheelDelta) / 2; 273 m_glidePhaseShift = acos((m_glideInitialWheelDelta - targetFinalWheelDelta) / (m_glideInitialWheelDelta + targetFinalWheelDelta)); 274 } 275 276 void AxisScrollSnapAnimator::pushInitialWheelDelta(float wheelDelta) 48 void ScrollSnapAnimatorState::pushInitialWheelDelta(float wheelDelta) 277 49 { 278 50 if (m_numWheelDeltasTracked < wheelDeltaWindowSize) … … 280 52 } 281 53 282 float AxisScrollSnapAnimator::averageInitialWheelDelta() const54 float ScrollSnapAnimatorState::averageInitialWheelDelta() const 283 55 { 284 56 if (!m_numWheelDeltasTracked) … … 292 64 } 293 65 294 void AxisScrollSnapAnimator::clearInitialWheelDeltaWindow()66 void ScrollSnapAnimatorState::clearInitialWheelDeltaWindow() 295 67 { 296 68 for (int i = 0; i < m_numWheelDeltasTracked; i++) -
trunk/Source/WebCore/platform/mac/ScrollAnimatorMac.h
r180974 r181087 148 148 virtual void adjustScrollPositionToBoundsIfNecessary() override; 149 149 150 bool pinnedInDirection(float deltaX, float deltaY);151 152 150 bool isAlreadyPinnedInDirectionOfGesture(const PlatformWheelEvent&, ScrollEventAxis); 153 151 #endif -
trunk/Source/WebCore/platform/mac/ScrollAnimatorMac.mm
r180974 r181087 1103 1103 } 1104 1104 1105 bool ScrollAnimatorMac::pinnedInDirection( float deltaX, float deltaY)1105 bool ScrollAnimatorMac::pinnedInDirection(const FloatSize& direction) 1106 1106 { 1107 1107 FloatSize limitDelta; 1108 if ( std::fabsf(deltaY) >= fabsf(deltaX)) {1109 if (d eltaY< 0) {1108 if (fabsf(direction.height()) >= fabsf(direction.width())) { 1109 if (direction.height() < 0) { 1110 1110 // We are trying to scroll up. Make sure we are not pinned to the top 1111 1111 limitDelta.setHeight(m_scrollableArea.visibleContentRect().y() + m_scrollableArea.scrollOrigin().y()); … … 1114 1114 limitDelta.setHeight(m_scrollableArea.totalContentsSize().height() - (m_scrollableArea.visibleContentRect().maxY() + m_scrollableArea.scrollOrigin().y())); 1115 1115 } 1116 } else if (d eltaX != 0) {1117 if (d eltaX< 0) {1116 } else if (direction.width()) { 1117 if (direction.width() < 0) { 1118 1118 // We are trying to scroll left. Make sure we are not pinned to the left 1119 1119 limitDelta.setWidth(m_scrollableArea.visibleContentRect().x() + m_scrollableArea.scrollOrigin().x()); … … 1124 1124 } 1125 1125 1126 if ((d eltaX != 0 || deltaY != 0) && (limitDelta.width() < 1 && limitDelta.height() < 1))1126 if ((direction.width() || direction.height()) && (limitDelta.width() < 1 && limitDelta.height() < 1)) 1127 1127 return true; 1128 1128 return false; … … 1217 1217 } 1218 1218 1219 bool ScrollAnimatorMac::pinnedInDirection(const FloatSize& direction)1220 {1221 return pinnedInDirection(direction.width(), direction.height());1222 }1223 1224 1219 bool ScrollAnimatorMac::canScrollHorizontally() 1225 1220 {
Note: See TracChangeset
for help on using the changeset viewer.