• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2022 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 "bridge/declarative_frontend/jsview/js_scroller.h"
17 
18 #include "base/geometry/axis.h"
19 #include "base/utils/linear_map.h"
20 #include "base/utils/utils.h"
21 #include "bridge/declarative_frontend/engine/js_types.h"
22 #include "bridge/declarative_frontend/jsview/js_view_common_def.h"
23 #include "core/animation/curves.h"
24 #include "core/common/container.h"
25 #include "core/components/common/layout/align_declaration.h"
26 
27 namespace OHOS::Ace::Framework {
28 namespace {
29 
30 constexpr Axis DIRECTION_TABLE[] = { Axis::VERTICAL, Axis::HORIZONTAL };
31 
32 constexpr AlignDeclaration::Edge EDGE_TABLE[] = {
33     AlignDeclaration::Edge::TOP,
34     AlignDeclaration::Edge::CENTER,
35     AlignDeclaration::Edge::BOTTOM,
36     AlignDeclaration::Edge::BASELINE,
37     AlignDeclaration::Edge::START,
38     AlignDeclaration::Edge::MIDDLE,
39     AlignDeclaration::Edge::END,
40 };
41 
42 // corresponding to EDGE_TABLE[]
43 constexpr ScrollEdgeType EDGE_TYPE_TABLE[] = { ScrollEdgeType::SCROLL_TOP, ScrollEdgeType::SCROLL_NONE,
44     ScrollEdgeType::SCROLL_BOTTOM, ScrollEdgeType::SCROLL_NONE, ScrollEdgeType::SCROLL_TOP, ScrollEdgeType::SCROLL_NONE,
45     ScrollEdgeType::SCROLL_BOTTOM };
46 
47 const LinearMapNode<RefPtr<Curve>> CURVE_MAP[] = {
48     { "ease", Curves::EASE },
49     { "ease-in", Curves::EASE_IN },
50     { "ease-in-out", Curves::EASE_IN_OUT },
51     { "ease-out", Curves::EASE_OUT },
52     { "friction", Curves::FRICTION },
53     { "linear", Curves::LINEAR },
54 };
55 
56 constexpr double DEFAULT_DURATION = 1000.0;
57 constexpr ScrollAlign ALIGN_TABLE[] = {
58     ScrollAlign::START,
59     ScrollAlign::CENTER,
60     ScrollAlign::END,
61     ScrollAlign::AUTO,
62 };
63 
64 const std::regex DIMENSION_REGEX(R"(^[-+]?\d+(?:\.\d+)?(?:px|vp|fp|lpx)?$)", std::regex::icase);
65 
66 } // namespace
67 
JSBind(BindingTarget globalObj)68 void JSScroller::JSBind(BindingTarget globalObj)
69 {
70     JSClass<JSScroller>::Declare("Scroller");
71     JSClass<JSScroller>::CustomMethod("scrollTo", &JSScroller::ScrollTo);
72     JSClass<JSScroller>::CustomMethod("scrollEdge", &JSScroller::ScrollEdge);
73     JSClass<JSScroller>::CustomMethod("scrollPage", &JSScroller::ScrollPage);
74     JSClass<JSScroller>::CustomMethod("currentOffset", &JSScroller::CurrentOffset);
75     JSClass<JSScroller>::CustomMethod("scrollToIndex", &JSScroller::ScrollToIndex);
76     JSClass<JSScroller>::CustomMethod("scrollBy", &JSScroller::ScrollBy);
77     JSClass<JSScroller>::CustomMethod("isAtEnd", &JSScroller::IsAtEnd);
78     JSClass<JSScroller>::Bind(globalObj, JSScroller::Constructor, JSScroller::Destructor);
79 }
80 
Constructor(const JSCallbackInfo & args)81 void JSScroller::Constructor(const JSCallbackInfo& args)
82 {
83     auto scroller = Referenced::MakeRefPtr<JSScroller>();
84     scroller->IncRefCount();
85     args.SetReturnValue(Referenced::RawPtr(scroller));
86 }
87 
Destructor(JSScroller * scroller)88 void JSScroller::Destructor(JSScroller* scroller)
89 {
90     if (scroller != nullptr) {
91         scroller->DecRefCount();
92     }
93 }
94 
ScrollTo(const JSCallbackInfo & args)95 void JSScroller::ScrollTo(const JSCallbackInfo& args)
96 {
97     if (args.Length() < 1 || !args[0]->IsObject()) {
98         LOGW("Invalid params");
99         return;
100     }
101 
102     JSRef<JSObject> obj = JSRef<JSObject>::Cast(args[0]);
103     Dimension xOffset;
104     Dimension yOffset;
105     auto xOffsetStr = obj->GetProperty("xOffset");
106     auto yOffsetStr = obj->GetProperty("yOffset");
107     if (!std::regex_match(xOffsetStr->ToString(), DIMENSION_REGEX) ||
108         !std::regex_match(yOffsetStr->ToString(), DIMENSION_REGEX) || !ConvertFromJSValue(xOffsetStr, xOffset) ||
109         !ConvertFromJSValue(yOffsetStr, yOffset)) {
110         LOGW("Failed to parse param 'xOffset' or 'yOffset'");
111         return;
112     }
113 
114     double duration = 0.0;
115     bool smooth = false;
116     RefPtr<Curve> curve = Curves::EASE;
117     auto animationValue = obj->GetProperty("animation");
118     if (animationValue->IsObject()) {
119         auto animationObj = JSRef<JSObject>::Cast(animationValue);
120         if (!ConvertFromJSValue(animationObj->GetProperty("duration"), duration) || NonPositive(duration)) {
121             LOGW("Failed to parse param 'duration' or it is not a positive number, set it as the default value");
122             duration = DEFAULT_DURATION;
123         }
124 
125         auto curveArgs = animationObj->GetProperty("curve");
126         ParseCurveParams(curve, curveArgs);
127     } else if (animationValue->IsBoolean()) {
128         smooth = animationValue->ToBoolean();
129     }
130 
131     if (GreatNotEqual(duration, 0.0)) {
132         LOGD("ScrollTo(%lf, %lf, %lf)", xOffset.Value(), yOffset.Value(), duration);
133     } else {
134         LOGD("ScrollTo(%lf, %lf)", xOffset.Value(), yOffset.Value());
135     }
136     auto scrollController = controllerWeak_.Upgrade();
137     if (!scrollController) {
138         LOGE("controller_ is nullptr");
139         return;
140     }
141     auto direction = scrollController->GetScrollDirection();
142     auto position = direction == Axis::VERTICAL ? yOffset : xOffset;
143     scrollController->AnimateTo(position, static_cast<float>(duration), curve, smooth);
144 }
145 
ParseCurveParams(RefPtr<Curve> & curve,const JSRef<JSVal> & jsValue)146 void JSScroller::ParseCurveParams(RefPtr<Curve>& curve, const JSRef<JSVal>& jsValue)
147 {
148     std::string curveName;
149     if (ConvertFromJSValue(jsValue, curveName)) {
150         auto index = BinarySearchFindIndex(CURVE_MAP, ArraySize(CURVE_MAP), curveName.c_str());
151         if (index >= 0) {
152             curve = CURVE_MAP[index].value;
153         }
154     } else if (jsValue->IsObject()) {
155         auto icurveArgs = JsonUtil::ParseJsonString(jsValue->ToString());
156         if (icurveArgs->IsObject()) {
157             auto curveString = icurveArgs->GetValue("__curveString");
158             curve = CreateCurve(curveString->GetString());
159         }
160     }
161 }
162 
ScrollEdge(const JSCallbackInfo & args)163 void JSScroller::ScrollEdge(const JSCallbackInfo& args)
164 {
165     AlignDeclaration::Edge edge = AlignDeclaration::Edge::AUTO;
166     if (args.Length() < 1 || !ConvertFromJSValue(args[0], EDGE_TABLE, edge)) {
167         LOGW("Invalid params");
168         return;
169     }
170     auto scrollController = controllerWeak_.Upgrade();
171     if (!scrollController) {
172         LOGE("controller_ is nullptr");
173         return;
174     }
175     LOGD("ScrollEdge(%{public}d)", static_cast<int32_t>(edge));
176     ScrollEdgeType edgeType = EDGE_TYPE_TABLE[static_cast<int32_t>(edge)];
177     scrollController->ScrollToEdge(edgeType, true);
178 }
179 
ScrollToIndex(const JSCallbackInfo & args)180 void JSScroller::ScrollToIndex(const JSCallbackInfo& args)
181 {
182     int32_t index = 0;
183     bool smooth = false;
184     ScrollAlign align = ScrollAlign::NONE;
185     if (args.Length() < 1 || !ConvertFromJSValue(args[0], index) || index < 0) {
186         LOGW("Invalid params");
187         return;
188     }
189     auto scrollController = controllerWeak_.Upgrade();
190     if (!scrollController) {
191         LOGE("controller_ is nullptr");
192         return;
193     }
194     // 2:parameters count, 1: parameter index
195     if (args.Length() >= 2 && args[1]->IsBoolean()) {
196         smooth = args[1]->ToBoolean();
197     }
198     // 3:parameters count, 2: parameter index
199     if (args.Length() == 3 && !ConvertFromJSValue(args[2], ALIGN_TABLE, align)) {
200         LOGE("Invalid align params");
201     }
202     scrollController->JumpTo(index, smooth, align, SCROLL_FROM_JUMP);
203 }
204 
ScrollPage(const JSCallbackInfo & args)205 void JSScroller::ScrollPage(const JSCallbackInfo& args)
206 {
207     if (args.Length() < 1 || !args[0]->IsObject()) {
208         LOGW("Invalid params");
209         return;
210     }
211 
212     auto obj = JSRef<JSObject>::Cast(args[0]);
213     bool next = true;
214     if (!ConvertFromJSValue(obj->GetProperty("next"), next)) {
215         LOGW("Failed to parse param 'next'");
216         return;
217     }
218 
219     Axis direction = Axis::NONE;
220     ConvertFromJSValue(obj->GetProperty("direction"), DIRECTION_TABLE, direction);
221     auto scrollController = controllerWeak_.Upgrade();
222     if (!scrollController) {
223         LOGE("controller_ is nullptr");
224         return;
225     }
226     LOGD("ScrollPage(%{public}s, %{public}d)", next ? "true" : "false", static_cast<int32_t>(direction));
227     scrollController->ScrollPage(!next, true);
228 }
229 
CurrentOffset(const JSCallbackInfo & args)230 void JSScroller::CurrentOffset(const JSCallbackInfo& args)
231 {
232     LOGD("CurrentOffset()");
233     auto scrollController = controllerWeak_.Upgrade();
234     if (!scrollController) {
235         LOGE("controller_ is nullptr");
236         return;
237     }
238     auto retObj = JSRef<JSObject>::New();
239     auto offset = scrollController->GetCurrentOffset();
240     retObj->SetProperty("xOffset", offset.GetX());
241     retObj->SetProperty("yOffset", offset.GetY());
242     args.SetReturnValue(retObj);
243 }
244 
ScrollBy(const JSCallbackInfo & args)245 void JSScroller::ScrollBy(const JSCallbackInfo& args)
246 {
247     if (args.Length() < 2) {
248         LOGW("Invalid params");
249         return;
250     }
251 
252     Dimension xOffset;
253     Dimension yOffset;
254     if (!ConvertFromJSValue(args[0], xOffset) ||
255         !ConvertFromJSValue(args[1], yOffset)) {
256         LOGW("Failed to parse param");
257         return;
258     }
259 
260     auto deltaX = xOffset.Value();
261     auto deltaY = yOffset.Value();
262     auto container = Container::Current();
263     if (container) {
264         auto context = container->GetPipelineContext();
265         if (context) {
266             if (xOffset.Unit() == DimensionUnit::PERCENT) {
267                 deltaX = 0.0;
268             } else {
269                 deltaX = context->NormalizeToPx(xOffset);
270             }
271             if (yOffset.Unit() == DimensionUnit::PERCENT) {
272                 deltaY = 0.0;
273             } else {
274                 deltaY = context->NormalizeToPx(yOffset);
275             }
276         }
277     }
278     auto scrollController = controllerWeak_.Upgrade();
279     if (scrollController) {
280         scrollController->ScrollBy(deltaX, deltaY, false);
281     }
282 }
283 
IsAtEnd(const JSCallbackInfo & args)284 void JSScroller::IsAtEnd(const JSCallbackInfo& args)
285 {
286     auto scrollController = controllerWeak_.Upgrade();
287     if (!scrollController) {
288         LOGE("controller_ is nullptr");
289         return;
290     }
291     bool isAtEnd = scrollController->IsAtEnd();
292     auto retVal = JSRef<JSVal>::Make(ToJSValue(isAtEnd));
293     args.SetReturnValue(retVal);
294 }
295 } // namespace OHOS::Ace::Framework
296