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