1 /*
2 * Copyright (c) 2021 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/scroll/scroll_fade_controller.h"
17
18 #include "core/pipeline/pipeline_context.h"
19
20 namespace OHOS::Ace {
21 namespace {
22
23 constexpr int32_t RECEDE_TIME = 600; // milliseconds
24 constexpr int32_t PULL_TIME = 167; // milliseconds
25 constexpr int32_t PULL_HOLD_TIME = 167; // milliseconds
26 constexpr int32_t PULL_RECEDE_TIME = 1000; // milliseconds
27 constexpr double MIN_VELOCITY = 100.0;
28 constexpr double MAX_VELOCITY = 10000.0;
29 constexpr double MIN_OPACITY = 0.15;
30 constexpr double MAX_OPACITY = 0.5;
31 constexpr double DEFAULT_OPACITY = 0.3;
32 constexpr double MAX_SCALE_SIZE = 1.0;
33 constexpr double VELOCITY_FACTOR = 0.00006;
34 constexpr double WIDTH_TO_HEIGHT_FACTOR = 0.20096; // (3.0 / 4.0) * (2.0 - std::sqrt(3));
35 constexpr double SCALE_SIZE_BASE = 0.025;
36 constexpr double SCALE_VELOCITY_FACTOR = 7.5e-7;
37 constexpr double DURATION_BASE = 0.15;
38 constexpr double DURATION_VELOCITY_FACTOR = 0.02;
39 constexpr double OVER_DISTANCE_SCALE = 200.0;
40 constexpr double PULL_OPACITY_FACTOR = 3.2;
41 constexpr double SCALE_CALCULATOR_FACTOR = 0.7;
42
43 } // namespace
44
ScrollFadeController(const WeakPtr<PipelineContext> & context)45 ScrollFadeController::ScrollFadeController(const WeakPtr<PipelineContext>& context) : context_(context)
46 {
47 Initialize();
48 }
49
~ScrollFadeController()50 ScrollFadeController::~ScrollFadeController()
51 {
52 // If animation still runs, force stop it.
53 if (controller_ && !controller_->IsStopped()) {
54 controller_->Stop();
55 }
56 pullTask_.Cancel();
57 }
58
Initialize()59 void ScrollFadeController::Initialize()
60 {
61 decele_ = AceType::MakeRefPtr<CurveAnimation<double>>(0.0, 1.0, Curves::DECELE);
62 auto weak = AceType::WeakClaim(this);
63 decele_->AddListener([weak](double value) {
64 auto controller = weak.Upgrade();
65 if (controller) {
66 controller->DecelerateListener(value);
67 }
68 });
69
70 controller_ = AceType::MakeRefPtr<Animator>(context_);
71 controller_->AddInterpolator(decele_);
72 controller_->AddStopListener([weak]() {
73 auto controller = weak.Upgrade();
74 if (controller) {
75 controller->ChangeState();
76 }
77 });
78 }
79
DecelerateListener(double value)80 void ScrollFadeController::DecelerateListener(double value)
81 {
82 opacity_ = opacityFloor_ + value * (opacityCeil_ - opacityFloor_);
83 scaleSize_ = scaleSizeFloor_ + value * (scaleSizeCeil_ - scaleSizeFloor_);
84 if (callback_) {
85 callback_(opacity_, scaleSize_);
86 }
87 }
88
ProcessAbsorb(double velocity)89 void ScrollFadeController::ProcessAbsorb(double velocity)
90 {
91 LOGD("ProcessAbsorb enter: velocity(%{public}lf)", velocity);
92 if (velocity < 0.0 || state_ == OverScrollState::PULL) {
93 return;
94 }
95 pullTask_.Cancel();
96 velocity = std::clamp(velocity, MIN_VELOCITY, MAX_VELOCITY);
97 opacityFloor_ = state_ == OverScrollState::IDLE ? DEFAULT_OPACITY : opacity_;
98 opacityCeil_ = std::clamp(velocity * VELOCITY_FACTOR, opacityFloor_, MAX_OPACITY);
99 scaleSizeFloor_ = scaleSize_;
100 scaleSizeCeil_ = std::min(SCALE_SIZE_BASE + SCALE_VELOCITY_FACTOR * velocity * velocity, MAX_SCALE_SIZE);
101 if (controller_) {
102 controller_->SetDuration(std::round(DURATION_BASE + velocity * DURATION_VELOCITY_FACTOR));
103 controller_->Play();
104 state_ = OverScrollState::ABSORB;
105 }
106 }
107
ProcessPull(double overDistance,double mainAxisExtent,double crossAxisExtent)108 void ScrollFadeController::ProcessPull(double overDistance, double mainAxisExtent, double crossAxisExtent)
109 {
110 LOGD("ProcessPull enter:overDistance(%{public}lf), mainAxisExtent(%{public}lf), crossAxisExtent(%{public}lf)",
111 overDistance, mainAxisExtent, crossAxisExtent);
112 pullTask_.Cancel();
113 opacityFloor_ = std::min(MIN_OPACITY + opacity_, DEFAULT_OPACITY);
114 opacityCeil_ = NearZero(mainAxisExtent)
115 ? MAX_OPACITY
116 : std::min(opacity_ + overDistance / mainAxisExtent * PULL_OPACITY_FACTOR, MAX_OPACITY);
117 double height = std::min(mainAxisExtent, crossAxisExtent * WIDTH_TO_HEIGHT_FACTOR);
118 pullDistance_ += overDistance / OVER_DISTANCE_SCALE;
119 scaleSizeFloor_ = scaleSize_;
120 double scaleByPullDistance = SCALE_CALCULATOR_FACTOR * std::sqrt(pullDistance_ * height);
121 scaleSizeCeil_ = NearZero(scaleSize_) ? MAX_SCALE_SIZE
122 : std::max(MAX_SCALE_SIZE - MAX_SCALE_SIZE / scaleByPullDistance, scaleSize_);
123 LOGD("opacityFloor_(%{public}lf), opacityCeil_(%{public}lf)", opacityFloor_, opacityCeil_);
124 LOGD("scaleSizeFloor_(%{public}lf), scaleSizeCeil_(%{public}lf)", scaleSizeFloor_, scaleSizeCeil_);
125 if (controller_) {
126 controller_->SetDuration(PULL_TIME);
127 if (state_ != OverScrollState::PULL) {
128 controller_->Play();
129 state_ = OverScrollState::PULL;
130 } else {
131 controller_->Stop();
132 controller_->Play();
133 }
134 }
135
136 SchedulePullHoldTask();
137 }
138
ProcessRecede(int32_t duration)139 void ScrollFadeController::ProcessRecede(int32_t duration)
140 {
141 LOGD("ProcessRecede enter:duration(%{public}d)", duration);
142 if (state_ == OverScrollState::RECEDE || state_ == OverScrollState::IDLE) {
143 LOGD("current state do not support recede:state_(%{public}d)", state_);
144 return;
145 }
146 pullTask_.Cancel();
147 opacityFloor_ = opacity_;
148 opacityCeil_ = 0.0;
149 scaleSizeFloor_ = scaleSize_;
150 scaleSizeCeil_ = 0.0;
151 if (controller_) {
152 controller_->SetDuration(duration);
153 controller_->Play();
154 state_ = OverScrollState::RECEDE;
155 }
156 }
157
ChangeState()158 void ScrollFadeController::ChangeState()
159 {
160 LOGD("ChangeState enter: old(%{public}d)", state_);
161 switch (state_) {
162 case OverScrollState::RECEDE:
163 state_ = OverScrollState::IDLE;
164 pullDistance_ = 0.0;
165 break;
166 case OverScrollState::ABSORB:
167 ProcessRecede(RECEDE_TIME);
168 break;
169 case OverScrollState::PULL:
170 case OverScrollState::IDLE:
171 break;
172 default:
173 break;
174 }
175 }
176
SchedulePullHoldTask()177 void ScrollFadeController::SchedulePullHoldTask()
178 {
179 const auto context = context_.Upgrade();
180 if (!context) {
181 LOGW("No context exists.");
182 return;
183 }
184
185 if (!context->GetTaskExecutor()) {
186 LOGW("context has no task executor.");
187 return;
188 }
189
190 pullTask_.Reset([weak = WeakClaim(this)] {
191 auto client = weak.Upgrade();
192 if (client) {
193 client->ProcessRecede(PULL_RECEDE_TIME);
194 }
195 });
196 context->GetTaskExecutor()->PostDelayedTask(pullTask_, TaskExecutor::TaskType::UI, PULL_HOLD_TIME);
197 }
198
199 } // namespace OHOS::Ace