1 /*
2 * Copyright (c) 2023 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/pattern/select_overlay/select_overlay_modifier.h"
17
18 #include <vector>
19
20 #include "base/geometry/ng/offset_t.h"
21 #include "base/utils/utils.h"
22 #include "core/components/common/properties/color.h"
23 #include "core/components/text_overlay/text_overlay_theme.h"
24 #include "core/components_ng/base/modifier.h"
25 #include "core/components_ng/render/drawing.h"
26
27 namespace OHOS::Ace::NG {
28 namespace {
29 constexpr Dimension COORDINATE_X = 8.13_vp;
30 constexpr Dimension COORDINATE_Y = 8.13_vp;
31 constexpr Dimension MORE_ANIMATION_LINEEND_X = -8.9_vp;
32 constexpr Dimension MORE_ANIMATION_LINEEND_Y = 0.6_vp;
33 constexpr Dimension MORE_ANIMATION_OTHER_CIRCLE_X = 1.25_vp;
34 constexpr Dimension MORE_ANIMATION_OTHER_CIRCLE_Y = 8.25_vp;
35 constexpr Dimension MORE_ANIMATION_END_CIRCLE_X = 9.0_vp;
36 constexpr Dimension MORE_ANIMATION_TOP_CIRCLE_Y = -0.25_vp;
37 constexpr Dimension MASK_OFFSET_Y = 1.75_vp;
38 constexpr Dimension MASK_WIDTH = 24.0_vp;
39 constexpr Dimension MASK_HEIGHT = 10.25_vp;
40
41 constexpr int32_t ICON_MICRO_ANIMATION_DURATION1 = 300;
42 constexpr int32_t ICON_MICRO_ANIMATION_DURATION2 = 200;
43 constexpr int32_t ROUND_NUMBER = 4;
44
45 constexpr float ROTATION_ANGLE = 45.0f;
46
47 std::vector<int32_t> circle_x { -1, 0, 1, 0 };
48 std::vector<int32_t> circle_Y { 0, -1, 0, 1 };
49 } // namespace
50
SelectOverlayModifier(const OffsetF & menuOptionOffset)51 SelectOverlayModifier::SelectOverlayModifier(const OffsetF& menuOptionOffset)
52 {
53 pointRadius_ = AceType::MakeRefPtr<AnimatablePropertyFloat>(Dimension(1.75_vp).ConvertToPx());
54 AttachProperty(pointRadius_);
55
56 headPointRadius_ = AceType::MakeRefPtr<AnimatablePropertyFloat>(Dimension(1.75_vp).ConvertToPx());
57 AttachProperty(headPointRadius_);
58
59 menuOptionOffset_ = AceType::MakeRefPtr<PropertyOffsetF>(OffsetF());
60 AttachProperty(menuOptionOffset_);
61
62 rotationAngle_ = AceType::MakeRefPtr<AnimatablePropertyFloat>(ROTATION_ANGLE);
63 AttachProperty(rotationAngle_);
64
65 circlesAndBackArrowOpacity_ = AceType::MakeRefPtr<AnimatablePropertyFloat>(0.0);
66 AttachProperty(circlesAndBackArrowOpacity_);
67
68 firstHandleIsShow_ = AceType::MakeRefPtr<PropertyBool>(false);
69 AttachProperty(firstHandleIsShow_);
70
71 secondHandleIsShow_ = AceType::MakeRefPtr<PropertyBool>(false);
72 AttachProperty(secondHandleIsShow_);
73
74 SetDefaultCircleAndLineEndOffset();
75 }
76
SetDefaultCircleAndLineEndOffset()77 void SelectOverlayModifier::SetDefaultCircleAndLineEndOffset()
78 {
79 for (int32_t i = 0; i < ROUND_NUMBER; i++) {
80 auto coordinate = OffsetF(COORDINATE_X.ConvertToPx() * circle_x[i], COORDINATE_Y.ConvertToPx() * circle_Y[i]);
81 auto circleOffset = AceType::MakeRefPtr<AnimatablePropertyOffsetF>(coordinate);
82 auto lineEndCoordinate = coordinate;
83 auto lineEndOffset = AceType::MakeRefPtr<AnimatablePropertyOffsetF>(lineEndCoordinate);
84 circleOffset_.emplace_back(circleOffset);
85 if (i > 0) {
86 lineEndOffset_.emplace_back(lineEndOffset);
87 AttachProperty(lineEndOffset_[i - 1]);
88 }
89 AttachProperty(circleOffset_[i]);
90 }
91 }
92
SetOtherPointRadius(const Dimension & radius)93 void SelectOverlayModifier::SetOtherPointRadius(const Dimension& radius)
94 {
95 if (pointRadius_) {
96 AnimationOption option = AnimationOption();
97 option.SetDuration(ICON_MICRO_ANIMATION_DURATION2);
98 option.SetCurve(Curves::FRICTION);
99 AnimationUtils::Animate(
100 option, [weakPointRadius = AceType::WeakClaim(AceType::RawPtr(pointRadius_)), radius]() {
101 auto pointRadius = weakPointRadius.Upgrade();
102 pointRadius->Set(radius.ConvertToPx());
103 });
104 }
105 }
106
SetHeadPointRadius(const Dimension & radius)107 void SelectOverlayModifier::SetHeadPointRadius(const Dimension& radius)
108 {
109 if (headPointRadius_) {
110 AnimationOption option = AnimationOption();
111 option.SetDuration(ICON_MICRO_ANIMATION_DURATION2);
112 option.SetCurve(Curves::FRICTION);
113 AnimationUtils::Animate(
114 option, [weakHeadPointRadius = AceType::WeakClaim(AceType::RawPtr(headPointRadius_)), radius]() {
115 auto headPointRadius = weakHeadPointRadius.Upgrade();
116 headPointRadius->Set(radius.ConvertToPx());
117 });
118 }
119 }
120
SetLineEndOffset(bool isMore)121 void SelectOverlayModifier::SetLineEndOffset(bool isMore)
122 {
123 for (int32_t i = 0; i < ROUND_NUMBER; i++) {
124 CHECK_NULL_VOID(circleOffset_[i]);
125 if (i < ROUND_NUMBER - 1) {
126 CHECK_NULL_VOID(lineEndOffset_[i]);
127 }
128 }
129 CHECK_NULL_VOID(rotationAngle_);
130 AnimationOption option = AnimationOption();
131 option.SetDuration(ICON_MICRO_ANIMATION_DURATION1);
132 option.SetCurve(Curves::FRICTION);
133 if (isMore) {
134 AnimationUtils::Animate(option, [weak = AceType::WeakClaim(this),
135 weakRotationAngle = AceType::WeakClaim(AceType::RawPtr(rotationAngle_))]() {
136 auto overlayModifier = weak.Upgrade();
137 auto rotationAngle = weakRotationAngle.Upgrade();
138 overlayModifier->circleOffset_[0]->Set(
139 OffsetF(MORE_ANIMATION_LINEEND_X.ConvertToPx(), MORE_ANIMATION_TOP_CIRCLE_Y.ConvertToPx()));
140 overlayModifier->circleOffset_[1]->Set(
141 OffsetF(-MORE_ANIMATION_OTHER_CIRCLE_X.ConvertToPx(), -MORE_ANIMATION_OTHER_CIRCLE_Y.ConvertToPx()));
142 overlayModifier->circleOffset_[2]->Set(OffsetF(MORE_ANIMATION_END_CIRCLE_X.ConvertToPx(), 0));
143 overlayModifier->circleOffset_[3]->Set(
144 OffsetF(-MORE_ANIMATION_OTHER_CIRCLE_X.ConvertToPx(), MORE_ANIMATION_OTHER_CIRCLE_Y.ConvertToPx()));
145 overlayModifier->lineEndOffset_[0]->Set(
146 OffsetF(MORE_ANIMATION_LINEEND_X.ConvertToPx(), -MORE_ANIMATION_LINEEND_Y.ConvertToPx()));
147 overlayModifier->lineEndOffset_[1]->Set(
148 OffsetF(MORE_ANIMATION_LINEEND_X.ConvertToPx(), Dimension(0, DimensionUnit::VP).ConvertToPx()));
149 overlayModifier->lineEndOffset_[2]->Set(
150 OffsetF(MORE_ANIMATION_LINEEND_X.ConvertToPx(), MORE_ANIMATION_LINEEND_Y.ConvertToPx()));
151 rotationAngle->Set(0);
152 });
153 } else {
154 for (int32_t i = 0; i < ROUND_NUMBER; i++) {
155 auto coordinate =
156 OffsetF(COORDINATE_X.ConvertToPx() * circle_x[i], COORDINATE_Y.ConvertToPx() * circle_Y[i]);
157 AnimationUtils::Animate(
158 option, [weak = AceType::WeakClaim(this),
159 weakRotationAngle = AceType::WeakClaim(AceType::RawPtr(rotationAngle_)), i, coordinate]() {
160 auto overlayModifier = weak.Upgrade();
161 auto rotationAngle = weakRotationAngle.Upgrade();
162 overlayModifier->circleOffset_[i]->Set(coordinate);
163 rotationAngle->Set(ROTATION_ANGLE);
164 if (i > 0) {
165 overlayModifier->lineEndOffset_[i - 1]->Set(coordinate);
166 };
167 });
168 }
169 }
170 }
171
onDraw(DrawingContext & drawingContext)172 void SelectOverlayModifier::onDraw(DrawingContext& drawingContext)
173 {
174 for (int32_t i = 0; i < ROUND_NUMBER; i++) {
175 CHECK_NULL_VOID(circleOffset_[i]);
176 if (i < ROUND_NUMBER - 1) {
177 CHECK_NULL_VOID(lineEndOffset_[i]);
178 }
179 }
180 CHECK_NULL_VOID(rotationAngle_);
181 CHECK_NULL_VOID(menuOptionOffset_);
182 CHECK_NULL_VOID(pointRadius_);
183 CHECK_NULL_VOID(headPointRadius_);
184 CHECK_NULL_VOID(firstHandleIsShow_);
185 CHECK_NULL_VOID(secondHandleIsShow_);
186
187 if (!isNewAvoid_ && !firstHandleIsShow_->Get() && !secondHandleIsShow_->Get()) {
188 return;
189 }
190
191 auto pipeline = PipelineContext::GetCurrentContext();
192 CHECK_NULL_VOID(pipeline);
193 auto textOverlayTheme = pipeline->GetTheme<TextOverlayTheme>();
194 CHECK_NULL_VOID(textOverlayTheme);
195 iconColor_ = textOverlayTheme->GetMoreOrBackIconColor();
196 DrawbBackArrow(drawingContext);
197 DrawbCircles(drawingContext);
198 }
199
DrawbBackArrow(DrawingContext & drawingContext)200 void SelectOverlayModifier::DrawbBackArrow(DrawingContext& drawingContext)
201 {
202 auto& canvas = drawingContext.canvas;
203 // Draw a back arrow.
204 canvas.Save();
205 canvas.Rotate(rotationAngle_->Get(), menuOptionOffset_->Get().GetX(), menuOptionOffset_->Get().GetY());
206
207 Color iconColor = iconColor_;
208 iconColor = iconColor.BlendOpacity(circlesAndBackArrowOpacity_->Get());
209 for (int32_t i = 0; i < ROUND_NUMBER - 2; i++) {
210 RSPen pen;
211 pen.SetAntiAlias(true);
212 pen.SetColor(iconColor.GetValue());
213 pen.SetWidth(pointRadius_->Get() * 2);
214 pen.SetCapStyle(RSPen::CapStyle::ROUND_CAP);
215 canvas.AttachPen(pen);
216 auto coordinate = menuOptionOffset_->Get() + circleOffset_[i + 1]->Get();
217 auto endOffset = menuOptionOffset_->Get() + lineEndOffset_[i]->Get();
218 canvas.DrawLine({ coordinate.GetX(), coordinate.GetY() }, { endOffset.GetX(), endOffset.GetY() });
219 canvas.DetachPen();
220 }
221
222 auto sideWidth = MASK_WIDTH.ConvertToPx();
223 auto maskOffset = menuOptionOffset_->Get() + OffsetF(-sideWidth / 2.0, MASK_OFFSET_Y.ConvertToPx());
224 RSRect clipInnerRect = RSRect(maskOffset.GetX(), maskOffset.GetY(), sideWidth + maskOffset.GetX(),
225 maskOffset.GetY() + MASK_HEIGHT.ConvertToPx());
226 canvas.ClipRect(clipInnerRect, RSClipOp::INTERSECT);
227 RSPen pen;
228 pen.SetAntiAlias(true);
229 pen.SetColor(iconColor.GetValue());
230 pen.SetWidth(pointRadius_->Get() * 2);
231 pen.SetCapStyle(RSPen::CapStyle::ROUND_CAP);
232 canvas.AttachPen(pen);
233 auto coordinate = menuOptionOffset_->Get() + circleOffset_[3]->Get();
234 auto endOffset = menuOptionOffset_->Get() + lineEndOffset_[2]->Get();
235 canvas.DrawLine({ coordinate.GetX(), coordinate.GetY() }, { endOffset.GetX(), endOffset.GetY() });
236 canvas.DetachPen();
237 canvas.Restore();
238 }
239
DrawbCircles(DrawingContext & drawingContext)240 void SelectOverlayModifier::DrawbCircles(DrawingContext& drawingContext)
241 {
242 auto& canvas = drawingContext.canvas;
243 // Paint other circles.
244 Color iconColor = iconColor_;
245 iconColor = iconColor.BlendOpacity(circlesAndBackArrowOpacity_->Get());
246 for (int32_t i = 0; i < ROUND_NUMBER; i++) {
247 canvas.Save();
248 canvas.Rotate(rotationAngle_->Get(), menuOptionOffset_->Get().GetX(), menuOptionOffset_->Get().GetY());
249 auto coordinate = menuOptionOffset_->Get() + circleOffset_[i]->Get();
250 canvas.Translate(coordinate.GetX(), coordinate.GetY());
251 RSBrush brush;
252 brush.SetAntiAlias(true);
253 brush.SetColor(iconColor.GetValue());
254 canvas.AttachBrush(brush);
255 // The radius UX effect of the top circle is different from other circles.
256 if (i == 0) {
257 canvas.DrawCircle({ 0.0, 0.0 }, headPointRadius_->Get());
258 } else {
259 canvas.DrawCircle({ 0.0, 0.0 }, pointRadius_->Get());
260 }
261 canvas.DetachBrush();
262 canvas.Restore();
263 }
264 }
265 } // namespace OHOS::Ace::NG
266