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