blob: 645c9c5440a30d9bc96e933ce39318c4f1cb000c [file] [log] [blame]
 // 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 "cc/input/snap_fling_curve.h" #include #include "build/build_config.h" namespace cc { 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 cc