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/navigation_bar/render_collapsing_navigation_bar.h"
17
18 #include "core/components/display/display_component.h"
19 #include "core/components/navigation_bar/navigation_bar_component.h"
20 #include "core/components/navigation_bar/render_navigation_container.h"
21 #include "core/components/transform/transform_component.h"
22 #include "core/components_ng/pattern/navigation/navigation_model_data.h"
23
24 namespace OHOS::Ace {
25 namespace {
26
27 constexpr int32_t COLLAPSING_ANIMATION_DURATION = 300;
28 constexpr double SPRING_DRAG_DELTA_OFFSET_RATIO = 7;
29 constexpr double SPRING_RESTORE_DELTA_OFFSET_RATIO = 5;
30 constexpr double FIX_TITLE_BAR_OFFSET_RATIO = 1.5;
31 constexpr double BIGGER_TITLE_SIZE = 1.0;
32 constexpr double BIGGER_TITLE_SIZE_MULTIPLE = 1.1;
33 constexpr double MAX_ALPHA_VALUE = 255.0;
34 constexpr double TRANSPARENT = 0.0;
35 constexpr Dimension HIDE_TITLE_MARGIN_TOP = 12.0_vp;
36 constexpr Dimension SHOW_TITLE_MARGIN_TOP = 8.0_vp;
37 constexpr Dimension BIGGER_TITLE_POSITION_OFFSET = 30.0_vp;
38
39 } // namespace
40
Create()41 RefPtr<RenderNode> RenderCollapsingNavigationBar::Create()
42 {
43 return AceType::MakeRefPtr<RenderCollapsingNavigationBar>();
44 }
45
Update(const RefPtr<Component> & component)46 void RenderCollapsingNavigationBar::Update(const RefPtr<Component>& component)
47 {
48 Initialize(GetContext());
49
50 auto collapsingComponent = AceType::DynamicCast<CollapsingNavigationBarComponent>(component);
51 ACE_DCHECK(collapsingComponent);
52 auto pipelineContext = GetContext();
53 auto titleComposed = collapsingComponent->GetTitleComposed();
54 if (titleComposed) {
55 titleChangedCallback_ = [titleComposed, pipelineContext, weakBar = AceType::WeakClaim(this)](double fontSize) {
56 auto boxComponent = AceType::DynamicCast<BoxComponent>(titleComposed->GetChild());
57 if (!boxComponent) {
58 return;
59 }
60 auto transformComponent = AceType::DynamicCast<TransformComponent>(boxComponent->GetChild());
61 if (!transformComponent) {
62 return;
63 }
64 auto bar = weakBar.Upgrade();
65 if (!bar) {
66 return;
67 }
68 double scale = fontSize / bar->lastTitleScale_;
69 transformComponent->Scale(scale, scale);
70 bar->lastTitleScale_ = scale * bar->lastTitleScale_;
71 pipelineContext.Upgrade()->ScheduleUpdate(titleComposed);
72 };
73 }
74
75 auto subtitleComposed = collapsingComponent->GetSubtitleComposed();
76 if (subtitleComposed) {
77 subtitleChangedCallback_ = [subtitleComposed, pipelineContext](double opacity) {
78 auto boxComponent = AceType::DynamicCast<BoxComponent>(subtitleComposed->GetChild());
79 if (!boxComponent) {
80 return;
81 }
82 auto displayComponent = AceType::DynamicCast<DisplayComponent>(boxComponent->GetChild());
83 if (!displayComponent) {
84 return;
85 }
86 displayComponent->SetOpacity(opacity);
87 pipelineContext.Upgrade()->ScheduleUpdate(subtitleComposed);
88 };
89 }
90 minHeight_ = collapsingComponent->GetMinHeight();
91 auto theme = GetTheme<NavigationBarTheme>();
92 titleSize_ = ChangedKeyframe(theme->GetTitleFontSize().Value() / theme->GetTitleFontSizeBig().Value(),
93 BIGGER_TITLE_SIZE, BIGGER_TITLE_SIZE_MULTIPLE);
94 double subtitleOpacity = theme->GetSubTitleColor().GetAlpha() / MAX_ALPHA_VALUE;
95 subtitleOpacity_ = ChangedKeyframe(TRANSPARENT, subtitleOpacity, subtitleOpacity);
96 changeEvent_ = AceAsyncEvent<void(const std::shared_ptr<BaseEventInfo>&)>::Create(
97 collapsingComponent->GetTitleModeChangedEvent(), context_);
98 }
99
PerformLayout()100 void RenderCollapsingNavigationBar::PerformLayout()
101 {
102 if (!GetLayoutParam().IsValid()) {
103 return;
104 }
105 auto layoutParam = GetLayoutParam();
106 SetLayoutSize(layoutParam.GetMaxSize());
107 scrollableHeight_ = GetLayoutSize().Height() - NormalizeToPx(minHeight_);
108 auto dipScale = GetContext().Upgrade()->GetDipScale();
109 if (!NearEqual(dipScale_, dipScale)) {
110 dipScale_ = dipScale;
111 positionY_.Update(-scrollableHeight_, 0.0, BIGGER_TITLE_POSITION_OFFSET.ConvertToPx(dipScale_));
112 titlePositionY_.Update(scrollableHeight_ + NormalizeToPx(HIDE_TITLE_MARGIN_TOP),
113 NormalizeToPx(SHOW_TITLE_MARGIN_TOP + minHeight_),
114 NormalizeToPx(SHOW_TITLE_MARGIN_TOP + minHeight_ + BIGGER_TITLE_POSITION_OFFSET));
115 }
116
117 double titlePositionY;
118 double fixedToolBarPos = -positionY_.value;
119 if (GreatNotEqual(positionY_.value, positionY_.expand)) {
120 titlePositionY = titlePositionY_.expand + positionY_.value;
121 fixedToolBarPos += positionY_.value / FIX_TITLE_BAR_OFFSET_RATIO;
122 } else {
123 titlePositionY = titlePositionY_.expand - positionY_.value * titlePositionY_.expandDis / positionY_.collapse;
124 }
125
126 auto fixedToolBar = GetFirstChild();
127 if (fixedToolBar) {
128 fixedToolBar->Layout(layoutParam);
129 fixedToolBar->SetPosition(Offset(0.0, fixedToolBarPos));
130 }
131
132 if (GetChildren().empty()) {
133 LOGI("Children is empty, just return.");
134 return;
135 }
136 auto titleZone = GetChildren().back();
137 titleZone->Layout(layoutParam);
138 titleZone->SetPosition(Offset(0, titlePositionY));
139 }
140
OnRelatedStart()141 void RenderCollapsingNavigationBar::OnRelatedStart()
142 {
143 relateEvent_ = true;
144 }
145
OnRelatedPreScroll(const Offset & delta,Offset & consumed)146 void RenderCollapsingNavigationBar::OnRelatedPreScroll(const Offset& delta, Offset& consumed)
147 {
148 double dy = delta.GetY();
149 if (!NeedHidden(dy)) {
150 return;
151 }
152
153 ScrollBy(dy, positionY_.bigger);
154 consumed.SetY(dy);
155 if (!barIsMini_ && NearEqual(positionY_.value, positionY_.collapse)) {
156 barIsMini_ = true;
157
158 if (changeEvent_) {
159 changeEvent_(std::make_shared<NavigationTitleModeChangeEvent>(barIsMini_));
160 }
161 }
162 }
163
OnRelatedScroll(const Offset & delta,Offset & consumed)164 void RenderCollapsingNavigationBar::OnRelatedScroll(const Offset& delta, Offset& consumed)
165 {
166 double dy = -delta.GetY();
167 if (!NeedShow(dy)) {
168 if (!relateEvent_ && LessNotEqual(positionY_.value, positionY_.expand)) {
169 PrepareTitleSizeTranslate(titleSize_.value, titleSize_.expand);
170 PrepareSubtitleSizeTranslate(subtitleOpacity_.value, subtitleOpacity_.expand);
171 PreparePositionTranslate(positionY_.value, positionY_.expand);
172 controller_->Forward();
173 }
174 return;
175 }
176 if (barIsMini_) {
177 barIsMini_ = false;
178 if (changeEvent_) {
179 changeEvent_(std::make_shared<NavigationTitleModeChangeEvent>(barIsMini_));
180 }
181 }
182
183 if (!relateEvent_ && LessNotEqual(dy, 0.0)) {
184 dy = dy / SPRING_RESTORE_DELTA_OFFSET_RATIO;
185 } else if (GreatOrEqual(positionY_.value, positionY_.expand)) {
186 dy = dy / SPRING_DRAG_DELTA_OFFSET_RATIO;
187 }
188 ScrollBy(dy, positionY_.bigger);
189 if (LessOrEqual(positionY_.value, positionY_.expand) && relateEvent_) {
190 consumed.SetY(dy);
191 }
192 }
193
OnRelatedEnd()194 void RenderCollapsingNavigationBar::OnRelatedEnd()
195 {
196 relateEvent_ = false;
197 }
198
OnTouchTestHit(const Offset & coordinateOffset,const TouchRestrict & touchRestrict,TouchTestResult & result)199 void RenderCollapsingNavigationBar::OnTouchTestHit(
200 const Offset& coordinateOffset, const TouchRestrict& touchRestrict, TouchTestResult& result)
201 {
202 dragRecognizer_->SetCoordinateOffset(coordinateOffset);
203 result.emplace_back(dragRecognizer_);
204 }
205
Initialize(const WeakPtr<PipelineContext> & context)206 void RenderCollapsingNavigationBar::Initialize(const WeakPtr<PipelineContext>& context)
207 {
208 dragRecognizer_ = AceType::MakeRefPtr<VerticalDragRecognizer>();
209 dragRecognizer_->SetOnDragStart([weakScroll = AceType::WeakClaim(this)](const DragStartInfo& info) {
210 auto scroll = weakScroll.Upgrade();
211 if (scroll) {
212 scroll->HandleDragStart(info);
213 }
214 });
215 dragRecognizer_->SetOnDragUpdate([weakScroll = AceType::WeakClaim(this)](const DragUpdateInfo& info) {
216 auto scroll = weakScroll.Upgrade();
217 if (scroll) {
218 scroll->HandleDragUpdate(info);
219 }
220 });
221 dragRecognizer_->SetOnDragEnd([weakScroll = AceType::WeakClaim(this)](const DragEndInfo&) {
222 auto scroll = weakScroll.Upgrade();
223 if (scroll) {
224 scroll->HandleDragEnd();
225 }
226 });
227 controller_ = CREATE_ANIMATOR(context);
228 controller_->SetDuration(COLLAPSING_ANIMATION_DURATION);
229
230 InitRelatedParent(GetParent());
231 if (IsRelatedEventEnable()) {
232 auto navigationContainer = AceType::DynamicCast<RenderNavigationContainer>(relatedParent_.Upgrade());
233 if (navigationContainer) {
234 navigationContainer->SetCollapsingNavigationBar(AceType::Claim(this));
235 }
236 }
237 }
238
ScrollBy(double dy,double maxPosition)239 void RenderCollapsingNavigationBar::ScrollBy(double dy, double maxPosition)
240 {
241 lastUpScroll_ = GreatNotEqual(dy, 0.0) ? false : true;
242 positionY_.value += dy;
243 if (LessNotEqual(positionY_.value, -scrollableHeight_)) {
244 positionY_.value = -scrollableHeight_;
245 } else if (GreatNotEqual(positionY_.value, maxPosition)) {
246 positionY_.value = maxPosition;
247 }
248
249 if (titleChangedCallback_) {
250 if (GreatNotEqual(positionY_.value, positionY_.expand)) {
251 titleSize_.value = titleSize_.expand + positionY_.value * titleSize_.biggerDis / positionY_.bigger;
252 } else {
253 titleSize_.value = titleSize_.expand - positionY_.value * titleSize_.expandDis / positionY_.collapse;
254 }
255 titleChangedCallback_(titleSize_.value);
256 }
257 if (subtitleChangedCallback_ && LessNotEqual(positionY_.value, positionY_.expand)) {
258 subtitleOpacity_.value =
259 subtitleOpacity_.expand + positionY_.value * subtitleOpacity_.expandDis / scrollableHeight_;
260 subtitleChangedCallback_(subtitleOpacity_.value);
261 }
262 MarkNeedLayout(false, true);
263 }
264
HandleDragStart(const DragStartInfo & info)265 void RenderCollapsingNavigationBar::HandleDragStart(const DragStartInfo& info) {}
266
HandleDragUpdate(const DragUpdateInfo & info)267 void RenderCollapsingNavigationBar::HandleDragUpdate(const DragUpdateInfo& info)
268 {
269 double mainDelta = info.GetMainDelta();
270 bool canExpand = GreatNotEqual(mainDelta, 0.0) && LessNotEqual(positionY_.value, positionY_.expand);
271 if (!NeedHidden(mainDelta) && !canExpand) {
272 return;
273 }
274 ScrollBy(mainDelta, positionY_.expand);
275 }
276
HandleDragEnd()277 void RenderCollapsingNavigationBar::HandleDragEnd()
278 {
279 if (GreatNotEqual(positionY_.value, positionY_.expand)) {
280 PrepareTitleSizeTranslate(titleSize_.value, titleSize_.expand);
281 PrepareSubtitleSizeTranslate(subtitleOpacity_.value, subtitleOpacity_.expand);
282 PreparePositionTranslate(positionY_.value, positionY_.expand);
283 controller_->Forward();
284 } else if (GreatNotEqual(positionY_.value, positionY_.collapse) && !NearZero(positionY_.value)) {
285 if (lastUpScroll_) {
286 PrepareTitleSizeTranslate(titleSize_.value, titleSize_.collapse);
287 PrepareSubtitleSizeTranslate(subtitleOpacity_.value, subtitleOpacity_.collapse);
288 PreparePositionTranslate(positionY_.value, positionY_.collapse);
289 controller_->Forward();
290 } else {
291 PrepareTitleSizeTranslate(titleSize_.value, titleSize_.expand);
292 PrepareSubtitleSizeTranslate(subtitleOpacity_.value, subtitleOpacity_.expand);
293 PreparePositionTranslate(positionY_.value, positionY_.expand);
294 controller_->Forward();
295 }
296 }
297 }
298
PrepareTitleSizeTranslate(double expand,double collapse)299 void RenderCollapsingNavigationBar::PrepareTitleSizeTranslate(double expand, double collapse)
300 {
301 if (!titleChangedCallback_) {
302 return;
303 }
304 titleSizeTranslate_ = AceType::MakeRefPtr<CurveAnimation<double>>(expand, collapse, Curves::FRICTION);
305 auto weak = AceType::WeakClaim(this);
306 titleSizeTranslate_->AddListener(Animation<double>::ValueCallback([weak](double value) {
307 auto bar = weak.Upgrade();
308 if (bar) {
309 bar->titleChangedCallback_(value);
310 }
311 }));
312 controller_->AddInterpolator(titleSizeTranslate_);
313 }
314
PrepareSubtitleSizeTranslate(double expand,double collapse)315 void RenderCollapsingNavigationBar::PrepareSubtitleSizeTranslate(double expand, double collapse)
316 {
317 if (!subtitleChangedCallback_) {
318 return;
319 }
320 subtitleSizeTranslate_ = AceType::MakeRefPtr<CurveAnimation<double>>(expand, collapse, Curves::FRICTION);
321 auto weak = AceType::WeakClaim(this);
322 subtitleSizeTranslate_->AddListener(Animation<double>::ValueCallback([weak](double value) {
323 auto bar = weak.Upgrade();
324 if (bar) {
325 bar->subtitleChangedCallback_(value);
326 }
327 }));
328 controller_->AddInterpolator(subtitleSizeTranslate_);
329 }
330
PreparePositionTranslate(double expand,double collapse)331 void RenderCollapsingNavigationBar::PreparePositionTranslate(double expand, double collapse)
332 {
333 positionTranslate_ = AceType::MakeRefPtr<CurveAnimation<double>>(expand, collapse, Curves::FRICTION);
334 auto weak = AceType::WeakClaim(this);
335 positionTranslate_->AddListener(Animation<double>::ValueCallback([weak](double value) {
336 auto bar = weak.Upgrade();
337 if (bar) {
338 bar->positionY_.value = value;
339 bar->MarkNeedLayout();
340 }
341 }));
342 controller_->AddInterpolator(positionTranslate_);
343 }
344
345 } // namespace OHOS::Ace
346