diff options
Diffstat (limited to 'gfx/layers/apz/src/SmoothMsdScrollAnimation.cpp')
-rw-r--r-- | gfx/layers/apz/src/SmoothMsdScrollAnimation.cpp | 139 |
1 files changed, 139 insertions, 0 deletions
diff --git a/gfx/layers/apz/src/SmoothMsdScrollAnimation.cpp b/gfx/layers/apz/src/SmoothMsdScrollAnimation.cpp new file mode 100644 index 0000000000..8342dc157f --- /dev/null +++ b/gfx/layers/apz/src/SmoothMsdScrollAnimation.cpp @@ -0,0 +1,139 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "SmoothMsdScrollAnimation.h" +#include "AsyncPanZoomController.h" + +namespace mozilla { +namespace layers { + +SmoothMsdScrollAnimation::SmoothMsdScrollAnimation( + AsyncPanZoomController& aApzc, const CSSPoint& aInitialPosition, + const CSSPoint& aInitialVelocity, const CSSPoint& aDestination, + double aSpringConstant, double aDampingRatio, + ScrollSnapTargetIds&& aSnapTargetIds, + ScrollTriggeredByScript aTriggeredByScript) + : mApzc(aApzc), + mXAxisModel(aInitialPosition.x, aDestination.x, aInitialVelocity.x, + aSpringConstant, aDampingRatio), + mYAxisModel(aInitialPosition.y, aDestination.y, aInitialVelocity.y, + aSpringConstant, aDampingRatio), + mSnapTargetIds(std::move(aSnapTargetIds)), + mTriggeredByScript(aTriggeredByScript) {} + +bool SmoothMsdScrollAnimation::DoSample(FrameMetrics& aFrameMetrics, + const TimeDuration& aDelta) { + CSSToParentLayerScale zoom(aFrameMetrics.GetZoom()); + if (zoom == CSSToParentLayerScale(0)) { + return false; + } + CSSPoint oneParentLayerPixel = + ParentLayerPoint(1, 1) / aFrameMetrics.GetZoom(); + if (mXAxisModel.IsFinished(oneParentLayerPixel.x) && + mYAxisModel.IsFinished(oneParentLayerPixel.y)) { + // Set the scroll offset to the exact destination. If we allow the scroll + // offset to end up being a bit off from the destination, we can get + // artefacts like "scroll to the next snap point in this direction" + // scrolling to the snap point we're already supposed to be at. + mApzc.ClampAndSetVisualScrollOffset( + CSSPoint(mXAxisModel.GetDestination(), mYAxisModel.GetDestination())); + return false; + } + + mXAxisModel.Simulate(aDelta); + mYAxisModel.Simulate(aDelta); + + CSSPoint position = + CSSPoint(mXAxisModel.GetPosition(), mYAxisModel.GetPosition()); + CSSPoint css_velocity = + CSSPoint(mXAxisModel.GetVelocity(), mYAxisModel.GetVelocity()); + + // Convert from pixels/second to pixels/ms + ParentLayerPoint velocity = + ParentLayerPoint(css_velocity.x, css_velocity.y) / 1000.0f; + + // Keep the velocity updated for the Axis class so that any animations + // chained off of the smooth scroll will inherit it. + if (mXAxisModel.IsFinished(oneParentLayerPixel.x)) { + mApzc.mX.SetVelocity(0); + } else { + mApzc.mX.SetVelocity(velocity.x); + } + if (mYAxisModel.IsFinished(oneParentLayerPixel.y)) { + mApzc.mY.SetVelocity(0); + } else { + mApzc.mY.SetVelocity(velocity.y); + } + // If we overscroll, hand off to a fling animation that will complete the + // spring back. + ParentLayerPoint displacement = + (position - aFrameMetrics.GetVisualScrollOffset()) * zoom; + + ParentLayerPoint overscroll; + ParentLayerPoint adjustedOffset; + mApzc.mX.AdjustDisplacement(displacement.x, adjustedOffset.x, overscroll.x); + mApzc.mY.AdjustDisplacement(displacement.y, adjustedOffset.y, overscroll.y); + mApzc.ScrollBy(adjustedOffset / zoom); + // The smooth scroll may have caused us to reach the end of our scroll + // range. This can happen if either the + // layout.css.scroll-behavior.damping-ratio preference is set to less than 1 + // (underdamped) or if a smooth scroll inherits velocity from a fling + // gesture. + if (!IsZero(overscroll / zoom)) { + // Hand off a fling with the remaining momentum to the next APZC in the + // overscroll handoff chain. + + // We may have reached the end of the scroll range along one axis but + // not the other. In such a case we only want to hand off the relevant + // component of the fling. + if (mApzc.IsZero(overscroll.x)) { + velocity.x = 0; + } else if (mApzc.IsZero(overscroll.y)) { + velocity.y = 0; + } + + // To hand off the fling, we attempt to find a target APZC and start a new + // fling with the same velocity on that APZC. For simplicity, the actual + // overscroll of the current sample is discarded rather than being handed + // off. The compositor should sample animations sufficiently frequently + // that this is not noticeable. The target APZC is chosen by seeing if + // there is an APZC further in the handoff chain which is pannable; if + // there isn't, we take the new fling ourselves, entering an overscrolled + // state. + // Note: APZC is holding mRecursiveMutex, so directly calling + // HandleSmoothScrollOverscroll() (which acquires the tree lock) would + // violate the lock ordering. Instead we schedule + // HandleSmoothScrollOverscroll() to be called after mRecursiveMutex is + // released. + mDeferredTasks.AppendElement(NewRunnableMethod<ParentLayerPoint, SideBits>( + "layers::AsyncPanZoomController::HandleSmoothScrollOverscroll", &mApzc, + &AsyncPanZoomController::HandleSmoothScrollOverscroll, velocity, + apz::GetOverscrollSideBits(overscroll))); + return false; + } + + return true; +} + +void SmoothMsdScrollAnimation::SetDestination( + const CSSPoint& aNewDestination, ScrollSnapTargetIds&& aSnapTargetIds, + ScrollTriggeredByScript aTriggeredByScript) { + mXAxisModel.SetDestination(aNewDestination.x); + mYAxisModel.SetDestination(aNewDestination.y); + mSnapTargetIds = std::move(aSnapTargetIds); + mTriggeredByScript = aTriggeredByScript; +} + +CSSPoint SmoothMsdScrollAnimation::GetDestination() const { + return CSSPoint(mXAxisModel.GetDestination(), mYAxisModel.GetDestination()); +} + +SmoothMsdScrollAnimation* +SmoothMsdScrollAnimation::AsSmoothMsdScrollAnimation() { + return this; +} + +} // namespace layers +} // namespace mozilla |