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