• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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