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/list/interactive_effect.h"
17 
18 #include "base/log/event_report.h"
19 #include "core/components/list/render_list_item.h"
20 
21 namespace OHOS::Ace {
22 namespace {
23 
24 constexpr int32_t TOUCH_START_DELAY = 100; // Unit: milliseconds.
25 
26 } // namespace
27 
InteractiveEffect(const WeakPtr<PipelineContext> & context)28 InteractiveEffect::InteractiveEffect(const WeakPtr<PipelineContext>& context)
29 {
30     auto pipelineContext = context.Upgrade();
31     if (pipelineContext) {
32         context_ = pipelineContext;
33         controller_ = CREATE_ANIMATOR(pipelineContext);
34     } else {
35         LOGE("context is null.");
36     }
37 }
38 
UpdateContext(const WeakPtr<PipelineContext> & context)39 void InteractiveEffect::UpdateContext(const WeakPtr<PipelineContext>& context)
40 {
41     auto pipelineContext = context.Upgrade();
42     if (pipelineContext) {
43         context_ = pipelineContext;
44         controller_ = CREATE_ANIMATOR(pipelineContext);
45     }
46 }
47 
Initialize(const RefPtr<ThemeManager> & themeManager)48 void InteractiveEffect::Initialize(const RefPtr<ThemeManager>& themeManager)
49 {
50     if (!themeManager) {
51         return;
52     }
53     if (!theme_) {
54         theme_ = themeManager->GetTheme<ListItemTheme>();
55     }
56 }
57 
FinishPreviousAnimation()58 void InteractiveEffect::FinishPreviousAnimation()
59 {
60     if (controller_ && !controller_->IsStopped()) {
61         controller_->Finish();
62     }
63 }
64 
ShowAnimation(ItemState state)65 void InteractiveEffect::ShowAnimation(ItemState state)
66 {
67     FinishPreviousAnimation();
68     switch (state) {
69         case ItemState::FOCUS:
70             HandleOnFocus();
71             BuildStateAnimation();
72             break;
73         case ItemState::BLUR:
74             HandleOnBlur();
75             BuildStateAnimation();
76             break;
77         case ItemState::CLICK:
78             HandleOnClick();
79             BuildClickAnimation();
80             break;
81         default:
82             LOGW("invalid state.");
83             break;
84     }
85 }
86 
TouchDownAnimation()87 void InteractiveEffect::TouchDownAnimation()
88 {
89     FinishPreviousAnimation();
90     if (!theme_) {
91         LOGE("theme is invalid, stop build animation");
92         EventReport::SendComponentException(ComponentExcepType::GET_THEME_ERR);
93         return;
94     }
95     RefPtr<KeyframeAnimation<double>> alphaAnimation = AceType::MakeRefPtr<KeyframeAnimation<double>>();
96     CreateDoubleAnimation(alphaAnimation, alphaBegin_, alphaEnd_);
97     StartTouchAnimation(controller_, alphaAnimation, TOUCH_START_DELAY);
98 }
99 
TouchUpAnimation()100 void InteractiveEffect::TouchUpAnimation()
101 {
102     FinishPreviousAnimation();
103     if (!theme_) {
104         LOGE("theme is invalid, stop build animation");
105         EventReport::SendComponentException(ComponentExcepType::GET_THEME_ERR);
106         return;
107     }
108     RefPtr<KeyframeAnimation<double>> alphaAnimation = AceType::MakeRefPtr<KeyframeAnimation<double>>();
109     CreateDoubleAnimation(alphaAnimation, GetAlpha(), alphaBegin_);
110     StartTouchAnimation(controller_, alphaAnimation);
111 }
112 
CancelTouchAnimation()113 void InteractiveEffect::CancelTouchAnimation()
114 {
115     if (!theme_) {
116         LOGE("theme is invalid, stop build animation");
117         EventReport::SendComponentException(ComponentExcepType::GET_THEME_ERR);
118         return;
119     }
120     if (controller_ && !controller_->IsStopped()) {
121         controller_->Stop();
122     }
123     double currentAlpha = GetAlpha();
124     if (NearEqual(currentAlpha, alphaBegin_)) {
125         return;
126     }
127     RefPtr<KeyframeAnimation<double>> alphaAnimation = AceType::MakeRefPtr<KeyframeAnimation<double>>();
128     CreateDoubleAnimation(alphaAnimation, currentAlpha, alphaBegin_);
129     StartTouchAnimation(controller_, alphaAnimation);
130 }
131 
StartTouchAnimation(RefPtr<Animator> controller,RefPtr<KeyframeAnimation<double>> & doubleAnimation,int32_t startDelay)132 void InteractiveEffect::StartTouchAnimation(RefPtr<Animator> controller,
133     RefPtr<KeyframeAnimation<double>>& doubleAnimation, int32_t startDelay)
134 {
135     if (!controller || !doubleAnimation) {
136         return;
137     }
138     controller->ClearInterpolators();
139     controller->SetStartDelay(startDelay);
140     controller->AddInterpolator(doubleAnimation);
141     controller->SetDuration(PRESS_ANIMATION_DURATION);
142     controller->SetFillMode(FillMode::FORWARDS);
143     controller->Play();
144 }
145 
CreateDoubleAnimation(RefPtr<KeyframeAnimation<double>> & doubleAnimation,double beginValue,double endValue)146 void InteractiveEffect::CreateDoubleAnimation(RefPtr<KeyframeAnimation<double>>& doubleAnimation, double beginValue,
147     double endValue)
148 {
149     if (!doubleAnimation) {
150         return;
151     }
152     auto alphaFrameStart = AceType::MakeRefPtr<Keyframe<double>>(ANIMATION_ZERO_TIME, beginValue);
153     auto alphaFrameEnd = AceType::MakeRefPtr<Keyframe<double>>(ANIMATION_END_TIME, endValue);
154     doubleAnimation->SetCurve(Curves::SHARP);
155     doubleAnimation->AddKeyframe(alphaFrameStart);
156     doubleAnimation->AddKeyframe(alphaFrameEnd);
157     doubleAnimation->AddListener([weakEffect = AceType::WeakClaim(this)](double value) {
158         auto effect = weakEffect.Upgrade();
159         if (effect) {
160             effect->SetAlpha(value);
161             effect->MarkItemRender();
162         }
163     });
164 }
165 
NeedClickAnimation()166 bool InteractiveEffect::NeedClickAnimation()
167 {
168     auto node = item_.Upgrade();
169     RefPtr<RenderListItem> listItem;
170     if (!node || !AceType::DynamicCast<RenderListItem>(node)) {
171         return false;
172     } else {
173         listItem = AceType::DynamicCast<RenderListItem>(node);
174         if (!listItem->GetSupportClick()) {
175             return false;
176         }
177     }
178     return true;
179 }
180 
BuildClickAnimation()181 void InteractiveEffect::BuildClickAnimation()
182 {
183     if (!theme_) {
184         LOGE("theme is invalid, stop build animation");
185         EventReport::SendComponentException(ComponentExcepType::GET_THEME_ERR);
186         return;
187     }
188 
189     if (!NeedClickAnimation()) {
190         return;
191     }
192 
193     RefPtr<KeyframeAnimation<double>> alphaAnimation = AceType::MakeRefPtr<KeyframeAnimation<double>>();
194     BuildClickAlphaAnimation(alphaAnimation);
195     if (controller_) {
196         controller_->ClearInterpolators();
197         controller_->AddInterpolator(alphaAnimation);
198         controller_->SetDuration(clickDuration_);
199         controller_->Play();
200     }
201 }
202 
MarkItemRender()203 void InteractiveEffect::MarkItemRender()
204 {
205     auto item = item_.Upgrade();
206     if (item) {
207         item->MarkNeedRender();
208         auto listItem = AceType::DynamicCast<RenderListItem>(item);
209         if (listItem) {
210             if (listItem->GetSticky()) {
211                 auto list = list_.Upgrade();
212                 if (list) {
213                     list->MarkNeedRender();
214                 }
215             }
216         }
217     }
218 }
219 
BuildClickAlphaAnimation(const RefPtr<KeyframeAnimation<double>> & alphaAnimation)220 void InteractiveEffect::BuildClickAlphaAnimation(const RefPtr<KeyframeAnimation<double>>& alphaAnimation)
221 {
222     auto alphaFrameStart = AceType::MakeRefPtr<Keyframe<double>>(ANIMATION_ZERO_TIME, alphaBegin_);
223     auto alphaFrameMid = AceType::MakeRefPtr<Keyframe<double>>(ANIMATION_HALF_TIME, alphaEnd_);
224     auto alphaFrameEnd = AceType::MakeRefPtr<Keyframe<double>>(ANIMATION_END_TIME, alphaBegin_);
225     alphaFrameMid->SetCurve(Curves::FRICTION);
226     alphaFrameEnd->SetCurve(Curves::FRICTION);
227 
228     alphaAnimation->AddKeyframe(alphaFrameStart);
229     alphaAnimation->AddKeyframe(alphaFrameMid);
230     alphaAnimation->AddKeyframe(alphaFrameEnd);
231     alphaAnimation->AddListener([weakEffect = AceType::WeakClaim(this)](double value) {
232         auto effect = weakEffect.Upgrade();
233         if (effect) {
234             effect->SetAlpha(value);
235             effect->MarkItemRender();
236         }
237     });
238 }
239 
240 } // namespace OHOS::Ace
241