diff options
Diffstat (limited to 'chromium/ui/events/blink/snap_fling_curve.cc')
-rw-r--r-- | chromium/ui/events/blink/snap_fling_curve.cc | 119 |
1 files changed, 119 insertions, 0 deletions
diff --git a/chromium/ui/events/blink/snap_fling_curve.cc b/chromium/ui/events/blink/snap_fling_curve.cc new file mode 100644 index 00000000000..f1fb6a1eb5f --- /dev/null +++ b/chromium/ui/events/blink/snap_fling_curve.cc @@ -0,0 +1,119 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/events/blink/snap_fling_curve.h" + +#include <cmath> +#include "build/build_config.h" + +namespace ui { +namespace { + +#if defined(OS_ANDROID) +constexpr double kDistanceEstimatorScalar = 40; +// The delta to be scrolled in next frame is 0.9 of the delta in last frame. +constexpr double kRatio = 0.9; +#else +constexpr double kDistanceEstimatorScalar = 25; +// The delta to be scrolled in next frame is 0.92 of the delta in last frame. +constexpr double kRatio = 0.92; +#endif +constexpr double kMsPerFrame = 16; +constexpr base::TimeDelta kMaximumSnapDuration = + base::TimeDelta::FromSecondsD(5); + +double GetDistanceFromDisplacement(gfx::Vector2dF displacement) { + return std::hypot(displacement.x(), displacement.y()); +} + +double EstimateFramesFromDistance(double distance) { + // We approximate scroll deltas as a geometric sequence with the ratio kRatio, + // and the last scrolled delta should be less or equal than 1, yielding the + // total distance as (1 - kRatio^(-n)) / (1 - (1 / kRatio)). Solving this + // could get n as below, which is the total number of deltas in the sequence, + // and is also the total frames needed to finish the curve. + return std::ceil(-std::log(1 - distance * (1 - 1 / kRatio)) / + std::log(kRatio)); +} + +double CalculateFirstDelta(double distance, double frames) { + // distance = first_delta (1 - kRatio^(frames) / (1 - kRatio)). + // We can get the |first_delta| by solving the equation above. + return distance * (1 - kRatio) / (1 - std::pow(kRatio, frames)); +} + +} // namespace + +gfx::Vector2dF SnapFlingCurve::EstimateDisplacement( + const gfx::Vector2dF& first_delta) { + gfx::Vector2dF destination = first_delta; + destination.Scale(kDistanceEstimatorScalar); + return destination; +} + +SnapFlingCurve::SnapFlingCurve(const gfx::Vector2dF& start_offset, + const gfx::Vector2dF& target_offset, + base::TimeTicks first_gsu_time) + : start_offset_(start_offset), + total_displacement_(target_offset - start_offset), + total_distance_(GetDistanceFromDisplacement(total_displacement_)), + start_time_(first_gsu_time), + total_frames_(EstimateFramesFromDistance(total_distance_)), + first_delta_(CalculateFirstDelta(total_distance_, total_frames_)), + duration_(base::TimeDelta::FromMilliseconds(total_frames_ * kMsPerFrame)), + is_finished_(total_distance_ == 0) { + if (is_finished_) + return; + ratio_x_ = total_displacement_.x() / total_distance_; + ratio_y_ = total_displacement_.y() / total_distance_; +} + +SnapFlingCurve::~SnapFlingCurve() = default; + +double SnapFlingCurve::GetCurrentCurveDistance(base::TimeTicks time_stamp) { + double current_distance = GetDistanceFromDisplacement(current_displacement_); + base::TimeDelta current_time = time_stamp - start_time_; + + // Finishes the curve if the time elapsed is longer than |duration_|, or the + // remaining distance is less than 1. + if (current_time >= duration_ || current_distance >= total_distance_ - 1) { + return total_distance_; + } + + double current_frame = current_time.InMillisecondsF() / kMsPerFrame + 1; + double sum = + first_delta_ * (1 - std::pow(kRatio, current_frame)) / (1 - kRatio); + return sum <= total_distance_ ? sum : total_distance_; +} + +gfx::Vector2dF SnapFlingCurve::GetScrollDelta(base::TimeTicks time_stamp) { + if (is_finished_) + return gfx::Vector2dF(); + + // The the snap offset may never be reached due to clamping or other factors. + // To avoid a never ending snap curve, we force the curve to end if the time + // has passed a maximum Duration. + if (time_stamp - start_time_ > kMaximumSnapDuration) { + is_finished_ = true; + return total_displacement_ - current_displacement_; + } + + double new_distance = GetCurrentCurveDistance(time_stamp); + gfx::Vector2dF new_displacement(new_distance * ratio_x_, + new_distance * ratio_y_); + + return new_displacement - current_displacement_; +} + +void SnapFlingCurve::UpdateCurrentOffset(const gfx::Vector2dF& current_offset) { + current_displacement_ = current_offset - start_offset_; + if (current_displacement_ == total_displacement_) + is_finished_ = true; +} + +bool SnapFlingCurve::IsFinished() const { + return is_finished_; +} + +} // namespace ui |