• 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 
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