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