summaryrefslogtreecommitdiffstats
path: root/gfx/layers/apz/src/SmoothMsdScrollAnimation.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--gfx/layers/apz/src/SmoothMsdScrollAnimation.cpp130
1 files changed, 130 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..ab57f345da
--- /dev/null
+++ b/gfx/layers/apz/src/SmoothMsdScrollAnimation.cpp
@@ -0,0 +1,130 @@
+/* -*- 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)
+ : mApzc(aApzc),
+ mXAxisModel(aInitialPosition.x, aDestination.x, aInitialVelocity.x,
+ aSpringConstant, aDampingRatio),
+ mYAxisModel(aInitialPosition.y, aDestination.y, aInitialVelocity.y,
+ aSpringConstant, aDampingRatio) {}
+
+bool SmoothMsdScrollAnimation::DoSample(FrameMetrics& aFrameMetrics,
+ const TimeDuration& aDelta) {
+ CSSToParentLayerScale2D zoom = aFrameMetrics.GetZoom();
+ if (zoom == CSSToParentLayerScale2D(0, 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)) {
+ // 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 (FuzzyEqualsAdditive(overscroll.x, 0.0f, COORDINATE_EPSILON)) {
+ velocity.x = 0;
+ } else if (FuzzyEqualsAdditive(overscroll.y, 0.0f, COORDINATE_EPSILON)) {
+ 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>(
+ "layers::AsyncPanZoomController::HandleSmoothScrollOverscroll", &mApzc,
+ &AsyncPanZoomController::HandleSmoothScrollOverscroll, velocity));
+ return false;
+ }
+
+ return true;
+}
+
+void SmoothMsdScrollAnimation::SetDestination(const CSSPoint& aNewDestination) {
+ mXAxisModel.SetDestination(aNewDestination.x);
+ mYAxisModel.SetDestination(aNewDestination.y);
+}
+
+CSSPoint SmoothMsdScrollAnimation::GetDestination() const {
+ return CSSPoint(mXAxisModel.GetDestination(), mYAxisModel.GetDestination());
+}
+
+SmoothMsdScrollAnimation*
+SmoothMsdScrollAnimation::AsSmoothMsdScrollAnimation() {
+ return this;
+}
+
+} // namespace layers
+} // namespace mozilla