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 "core/components_ng/pattern/text_picker/textpicker_column_pattern.h"
19
20 namespace OHOS::Ace::NG {
21 namespace {
22 const int32_t HALF_NUMBER = 2;
23 constexpr float MIN_SCROLL = 0.1f;
24 constexpr float MIN_VELOCITY = 200.f;
25 constexpr float REBOUND_SCROLL_DAMPING = -1.848f;
26 constexpr float REBOUND_SPRING_VELOCITY = 1.f;
27 constexpr float REBOUND_SPRING_MASS = 1.f;
28 constexpr float REBOUND_SPRING_STIFFNESS = 228.f;
29 constexpr float REBOUND_SPRING_DAMPING = 30.f;
30 constexpr float LOOP_TOSS_DAMPING = 100.f;
31 } //namespace
ApplyCurrentOffset(float yLast,float offsetY,float scrollDelta)32 bool TextPickerOverscroller::ApplyCurrentOffset(float yLast, float offsetY, float scrollDelta)
33 {
34 auto column = AceType::DynamicCast<TextPickerColumnPattern>(column_.Upgrade());
35 CHECK_NULL_RETURN(column, false);
36 auto hostNode = column->GetHost();
37 CHECK_NULL_RETURN(hostNode, false);
38 auto geometryNode = hostNode->GetGeometryNode();
39 CHECK_NULL_RETURN(geometryNode, false);
40 height_ = geometryNode->GetFrameSize().Height();
41
42 if (!CanOverScroll(scrollDelta)) {
43 overScroll_ = 0.0;
44 backScrollOffset_ = 0.0;
45 return false;
46 }
47
48 // start overScroll
49 isFirstStart_ = false;
50 if (NearZero(overScroll_)) {
51 overScrollStartOffsetY_ = yLast;
52 isFirstStart_ = true;
53 }
54
55 deltaScrollOffset_ = GetOverScrollOffset(yLast, offsetY);
56
57 auto isSignDiff = LessNotEqual(deltaScrollOffset_ * overScroll_, 0.0);
58 auto isBackZero = isSignDiff && GreatOrEqual(std::fabs(deltaScrollOffset_), std::fabs(overScroll_));
59 if (!NearZero(overScroll_) && isBackZero) { // overScroll come back zero
60 overScroll_ = 0.0;
61 backScrollOffset_ = overScroll_ + deltaScrollOffset_;
62 } else {
63 overScroll_ += deltaScrollOffset_;
64 overScroll_ = (overScroll_ > 0.0 ? 1 : -1) * std::min(std::fabs(overScroll_), height_ / HALF_NUMBER);
65 backScrollOffset_ = 0.0;
66 }
67
68 return true;
69 }
70
CanOverScroll(float scrollDelta) const71 bool TextPickerOverscroller::CanOverScroll(float scrollDelta) const
72 {
73 if (NearZero(scrollDelta)) {
74 return false;
75 }
76
77 auto column = AceType::DynamicCast<TextPickerColumnPattern>(column_.Upgrade());
78 CHECK_NULL_RETURN(column, false);
79 auto currentIdx = column->GetCurrentIndex();
80 auto optionCount = column->GetOptionCount();
81 if (optionCount == 0) {
82 return false;
83 }
84 auto isDown = GreatNotEqual(overScroll_, 0.0) || (GreatNotEqual(scrollDelta, 0.0) && NearZero(overScroll_));
85 auto isUp = LessNotEqual(overScroll_, 0.0) || (LessNotEqual(scrollDelta, 0.0) && NearZero(overScroll_));
86 return (currentIdx == 0 && isDown) || (currentIdx == optionCount - 1 && isUp);
87 }
88
GetOverScrollOffset(float yLast,float offsetY) const89 float TextPickerOverscroller::GetOverScrollOffset(float yLast, float offsetY) const
90 {
91 auto dx = offsetY - yLast;
92 auto input = NearZero(height_) ? 0.0 : std::fabs(offsetY - overScrollStartOffsetY_) / height_;
93 return dx * std::exp(REBOUND_SCROLL_DAMPING * input);
94 }
95
GetReboundCurve() const96 RefPtr<Curve> TextPickerOverscroller::GetReboundCurve() const
97 {
98 return AceType::MakeRefPtr<InterpolatingSpring>(
99 REBOUND_SPRING_VELOCITY, REBOUND_SPRING_MASS, REBOUND_SPRING_STIFFNESS, REBOUND_SPRING_DAMPING);
100 }
101
UpdateTossSpring(float offsetY)102 void TextPickerOverscroller::UpdateTossSpring(float offsetY)
103 {
104 velocityTracker_.UpdateTrackerPoint(0.0, offsetY, std::chrono::high_resolution_clock::now());
105 }
106
ShouldStartRebound()107 bool TextPickerOverscroller::ShouldStartRebound()
108 {
109 auto damping = NearZero(loopTossOffset_) ? 1.0 : LOOP_TOSS_DAMPING / std::abs(loopTossOffset_);
110 auto velocity = std::abs(velocityTracker_.GetVelocity().GetVelocityY()) * damping;
111 auto canRebound = !isFirstStart_ && velocity < MIN_VELOCITY;
112 return canRebound || InMaxOverScroll() || NearZero(deltaScrollOffset_, MIN_SCROLL);
113 }
114
Reset()115 void TextPickerOverscroller::Reset()
116 {
117 overScroll_ = 0.0;
118 backScrollOffset_ = 0.0;
119 deltaScrollOffset_ = 0.0;
120 overScrollStartOffsetY_ = 0.0;
121 isFirstStart_ = false;
122 loopTossOffset_ = 0.0;
123 velocityTracker_.Reset();
124 }
125
InMaxOverScroll() const126 bool TextPickerOverscroller::InMaxOverScroll() const
127 {
128 return GreatOrEqual(std::fabs(overScroll_), height_ / HALF_NUMBER);
129 }
130
IsOverScroll() const131 bool TextPickerOverscroller::IsOverScroll() const
132 {
133 return !NearZero(overScroll_);
134 }
135
IsBackOverScroll() const136 bool TextPickerOverscroller::IsBackOverScroll() const
137 {
138 return !NearZero(backScrollOffset_);
139 }
140 } // namespace OHOS::Ace::NG