1 /*
2 * Copyright (c) 2024 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_ng/render/adapter/focus_animation_modifier.h"
17
18 #include <cmath>
19 #include <tuple>
20
21 #include "base/log/log_wrapper.h"
22 #include "core/common/container.h"
23 #include "core/components_ng/pattern/checkbox/checkbox_paint_property.h"
24 #include "core/components_ng/render/drawing.h"
25 #include "core/components_ng/render/drawing_prop_convertor.h"
26 #include "core/pipeline/base/constants.h"
27
28 namespace OHOS::Ace::NG {
29 namespace {
30 constexpr int32_t TRAJECTORY_DATA_CNT = 8;
31 constexpr int32_t TOP_SIDE = 0;
32 constexpr int32_t TOP_RIGHT = 1;
33 constexpr int32_t RIGHT_SIDE = 2;
34 constexpr int32_t BOTTOM_RIGHT = 3;
35 constexpr int32_t BOTTOM_SIDE = 4;
36 constexpr int32_t BOTTOM_LEFT = 5;
37 constexpr int32_t LEFT_SIDE = 6;
38 constexpr int32_t TOP_LEFT = 7;
39 constexpr float COLOR_POS_START = 0.0f;
40 constexpr float COLOR_POS_END = 1.0f;
41 constexpr float PERCENT_GLOW_LINE = 1.0f;
42 constexpr float ILLUMINATION_ENHANCEMENT_FACTOR = 1.2f;
43 constexpr float RISING_SLOPE = (153.0f - 25.5f) / 360.0f;
44 constexpr float RISING_CONSTANT = 25.5f;
45 constexpr float DESECEND_SLOPE = (25.5f - 153.0f) / 360.0f;
46 constexpr float DESECEND_CONSTANT = 153.0f;
47 constexpr float CORNER_LEN_CONSTANT = 0.25f;
48 constexpr uint32_t ANIMATION_DURATION = 3000;
49 constexpr float GLOW_LINE_LEN_FACTOR = 0.2f;
50 constexpr float TRANSITION_LINE_LEN_FACTOR = 0.4f;
51 } // namespace
52
FocusAnimationModifier()53 FocusAnimationModifier::FocusAnimationModifier()
54 : focusProcessFloat_(AceType::MakeRefPtr<AnimatablePropertyFloat>(0.0f))
55 {
56 AttachProperty(focusProcessFloat_);
57 InitTrajectDataFunc();
58 }
59
InitTrajectDataFunc()60 void FocusAnimationModifier::InitTrajectDataFunc()
61 {
62 CHECK_NULL_VOID(&roundRect_);
63 trajectDataFunc_[TOP_SIDE] = [this](float current, float compeleted) -> std::tuple<float, float> {
64 return std::make_tuple(topLeftX_ + current, 0);
65 };
66 trajectDataFunc_[TOP_RIGHT] = [this](float current, float compeleted) -> std::tuple<float, float> {
67 float radius = (topRightX_ + topRightY_) / 2;
68 float angle = CalArcAngle(radius, current - compeleted);
69 return std::make_tuple(
70 roundRect_.GetRect().GetWidth() - radius * (1 - std::sin(angle)), radius * (1 - std::cos(angle)));
71 };
72 trajectDataFunc_[RIGHT_SIDE] = [this](float current, float compeleted) -> std::tuple<float, float> {
73 return std::make_tuple(roundRect_.GetRect().GetWidth(), current - compeleted + topRightY_);
74 };
75 trajectDataFunc_[BOTTOM_RIGHT] = [this](float current, float compeleted) -> std::tuple<float, float> {
76 float radius = (bottomRightX_ + bottomRightY_) / 2;
77 float angle = CalArcAngle(radius, current - compeleted);
78 return std::make_tuple(roundRect_.GetRect().GetWidth() - radius * (1 - std::cos(angle)),
79 roundRect_.GetRect().GetHeight() - radius * (1 - std::sin(angle)));
80 };
81 trajectDataFunc_[BOTTOM_SIDE] = [this](float current, float compeleted) -> std::tuple<float, float> {
82 return std::make_tuple(
83 roundRect_.GetRect().GetWidth() - bottomRightX_ - (current - compeleted), roundRect_.GetRect().GetHeight());
84 };
85 trajectDataFunc_[BOTTOM_LEFT] = [this](float current, float compeleted) -> std::tuple<float, float> {
86 float radius = (bottomLeftX_ + bottomLeftY_) / 2;
87 float angle = CalArcAngle(radius, current - compeleted);
88 return std::make_tuple(
89 radius * (1 - std::sin(angle)), roundRect_.GetRect().GetHeight() - radius * (1 - std::cos(angle)));
90 };
91 trajectDataFunc_[LEFT_SIDE] = [this](float current, float compeleted) -> std::tuple<float, float> {
92 return std::make_tuple(0.0f, roundRect_.GetRect().GetHeight() - bottomRightY_ - (current - compeleted));
93 };
94 trajectDataFunc_[TOP_LEFT] = [this](float current, float compeleted) -> std::tuple<float, float> {
95 float radius = (topLeftX_ + topLeftY_) / 2;
96 float angle = CalArcAngle(radius, current - compeleted);
97 return std::make_tuple(radius * (1 - std::cos(angle)), radius * (1 - std::sin(angle)));
98 };
99 }
100
InitTrajectoryData(const RSRoundRect & rrect)101 void FocusAnimationModifier::InitTrajectoryData(const RSRoundRect& rrect)
102 {
103 CHECK_NULL_VOID(&rrect);
104 auto rect = rrect.GetRect();
105 CHECK_NULL_VOID(&rect);
106 float width = rect.GetWidth();
107 float height = rect.GetHeight();
108 longerSide_ = std::max(width, height);
109 shorterSide_ = std::min(width, height);
110 CheckBoxStyle checkBoxStyle = CheckBoxStyle::CIRCULAR_STYLE;
111 checkBoxStyle = Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN) ? CheckBoxStyle::CIRCULAR_STYLE
112 : CheckBoxStyle::SQUARE_STYLE;
113 auto delegatePtr = weakFrameNode_.Upgrade();
114 CHECK_NULL_VOID(delegatePtr);
115 auto paintProperty = delegatePtr->GetPaintProperty<CheckBoxPaintProperty>();
116 if (paintProperty && paintProperty->HasCheckBoxSelectedStyle()) {
117 checkBoxStyle = paintProperty->GetCheckBoxSelectedStyleValue(CheckBoxStyle::CIRCULAR_STYLE);
118 }
119 if (!delegatePtr->GetCheckboxFlag() || CheckBoxStyle::SQUARE_STYLE == checkBoxStyle) {
120 InitRoundRectTrajectory(rrect);
121 return;
122 }
123 InitCircleTrajectory(shorterSide_ / 2.0f);
124 }
125
InitRoundRectTrajectory(const RSRoundRect & rrect)126 void FocusAnimationModifier::InitRoundRectTrajectory(const RSRoundRect& rrect)
127 {
128 auto rect = rrect.GetRect();
129 CHECK_NULL_VOID(&rect);
130 float width = rect.GetWidth();
131 float height = rect.GetHeight();
132 float halfShorterSide = shorterSide_ / 2.0f;
133 topLeftX_ = std::min(halfShorterSide, rrect.GetCornerRadius(RSRoundRect::CornerPos::TOP_LEFT_POS).GetX());
134 topLeftY_ = std::min(halfShorterSide, rrect.GetCornerRadius(RSRoundRect::CornerPos::TOP_LEFT_POS).GetY());
135 topRightX_ = std::min(halfShorterSide, rrect.GetCornerRadius(RSRoundRect::CornerPos::TOP_RIGHT_POS).GetX());
136 topRightY_ = std::min(halfShorterSide, rrect.GetCornerRadius(RSRoundRect::CornerPos::TOP_RIGHT_POS).GetY());
137 bottomLeftX_ = std::min(halfShorterSide, rrect.GetCornerRadius(RSRoundRect::CornerPos::BOTTOM_LEFT_POS).GetX());
138 bottomLeftY_ = std::min(halfShorterSide, rrect.GetCornerRadius(RSRoundRect::CornerPos::BOTTOM_LEFT_POS).GetY());
139 bottomRightX_ = std::min(halfShorterSide, rrect.GetCornerRadius(RSRoundRect::CornerPos::BOTTOM_RIGHT_POS).GetX());
140 bottomRightY_ = std::min(halfShorterSide, rrect.GetCornerRadius(RSRoundRect::CornerPos::BOTTOM_RIGHT_POS).GetY());
141 float topSide = std::max(0.0f, width - topLeftX_ - topRightX_);
142 float leftSide = std::max(0.0f, height - topLeftY_ - bottomLeftY_);
143 float rightSide = std::max(0.0f, height - topRightY_ - bottomRightY_);
144 float bottomSide = std::max(0.0f, width - bottomLeftX_ - bottomRightX_);
145 float topLeftArc = CalArcLen((topLeftX_ + topLeftY_) / 2.0f);
146 float topRightArc = CalArcLen((topRightX_ + topRightY_) / 2.0f);
147 float bottomLeftArc = CalArcLen((bottomLeftX_ + bottomLeftY_) / 2.0f);
148 float bottomRightArc = CalArcLen((bottomRightX_ + bottomRightY_) / 2.0f);
149 grith_ = topSide + bottomSide + rightSide + leftSide + topLeftArc + topRightArc + bottomRightArc + bottomLeftArc;
150 trajectoryData_[TOP_SIDE] = topSide;
151 trajectoryData_[TOP_RIGHT] = topRightArc;
152 trajectoryData_[RIGHT_SIDE] = rightSide;
153 trajectoryData_[BOTTOM_RIGHT] = bottomRightArc;
154 trajectoryData_[BOTTOM_SIDE] = bottomSide;
155 trajectoryData_[BOTTOM_LEFT] = bottomLeftArc;
156 trajectoryData_[LEFT_SIDE] = leftSide;
157 trajectoryData_[TOP_LEFT] = topLeftArc;
158 }
159
InitCircleTrajectory(float radius)160 void FocusAnimationModifier::InitCircleTrajectory(float radius)
161 {
162 grith_ = 2.0f * ACE_PI * radius;
163 topLeftX_ = radius;
164 topLeftY_ = radius;
165 topRightX_ = radius;
166 topRightY_ = radius;
167 bottomLeftX_ = radius;
168 bottomLeftY_ = radius;
169 bottomRightX_ = radius;
170 bottomRightY_ = radius;
171 trajectoryData_[TOP_SIDE] = 0.0f;
172 trajectoryData_[TOP_RIGHT] = CORNER_LEN_CONSTANT * grith_;
173 trajectoryData_[RIGHT_SIDE] = 0.0f;
174 trajectoryData_[BOTTOM_RIGHT] = trajectoryData_[TOP_RIGHT];
175 trajectoryData_[BOTTOM_SIDE] = 0.0f;
176 trajectoryData_[BOTTOM_LEFT] = trajectoryData_[TOP_RIGHT];
177 trajectoryData_[LEFT_SIDE] = 0.0f;
178 trajectoryData_[TOP_LEFT] = trajectoryData_[TOP_RIGHT];
179 }
180
StartFocusAnimation()181 void FocusAnimationModifier::StartFocusAnimation()
182 {
183 if (animating_) {
184 return;
185 }
186 InitTrajectoryData(roundRect_);
187 animating_ = true;
188 if (focusAnimation_) {
189 AnimationUtils::ResumeAnimation(focusAnimation_);
190 return;
191 }
192 AnimationOption option = AnimationOption();
193 RefPtr<Curve> curve = AceType::MakeRefPtr<LinearCurve>();
194 option.SetDuration(ANIMATION_DURATION);
195 option.SetDelay(0);
196 option.SetCurve(curve);
197 option.SetIteration(-1);
198 focusAnimation_ = AnimationUtils::StartAnimation(
199 option, [&]() { focusProcessFloat_->Set(360.0f); }, [&]() {}, [&]() { isRise_ = !isRise_; });
200 }
201
StopFocusAnimation()202 void FocusAnimationModifier::StopFocusAnimation()
203 {
204 if (!animating_) {
205 return;
206 }
207 if (focusAnimation_) {
208 AnimationUtils::PauseAnimation(focusAnimation_);
209 }
210 animating_ = false;
211 }
212
onDraw(DrawingContext & context)213 void FocusAnimationModifier::onDraw(DrawingContext& context)
214 {
215 CHECK_NULL_VOID(&roundRect_);
216 PaintFocusState(roundRect_, context.canvas, focusProcessFloat_->Get());
217 }
218
PaintFocusState(const RSRoundRect & rrect,RSCanvas & rsCanvas,float percent)219 void FocusAnimationModifier::PaintFocusState(const RSRoundRect& rrect, RSCanvas& rsCanvas, float percent)
220 {
221 RSPen pen;
222 pen.SetAntiAlias(true);
223 pen.SetWidth(paintWidthPx_);
224 pen.SetColor(ToRSColor(paintColor_));
225 float curProcess = percent / 360.0f;
226 RSScalar halfDenominator = 2.0f;
227 RSScalar centerX = (rrect.GetRect().GetLeft() + rrect.GetRect().GetRight()) / halfDenominator;
228 RSScalar centerY = (rrect.GetRect().GetTop() + rrect.GetRect().GetBottom()) / halfDenominator;
229 auto alpha = isRise_ ? RISING_SLOPE * percent + RISING_CONSTANT : DESECEND_SLOPE * percent + DESECEND_CONSTANT;
230 RSColorQuad baseColor =
231 RSColor::ColorQuadSetARGB(alpha, paintColor_.GetRed(), paintColor_.GetGreen(), paintColor_.GetBlue());
232 RSColorQuad transitionColor = RSColor::ColorQuadSetARGB(fmin(alpha * ILLUMINATION_ENHANCEMENT_FACTOR, 255),
233 paintColor_.GetRed(), paintColor_.GetGreen(), paintColor_.GetBlue());
234 RSColorQuad glowColor = RSColor::ColorQuadSetARGB(
235 paintColor_.GetAlpha(), paintColor_.GetRed(), paintColor_.GetGreen(), paintColor_.GetBlue());
236 std::vector<RSColorQuad> colors = { baseColor, transitionColor, glowColor, glowColor, transitionColor, baseColor };
237 auto [angleStart, angleEnd, rotateAngle, pos] = GetRenderParams(curProcess, centerX, centerY);
238 rsMatrix_.Translate(-centerX, -centerY);
239 rsMatrix_.PostRotate(rotateAngle);
240 rsMatrix_.PostTranslate(centerX, centerY);
241 pen.SetShaderEffect(RSShaderEffect::CreateSweepGradient(
242 RSPoint(centerX, centerY), colors, pos, RSTileMode::CLAMP, angleStart, angleEnd, &rsMatrix_));
243 rsCanvas.AttachPen(pen);
244 auto delegatePtr = weakFrameNode_.Upgrade();
245 CHECK_NULL_VOID(delegatePtr);
246 if (!delegatePtr->GetCheckboxFlag()) {
247 rsCanvas.DrawRoundRect(rrect);
248 } else {
249 auto paintProperty = delegatePtr->GetPaintProperty<CheckBoxPaintProperty>();
250 CHECK_NULL_VOID(paintProperty);
251 CheckBoxStyle checkBoxStyle =
252 paintProperty->HasCheckBoxSelectedStyle()
253 ? paintProperty->GetCheckBoxSelectedStyleValue(CheckBoxStyle::CIRCULAR_STYLE)
254 : (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN) ? CheckBoxStyle::CIRCULAR_STYLE
255 : CheckBoxStyle::SQUARE_STYLE);
256 if (CheckBoxStyle::SQUARE_STYLE == checkBoxStyle) {
257 rsCanvas.DrawRoundRect(rrect);
258 } else {
259 rsCanvas.DrawCircle(RSPoint(centerX, centerY), shorterSide_ / halfDenominator);
260 }
261 }
262 rsCanvas.DetachPen();
263 }
264
GetRenderParams(float curProcess,float centerX,float centerY)265 std::tuple<float, float, float, std::vector<RSScalar>> FocusAnimationModifier::GetRenderParams(
266 float curProcess, float centerX, float centerY)
267 {
268 float glowLineLen = longerSide_ * PERCENT_GLOW_LINE;
269 float halfLen = glowLineLen / 2.0f;
270 float curDistance = curProcess * grith_;
271 auto [leftX, leftY] =
272 GetPosition(curDistance - halfLen < 0 ? curDistance + grith_ - halfLen : curDistance - halfLen);
273 auto [rightX, rightY] =
274 GetPosition(curDistance + halfLen > grith_ ? curDistance + halfLen - grith_ : curDistance + halfLen);
275 float leftAngle = GetIncludeAngleOfVector(centerX, centerY, centerX + 1, centerY, leftX, leftY);
276 float rightAngle = GetIncludeAngleOfVector(centerX, centerY, centerX + 1, centerY, rightX, rightY);
277 float angleEnd = leftAngle <= rightAngle ? rightAngle - leftAngle : rightAngle - leftAngle + 360.0f;
278 float glowDistance = glowLineLen * GLOW_LINE_LEN_FACTOR;
279 float transDistance = glowLineLen * TRANSITION_LINE_LEN_FACTOR;
280 float glowLeftBreakPoint =
281 curDistance - glowDistance < 0 ? curDistance + grith_ - glowDistance : curDistance - glowDistance;
282 float glowRightBreakPoint =
283 curDistance + glowDistance > grith_ ? curDistance + glowDistance - grith_ : curDistance + glowDistance;
284 float transLeftBreakPoint =
285 curDistance - transDistance < 0 ? curDistance + grith_ - transDistance : curDistance - transDistance;
286 float transRightBreakPoint =
287 curDistance + transDistance > grith_ ? curDistance + transDistance - grith_ : curDistance + transDistance;
288 float transLeftPos = GetSweepGradientRenderPos(transLeftBreakPoint, centerX, centerY, leftAngle, angleEnd);
289 float glowLeftPos = GetSweepGradientRenderPos(glowLeftBreakPoint, centerX, centerY, leftAngle, angleEnd);
290 float transRightPos = GetSweepGradientRenderPos(transRightBreakPoint, centerX, centerY, leftAngle, angleEnd);
291 float glowRightPos = GetSweepGradientRenderPos(glowRightBreakPoint, centerX, centerY, leftAngle, angleEnd);
292 std::vector<RSScalar> pos = { COLOR_POS_START, transLeftPos, glowLeftPos, glowRightPos, transRightPos,
293 COLOR_POS_END };
294 return std::make_tuple(0.0f, angleEnd, leftAngle, pos);
295 }
296
GetSweepGradientRenderPos(float distance,float centerX,float centerY,float leftAngle,float renderAngle)297 float FocusAnimationModifier::GetSweepGradientRenderPos(
298 float distance, float centerX, float centerY, float leftAngle, float renderAngle)
299 {
300 auto [posX, posY] = GetPosition(distance);
301 float angle = GetIncludeAngleOfVector(centerX, centerY, centerX + 1, centerY, posX, posY);
302 float realAngle = leftAngle <= angle ? angle - leftAngle : angle - leftAngle + 360.0f;
303 if (renderAngle == 0) {
304 return 0.0f;
305 }
306 return realAngle / renderAngle;
307 }
308
CalArcAngle(float radius,float arcLen)309 float FocusAnimationModifier::CalArcAngle(float radius, float arcLen)
310 {
311 return radius == 0 ? 0.0f : arcLen / radius;
312 }
313
CalArcLen(float radius)314 float FocusAnimationModifier::CalArcLen(float radius)
315 {
316 return 0.5f * ACE_PI * radius;
317 }
318
GetPosition(float curProcess)319 std::tuple<float, float> FocusAnimationModifier::GetPosition(float curProcess)
320 {
321 float compeletedPro = 0.0f;
322 for (int i = 0; i < TRAJECTORY_DATA_CNT; i++) {
323 if (curProcess >= compeletedPro && curProcess < compeletedPro + trajectoryData_[i]) {
324 if (trajectDataFunc_[i]) {
325 auto [x, y] = trajectDataFunc_[i](curProcess, compeletedPro);
326 return std::make_tuple(x + roundRect_.GetRect().GetLeft(), y + roundRect_.GetRect().GetTop());
327 } else {
328 return std::make_tuple(0.0f, 0.0f);
329 }
330 }
331 compeletedPro += trajectoryData_[i];
332 }
333 return std::make_tuple(0.0f, 0.0f);
334 }
335
GetIncludeAngleOfVector(float x0,float y0,float x1,float y1,float x2,float y2)336 float FocusAnimationModifier::GetIncludeAngleOfVector(float x0, float y0, float x1, float y1, float x2, float y2)
337 {
338 float xa = x1 - x0;
339 float ya = y1 - y0;
340 float xb = x2 - x0;
341 float yb = y2 - y0;
342 float d1 = std::sqrt(std::pow(xa, 2) + std::pow(ya, 2));
343 float d2 = std::sqrt(std::pow(xb, 2) + std::pow(yb, 2));
344 if ((d1 * d2) == 0) {
345 return 0.0f;
346 }
347 float res = (xa * xb + ya * yb) / (d1 * d2);
348 float angle = std::acos(res);
349 return y2 < y0 ? 360.0f - angle / ACE_PI * 180.0f : angle / ACE_PI * 180.0f;
350 }
351 } // namespace OHOS::Ace::NG