1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "core/components_ng/pattern/text_picker/textpicker_overscroll.h"
17 
18 #include "base/utils/utils.h"
19 #include "core/components_ng/pattern/text_picker/textpicker_column_pattern.h"
20 
21 namespace OHOS::Ace::NG {
22 namespace {
23 const int32_t HALF_NUMBER = 2;
24 constexpr float MIN_SCROLL = 0.1f;
25 constexpr float MIN_VELOCITY = 200.f;
26 constexpr float REBOUND_SCROLL_DAMPING = -1.848f;
27 constexpr float REBOUND_SPRING_VELOCITY = 1.f;
28 constexpr float REBOUND_SPRING_MASS = 1.f;
29 constexpr float REBOUND_SPRING_STIFFNESS = 228.f;
30 constexpr float REBOUND_SPRING_DAMPING = 30.f;
31 constexpr float LOOP_TOSS_DAMPING = 100.f;
32 } //namespace
ApplyCurrentOffset(float yLast,float offsetY,float scrollDelta)33 bool TextPickerOverscroller::ApplyCurrentOffset(float yLast, float offsetY, float scrollDelta)
34 {
35     auto column = AceType::DynamicCast<TextPickerColumnPattern>(column_.Upgrade());
36     CHECK_NULL_RETURN(column, false);
37     auto hostNode = column->GetHost();
38     CHECK_NULL_RETURN(hostNode, false);
39     auto geometryNode = hostNode->GetGeometryNode();
40     CHECK_NULL_RETURN(geometryNode, false);
41     height_ = geometryNode->GetFrameSize().Height();
42 
43     if (!CanOverScroll(scrollDelta)) {
44         overScroll_ = 0.0;
45         backScrollOffset_ = 0.0;
46         return false;
47     }
48 
49     // start overScroll
50     isFirstStart_ = false;
51     if (NearZero(overScroll_)) {
52         overScrollStartOffsetY_ = yLast;
53         isFirstStart_ = true;
54     }
55 
56     deltaScrollOffset_ = GetOverScrollOffset(yLast, offsetY);
57 
58     auto isSignDiff = LessNotEqual(deltaScrollOffset_ * overScroll_, 0.0);
59     auto isBackZero = isSignDiff && GreatOrEqual(std::fabs(deltaScrollOffset_), std::fabs(overScroll_));
60     if (!NearZero(overScroll_) && isBackZero) { // overScroll come back zero
61         overScroll_ = 0.0;
62         backScrollOffset_ = overScroll_ + deltaScrollOffset_;
63     } else {
64         overScroll_ += deltaScrollOffset_;
65         overScroll_ = (overScroll_ > 0.0 ? 1 : -1) * std::min(std::fabs(overScroll_), height_ / HALF_NUMBER);
66         backScrollOffset_ = 0.0;
67     }
68 
69     return true;
70 }
71 
CanOverScroll(float scrollDelta) const72 bool TextPickerOverscroller::CanOverScroll(float scrollDelta) const
73 {
74     if (NearZero(scrollDelta)) {
75         return false;
76     }
77 
78     auto column = AceType::DynamicCast<TextPickerColumnPattern>(column_.Upgrade());
79     CHECK_NULL_RETURN(column, false);
80     auto currentIdx = column->GetCurrentIndex();
81     auto optionCount = column->GetOptionCount();
82     if (optionCount == 0) {
83         return false;
84     }
85     auto isDown = GreatNotEqual(overScroll_, 0.0) || (GreatNotEqual(scrollDelta, 0.0) && NearZero(overScroll_));
86     auto isUp = LessNotEqual(overScroll_, 0.0) || (LessNotEqual(scrollDelta, 0.0) && NearZero(overScroll_));
87     return (currentIdx == 0 && isDown) || (currentIdx == optionCount - 1 && isUp);
88 }
89 
GetOverScrollOffset(float yLast,float offsetY) const90 float TextPickerOverscroller::GetOverScrollOffset(float yLast, float offsetY) const
91 {
92     auto dx = offsetY - yLast;
93     auto input = NearZero(height_) ? 0.0 : std::fabs(offsetY - overScrollStartOffsetY_) / height_;
94     return dx * std::exp(REBOUND_SCROLL_DAMPING * input);
95 }
96 
GetReboundCurve() const97 RefPtr<Curve> TextPickerOverscroller::GetReboundCurve() const
98 {
99     return AceType::MakeRefPtr<InterpolatingSpring>(
100         REBOUND_SPRING_VELOCITY, REBOUND_SPRING_MASS, REBOUND_SPRING_STIFFNESS, REBOUND_SPRING_DAMPING);
101 }
102 
UpdateTossSpring(float offsetY)103 void TextPickerOverscroller::UpdateTossSpring(float offsetY)
104 {
105     velocityTracker_.UpdateTrackerPoint(0.0, offsetY, std::chrono::high_resolution_clock::now());
106 }
107 
ShouldStartRebound()108 bool TextPickerOverscroller::ShouldStartRebound()
109 {
110     auto damping = NearZero(loopTossOffset_) ? 1.0 : LOOP_TOSS_DAMPING / std::abs(loopTossOffset_);
111     auto velocity = std::abs(velocityTracker_.GetVelocity().GetVelocityY()) * damping;
112     auto canRebound = !isFirstStart_ && velocity < MIN_VELOCITY;
113     return canRebound || InMaxOverScroll() || NearZero(deltaScrollOffset_, MIN_SCROLL);
114 }
115 
Reset()116 void TextPickerOverscroller::Reset()
117 {
118     overScroll_ = 0.0;
119     backScrollOffset_ = 0.0;
120     deltaScrollOffset_ = 0.0;
121     overScrollStartOffsetY_ = 0.0;
122     isFirstStart_ = false;
123     loopTossOffset_ = 0.0;
124     velocityTracker_.Reset();
125 }
126 
InMaxOverScroll() const127 bool TextPickerOverscroller::InMaxOverScroll() const
128 {
129     return GreatOrEqual(std::fabs(overScroll_), height_ / HALF_NUMBER);
130 }
131 
IsOverScroll() const132 bool TextPickerOverscroller::IsOverScroll() const
133 {
134     return !NearZero(overScroll_);
135 }
136 
IsBackOverScroll() const137 bool TextPickerOverscroller::IsBackOverScroll() const
138 {
139     return !NearZero(backScrollOffset_);
140 }
141 } // namespace OHOS::Ace::NG