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