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 #include "bridge/declarative_frontend/jsview/js_particle.h"
16 
17 #include <array>
18 #include <utility>
19 
20 #include "core/components_ng/property/particle_property.h"
21 #include "core/components_ng/property/particle_property_animation.h"
22 namespace OHOS::Ace {
23 std::unique_ptr<ParticleModel> ParticleModel::instance_ = nullptr;
24 std::mutex ParticleModel::mutex_;
GetInstance()25 ParticleModel* ParticleModel::GetInstance()
26 {
27     if (!instance_) {
28         std::lock_guard<std::mutex> lock(mutex_);
29         if (!instance_) {
30 #ifdef NG_BUILD
31             instance_.reset(new NG::ParticleModelNG());
32 #else
33             if (Container::IsCurrentUseNewPipeline()) {
34                 instance_.reset(new NG::ParticleModelNG());
35             }
36 #endif
37         }
38     }
39     return instance_.get();
40 }
41 } // namespace OHOS::Ace
42 namespace OHOS::Ace::Framework {
43 namespace {
44 constexpr int32_t ARRAY_SIZE = 2;
45 constexpr int32_t PARTICLE_DEFAULT_EMITTER_RATE = 5;
46 constexpr float MIN_BOUNDARY = -100.0f;
47 constexpr float MIN_LIMIT = -10000.0f;
48 constexpr float MAX_BOUNDARY = 100.0f;
49 constexpr float MAX_LIMIT = 10000.0f;
50 constexpr float DEFAULT_OPACITY = 1.0f;
51 constexpr float DEFAULT_SCALE = 1.0f;
52 constexpr float DEFAULT_SPIN = 0.0f;
53 constexpr float DEFAULT_SPEED = 0.0f;
54 constexpr float DEFAULT_ANGLE = 0.0f;
55 constexpr float MIN_OPACITY = 0.0f;
56 constexpr float MIN_SCALE = 0.0f;
57 constexpr float MIN_SPIN = MIN_LIMIT;
58 constexpr float MIN_SPEED = 0.0f;
59 constexpr float MIN_ANGLE = MIN_LIMIT;
60 constexpr float MAX_OPACITY = 1.0f;
61 constexpr float MAX_SCALE = MAX_LIMIT;
62 constexpr float MAX_SPIN = MAX_LIMIT;
63 constexpr float MAX_SPEED = MAX_LIMIT;
64 constexpr float MAX_ANGLE = MAX_LIMIT;
65 
66 constexpr int DEFAULT_COLOR = 0xffffffff;
67 
ParsSize(std::pair<Dimension,Dimension> & size,JSRef<JSVal> & sizeJsValue)68 void ParsSize(std::pair<Dimension, Dimension>& size, JSRef<JSVal>& sizeJsValue)
69 {
70     if (sizeJsValue->IsArray()) {
71         auto sizeJsArray = JSRef<JSArray>::Cast(sizeJsValue);
72         if (static_cast<int32_t>(sizeJsArray->Length()) == ARRAY_SIZE) {
73             CalcDimension xValue;
74             CalcDimension yValue;
75             if (JSParticle::ParseJsDimensionVp(sizeJsArray->GetValueAt(0), xValue) &&
76                 GreatOrEqual(xValue.Value(), 0.0)) {
77                 size.first = xValue;
78             }
79             if (JSParticle::ParseJsDimensionVp(sizeJsArray->GetValueAt(1), yValue) &&
80                 GreatOrEqual(yValue.Value(), 0.0)) {
81                 size.second = yValue;
82             }
83         }
84     }
85 }
86 
ParseParticleRange(JSRef<JSVal> & jsValue,float defaultValue)87 std::optional<std::pair<float, float>> ParseParticleRange(JSRef<JSVal>& jsValue, float defaultValue)
88 {
89     std::optional<std::pair<float, float>> rangeOpt;
90     auto defaultPair = std::pair<float, float>(defaultValue, defaultValue);
91     if (!jsValue->IsArray()) {
92         rangeOpt = defaultPair;
93         return rangeOpt;
94     }
95     auto jsArray = JSRef<JSArray>::Cast(jsValue);
96     if (jsArray->Length() != ARRAY_SIZE) {
97         rangeOpt = defaultPair;
98         return rangeOpt;
99     }
100     auto from = defaultValue;
101     if (jsArray->GetValueAt(0)->IsNumber()) {
102         from = jsArray->GetValueAt(0)->ToNumber<float>();
103     }
104     auto to = defaultValue;
105     if (jsArray->GetValueAt(1)->IsNumber()) {
106         to = jsArray->GetValueAt(1)->ToNumber<float>();
107     }
108     if (GreatNotEqual(from, to)) {
109         rangeOpt = defaultPair;
110         return rangeOpt;
111     }
112     rangeOpt = std::pair<float, float>(from, to);
113     return rangeOpt;
114 }
115 
ParseCurve(JSRef<JSVal> & curveJsValue)116 RefPtr<Curve> ParseCurve(JSRef<JSVal>& curveJsValue)
117 {
118     RefPtr<Curve> curve;
119     if (curveJsValue->IsString()) {
120         std::string src;
121         if (JSParticle::ParseJsString(curveJsValue, src)) {
122             curve = CreateCurve(src);
123         }
124     }
125     if (!curve) {
126         curve = AceType::MakeRefPtr<LinearCurve>();
127     }
128     return curve;
129 }
130 
ParseAnimationFloatArray(JSRef<JSArray> & curveConfigJsArray,std::list<NG::ParticlePropertyAnimation<float>> & particleAnimationFloatArray,float defaultValue,float minValue,float maxValue)131 void ParseAnimationFloatArray(JSRef<JSArray>& curveConfigJsArray,
132     std::list<NG::ParticlePropertyAnimation<float>>& particleAnimationFloatArray, float defaultValue, float minValue,
133     float maxValue)
134 {
135     auto arraySize = static_cast<int32_t>(curveConfigJsArray->Length());
136     for (int i = 0; i < arraySize; i++) {
137         auto arrayItemJsValue = curveConfigJsArray->GetValueAt(i);
138         NG::ParticlePropertyAnimation<float> floatPropertyAnimation;
139         if (!arrayItemJsValue->IsObject()) {
140             continue;
141         }
142         auto arrayItemJsObject = JSRef<JSObject>::Cast(arrayItemJsValue);
143         auto fromJsValue = arrayItemJsObject->GetProperty("from");
144         float from = defaultValue;
145         if (fromJsValue->IsNumber()) {
146             from = fromJsValue->ToNumber<float>();
147             if (GreatNotEqual(minValue, MIN_BOUNDARY) && LessNotEqual(from, minValue)) {
148                 from = defaultValue;
149             }
150             if (LessNotEqual(maxValue, MAX_BOUNDARY) && GreatNotEqual(from, maxValue)) {
151                 from = defaultValue;
152             }
153         }
154         floatPropertyAnimation.SetFrom(from);
155         auto toJsValue = arrayItemJsObject->GetProperty("to");
156         float to = defaultValue;
157         if (toJsValue->IsNumber()) {
158             to = toJsValue->ToNumber<float>();
159             if (GreatNotEqual(minValue, MIN_BOUNDARY) && LessNotEqual(to, minValue)) {
160                 to = defaultValue;
161             }
162             if (LessNotEqual(maxValue, MAX_BOUNDARY) && GreatNotEqual(to, maxValue)) {
163                 to = defaultValue;
164             }
165         }
166         floatPropertyAnimation.SetTo(to);
167         auto startMillisJsValue = arrayItemJsObject->GetProperty("startMillis");
168         auto startMillis = static_cast<int32_t>(0);
169         if (!JSParticle::ParseJsInt32(startMillisJsValue, startMillis) || startMillis < 0) {
170             startMillis = 0;
171         }
172         floatPropertyAnimation.SetStartMills(startMillis);
173         auto endMillisJsValue = arrayItemJsObject->GetProperty("endMillis");
174         auto endMillis = static_cast<int32_t>(0);
175         if (!JSParticle::ParseJsInt32(endMillisJsValue, endMillis) || endMillis < 0) {
176             endMillis = 0;
177         }
178         floatPropertyAnimation.SetEndMills(endMillis);
179         auto curveJsValue = arrayItemJsObject->GetProperty("curve");
180         auto curve = ParseCurve(curveJsValue);
181         if (curve) {
182             floatPropertyAnimation.SetCurve(curve);
183         }
184         particleAnimationFloatArray.emplace_back(floatPropertyAnimation);
185     }
186 }
187 
ParseFloatRandomConfig(JSRef<JSVal> & configJsValue,OHOS::Ace::NG::ParticleFloatPropertyUpdater & updater)188 bool ParseFloatRandomConfig(JSRef<JSVal>& configJsValue, OHOS::Ace::NG::ParticleFloatPropertyUpdater& updater)
189 {
190     if (!configJsValue->IsArray()) {
191         return false;
192     }
193     auto randomConfigJsArray = JSRef<JSArray>::Cast(configJsValue);
194     auto randomArraySize = static_cast<int32_t>(randomConfigJsArray->Length());
195     if (randomArraySize != ARRAY_SIZE) {
196         return false;
197     }
198     auto randomRangePair = ParseParticleRange(configJsValue, 0.0f);
199     if (!randomRangePair.has_value()) {
200         return false;
201     }
202     NG::ParticleFloatPropertyUpdaterConfig randomUpdaterConfig;
203     randomUpdaterConfig.SetRandomConfig(randomRangePair.value());
204     updater.SetConfig(randomUpdaterConfig);
205     return true;
206 }
207 
ParseFloatCurveConfig(JSRef<JSVal> & configJsValue,OHOS::Ace::NG::ParticleFloatPropertyUpdater & updater,float defaultValue,float minValue,float maxValue)208 bool ParseFloatCurveConfig(JSRef<JSVal>& configJsValue, OHOS::Ace::NG::ParticleFloatPropertyUpdater& updater,
209     float defaultValue, float minValue, float maxValue)
210 {
211     if (!configJsValue->IsArray()) {
212         return false;
213     }
214     auto curveConfigJsArray = JSRef<JSArray>::Cast(configJsValue);
215     std::list<NG::ParticlePropertyAnimation<float>> particleAnimationFloatArray;
216     ParseAnimationFloatArray(curveConfigJsArray, particleAnimationFloatArray, defaultValue, minValue, maxValue);
217     NG::ParticleFloatPropertyUpdaterConfig updateConfig;
218     updateConfig.SetAnimations(particleAnimationFloatArray);
219     updater.SetConfig(updateConfig);
220     return true;
221 }
222 
ParseFloatUpdater(JSRef<JSObject> & updaterJsObject,OHOS::Ace::NG::ParticleFloatPropertyUpdater & updater,float defaultValue,float minValue,float maxValue)223 bool ParseFloatUpdater(JSRef<JSObject>& updaterJsObject, OHOS::Ace::NG::ParticleFloatPropertyUpdater& updater,
224     float defaultValue, float minValue, float maxValue)
225 {
226     auto typeJsValue = updaterJsObject->GetProperty("type");
227     if (typeJsValue->IsNumber()) {
228         auto typeIntValue = typeJsValue->ToNumber<int32_t>();
229         if (typeIntValue < NG::UpdaterType::NONE_UPDATER || typeIntValue > NG::UpdaterType::CURVE) {
230             typeIntValue = NG::UpdaterType::NONE_UPDATER;
231         }
232         auto type = static_cast<NG::UpdaterType>(typeIntValue);
233         updater.SetUpdaterType(type);
234         auto configJsValue = updaterJsObject->GetProperty("config");
235         if (type == NG::UpdaterType::RANDOM) {
236             if (!ParseFloatRandomConfig(configJsValue, updater)) {
237                 auto randomRangePair = std::pair<float, float>(0.0f, 0.0f);
238                 NG::ParticleFloatPropertyUpdaterConfig randomUpdaterConfig;
239                 randomUpdaterConfig.SetRandomConfig(randomRangePair);
240                 updater.SetConfig(randomUpdaterConfig);
241             }
242             return true;
243         } else if (type == NG::UpdaterType::CURVE) {
244             if (!ParseFloatCurveConfig(configJsValue, updater, defaultValue, minValue, maxValue)) {
245                 std::list<NG::ParticlePropertyAnimation<float>> particleAnimationFloatArray;
246                 NG::ParticleFloatPropertyUpdaterConfig updateConfig;
247                 updateConfig.SetAnimations(particleAnimationFloatArray);
248                 updater.SetConfig(updateConfig);
249             }
250             return true;
251         }
252     }
253     return false;
254 }
255 
ParseFloatInitRange(JSRef<JSVal> & floatRangeJsValue,OHOS::Ace::NG::ParticleFloatPropertyOption & floatOption,float defaultValue,float minValue,float maxValue)256 void ParseFloatInitRange(JSRef<JSVal>& floatRangeJsValue, OHOS::Ace::NG::ParticleFloatPropertyOption& floatOption,
257     float defaultValue, float minValue, float maxValue)
258 {
259     auto defaultPair = std::pair<float, float>(defaultValue, defaultValue);
260     if (!floatRangeJsValue->IsArray()) {
261         floatOption.SetRange(defaultPair);
262         return;
263     }
264     auto floatRangeJsArray = JSRef<JSArray>::Cast(floatRangeJsValue);
265     if (floatRangeJsArray->Length() != ARRAY_SIZE) {
266         floatOption.SetRange(defaultPair);
267         return;
268     }
269     auto from = defaultValue;
270     auto fromJsValue = floatRangeJsArray->GetValueAt(0);
271     if (fromJsValue->IsNumber()) {
272         from = fromJsValue->ToNumber<float>();
273         if (GreatNotEqual(minValue, MIN_BOUNDARY) && LessNotEqual(from, minValue)) {
274             from = defaultValue;
275         }
276         if (LessNotEqual(maxValue, MAX_BOUNDARY) && GreatNotEqual(from, maxValue)) {
277             from = defaultValue;
278         }
279     }
280     auto to = defaultValue;
281     auto toJsValue = floatRangeJsArray->GetValueAt(1);
282     if (toJsValue->IsNumber()) {
283         to = toJsValue->ToNumber<float>();
284         if (GreatNotEqual(minValue, MIN_BOUNDARY) && LessNotEqual(to, minValue)) {
285             to = defaultValue;
286         }
287         if (LessNotEqual(maxValue, MAX_BOUNDARY) && GreatNotEqual(to, maxValue)) {
288             to = defaultValue;
289         }
290     }
291     if (GreatNotEqual(from, to)) {
292         from = defaultValue;
293         to = defaultValue;
294     }
295     auto range = std::pair<float, float>(from, to);
296     floatOption.SetRange(range);
297 }
298 
ParseFloatOption(JSRef<JSObject> & floatJsObject,OHOS::Ace::NG::ParticleFloatPropertyOption & floatOption,float defaultValue,float minValue,float maxValue)299 void ParseFloatOption(JSRef<JSObject>& floatJsObject, OHOS::Ace::NG::ParticleFloatPropertyOption& floatOption,
300     float defaultValue, float minValue, float maxValue)
301 {
302     auto floatRangeJsValue = floatJsObject->GetProperty("range");
303     ParseFloatInitRange(floatRangeJsValue, floatOption, defaultValue, minValue, maxValue);
304     auto updaterJsValue = floatJsObject->GetProperty("updater");
305     NG::ParticleFloatPropertyUpdater updater;
306     if (updaterJsValue->IsObject()) {
307         auto updaterJsObject = JSRef<JSObject>::Cast(updaterJsValue);
308         if (ParseFloatUpdater(updaterJsObject, updater, defaultValue, minValue, maxValue)) {
309             floatOption.SetUpdater(updater);
310             return;
311         }
312     }
313     updater.SetUpdaterType(NG::UpdaterType::NONE_UPDATER);
314     NG::ParticleFloatPropertyUpdaterConfig updateConfig;
315     updateConfig.SetNullStr("");
316     updater.SetConfig(updateConfig);
317     floatOption.SetUpdater(updater);
318 }
319 
ParseParticleObject(JSRef<JSObject> & particleJsObject,OHOS::Ace::NG::Particle & particle)320 bool ParseParticleObject(JSRef<JSObject>& particleJsObject, OHOS::Ace::NG::Particle& particle)
321 {
322     auto typeJsValue = particleJsObject->GetProperty("type");
323     auto typeValue = NG::ParticleType::POINT;
324     if (typeJsValue->IsNumber()) {
325         auto typeIntValue = typeJsValue->ToNumber<int32_t>();
326         if (typeIntValue >= static_cast<int32_t>(NG::ParticleType::POINT) &&
327             typeIntValue <= static_cast<int32_t>(NG::ParticleType::IMAGE)) {
328             typeValue = static_cast<NG::ParticleType>(typeIntValue);
329         }
330     }
331     particle.SetParticleType(typeValue);
332 
333     auto configJsValue = particleJsObject->GetProperty("config");
334     if (!configJsValue->IsObject()) {
335         return false;
336     }
337     auto configJsObject = JSRef<JSObject>::Cast(configJsValue);
338     if (typeValue == NG::ParticleType::IMAGE) {
339         auto srcJsValue = configJsObject->GetProperty("src");
340         auto sizeJsValue = configJsObject->GetProperty("size");
341         auto objectFitJsValue = configJsObject->GetProperty("objectFit");
342         NG::ImageParticleParameter imageParameter;
343         std::string src;
344         if (srcJsValue->IsString()) {
345             src = srcJsValue->ToString();
346         } else if (!JSParticle::ParseJsMedia(srcJsValue, src)) {
347             return false;
348         }
349         imageParameter.SetImageSource(src);
350         auto width = Dimension(0.0);
351         auto height = Dimension(0.0);
352         auto sizeValue = std::pair<Dimension, Dimension>(width, height);
353         ParsSize(sizeValue, sizeJsValue);
354         imageParameter.SetSize(sizeValue);
355         auto fit = ImageFit::COVER;
356         if (objectFitJsValue->IsNumber()) {
357             auto fitIntValue = objectFitJsValue->ToNumber<int32_t>();
358             if (fitIntValue >= static_cast<int32_t>(ImageFit::FILL) &&
359                 fitIntValue <= static_cast<int32_t>(ImageFit::SCALE_DOWN)) {
360                 fit = static_cast<ImageFit>(fitIntValue);
361             }
362         }
363         imageParameter.SetImageFit(fit);
364         NG::ParticleConfig particleConfig;
365         particleConfig.SetImageParticleParameter(imageParameter);
366         particle.SetConfig(particleConfig);
367     } else {
368         auto radiusJsValue = configJsObject->GetProperty("radius");
369         CalcDimension radius;
370         JSParticle::ParseJsDimensionVp(radiusJsValue, radius);
371         NG::PointParticleParameter pointParameter;
372         pointParameter.SetRadius(!radius.IsNonPositive() ? radius.ConvertToPx() : 0.0f);
373         NG::ParticleConfig particleConfig;
374         particleConfig.SetPointParticleParameter(pointParameter);
375         particle.SetConfig(particleConfig);
376     }
377 
378     auto count = 0;
379     auto countJsValue = particleJsObject->GetProperty("count");
380     if (countJsValue->IsNumber()) {
381         auto countIntValue = countJsValue->ToNumber<int32_t>();
382         if (countIntValue >= -1) {
383             count = countIntValue;
384         }
385     }
386     particle.SetCount(count);
387 
388     int64_t lifeTime = 1000;
389     auto lifeTimeJsValue = particleJsObject->GetProperty("lifetime");
390     if (lifeTimeJsValue->IsNumber()) {
391         auto lifeTimeIntValue = lifeTimeJsValue->ToNumber<int64_t>();
392         if (lifeTimeIntValue >= -1) {
393             lifeTime = lifeTimeIntValue;
394         }
395     }
396     particle.SetLifeTime(lifeTime);
397     int64_t lifeTimeRange = 0;
398     auto lifeTimeRangeJsValue = particleJsObject->GetProperty("lifetimeRange");
399     if (lifeTimeRangeJsValue->IsNumber()) {
400         auto lifeTimeRangeIntValue = lifeTimeRangeJsValue->ToNumber<int64_t>();
401         if (lifeTimeRangeIntValue >= 0) {
402             lifeTimeRange = lifeTimeRangeIntValue;
403         }
404     }
405     particle.SetLifeTimeRange(lifeTimeRange);
406     return true;
407 }
408 
ParseEmitterOption(JSRef<JSObject> & emitterJsObject,OHOS::Ace::NG::EmitterOption & emitterOption)409 bool ParseEmitterOption(JSRef<JSObject>& emitterJsObject, OHOS::Ace::NG::EmitterOption& emitterOption)
410 {
411     auto particleJsValue = emitterJsObject->GetProperty("particle");
412     if (!particleJsValue->IsObject()) {
413         return false;
414     }
415     auto particleJsObject = JSRef<JSObject>::Cast(particleJsValue);
416     OHOS::Ace::NG::Particle particle;
417     if (!ParseParticleObject(particleJsObject, particle)) {
418         return false;
419     }
420     emitterOption.SetParticle(particle);
421     int32_t emitRate = PARTICLE_DEFAULT_EMITTER_RATE;
422     auto emitRateJsValue = emitterJsObject->GetProperty("emitRate");
423     if (emitRateJsValue->IsNumber()) {
424         emitRate = emitRateJsValue->ToNumber<int32_t>() >= 0 ? emitRateJsValue->ToNumber<int32_t>() : emitRate;
425     }
426     emitterOption.SetEmitterRate(emitRate);
427 
428     auto emitShape = OHOS::Ace::NG::ParticleEmitterShape::RECTANGLE;
429     auto emitShapeJsValue = emitterJsObject->GetProperty("shape");
430     if (emitShapeJsValue->IsNumber()) {
431         auto emitShapeInt = emitShapeJsValue->ToNumber<int32_t>();
432         if (emitShapeInt >= static_cast<int32_t>(OHOS::Ace::NG::ParticleEmitterShape::RECTANGLE) &&
433             emitShapeInt <= static_cast<int32_t>(OHOS::Ace::NG::ParticleEmitterShape::ELLIPSE)) {
434             emitShape = static_cast<OHOS::Ace::NG::ParticleEmitterShape>(emitShapeInt);
435         }
436     }
437     emitterOption.SetShape(emitShape);
438     auto positionJsValue = emitterJsObject->GetProperty("position");
439     CalcDimension xValue(0.0);
440     CalcDimension yValue(0.0);
441     if (positionJsValue->IsArray()) {
442         auto positionJsArray = JSRef<JSArray>::Cast(positionJsValue);
443         if (positionJsArray->Length() == ARRAY_SIZE) {
444             JSParticle::ParseJsDimensionVp(positionJsArray->GetValueAt(0), xValue);
445             JSParticle::ParseJsDimensionVp(positionJsArray->GetValueAt(1), yValue);
446         }
447     }
448     auto positionValue = std::pair<Dimension, Dimension>(xValue, yValue);
449     emitterOption.SetPosition(positionValue);
450 
451     auto width = Dimension(1.0, DimensionUnit::PERCENT);
452     auto height = Dimension(1.0, DimensionUnit::PERCENT);
453     auto sizeValue = std::pair<Dimension, Dimension>(width, height);
454     auto sizeJsValue = emitterJsObject->GetProperty("size");
455     ParsSize(sizeValue, sizeJsValue);
456     emitterOption.SetSize(sizeValue);
457     return true;
458 }
459 
ParseAnimationColorArray(JSRef<JSArray> & curveConfigJsArray,std::list<NG::ParticlePropertyAnimation<Color>> & particleAnimationColorArray)460 void ParseAnimationColorArray(
461     JSRef<JSArray>& curveConfigJsArray, std::list<NG::ParticlePropertyAnimation<Color>>& particleAnimationColorArray)
462 {
463     auto arraySize = static_cast<int32_t>(curveConfigJsArray->Length());
464     for (int i = 0; i < arraySize; i++) {
465         auto arrayItemJsValue = curveConfigJsArray->GetValueAt(i);
466         NG::ParticlePropertyAnimation<Color> colorPropertyAnimation;
467         if (!arrayItemJsValue->IsObject()) {
468             continue;
469         }
470         auto arrayItemJsObject = JSRef<JSObject>::Cast(arrayItemJsValue);
471         auto fromJsValue = arrayItemJsObject->GetProperty("from");
472         Color from(DEFAULT_COLOR);
473         JSParticle::ParseJsColor(fromJsValue, from);
474         colorPropertyAnimation.SetFrom(from);
475         auto toJsValue = arrayItemJsObject->GetProperty("to");
476         Color to(DEFAULT_COLOR);
477         JSParticle::ParseJsColor(toJsValue, to);
478         colorPropertyAnimation.SetTo(to);
479         auto startMillisJsValue = arrayItemJsObject->GetProperty("startMillis");
480         auto startMillis = static_cast<int32_t>(0);
481         if (!JSParticle::ParseJsInt32(startMillisJsValue, startMillis) || startMillis < 0) {
482             startMillis = 0;
483         }
484         colorPropertyAnimation.SetStartMills(startMillis);
485         auto endMillisJsValue = arrayItemJsObject->GetProperty("endMillis");
486         auto endMillis = static_cast<int32_t>(0);
487         if (!JSParticle::ParseJsInt32(endMillisJsValue, endMillis) || endMillis < 0) {
488             endMillis = 0;
489         }
490         colorPropertyAnimation.SetEndMills(endMillis);
491         auto curveJsValue = arrayItemJsObject->GetProperty("curve");
492         auto curve = ParseCurve(curveJsValue);
493         if (curve) {
494             colorPropertyAnimation.SetCurve(curve);
495         }
496         particleAnimationColorArray.emplace_back(colorPropertyAnimation);
497     }
498 }
499 
ParseColorRandomUpdater(JSRef<JSVal> configJsValue,OHOS::Ace::NG::ParticleColorPropertyUpdater & updater)500 void ParseColorRandomUpdater(JSRef<JSVal> configJsValue, OHOS::Ace::NG::ParticleColorPropertyUpdater& updater)
501 {
502     NG::ParticleColorPropertyUpdaterConfig randomUpdaterConfig;
503     NG::ColorParticleRandomUpdateConfig colorRandomConfig;
504     if (!configJsValue->IsObject()) {
505         auto defaultPair = std::pair<float, float>(0.0f, 0.0f);
506         colorRandomConfig.SetRedRandom(defaultPair);
507         colorRandomConfig.SetGreenRandom(defaultPair);
508         colorRandomConfig.SetBlueRandom(defaultPair);
509         colorRandomConfig.SetAlphaRandom(defaultPair);
510         randomUpdaterConfig.SetRandomConfig(colorRandomConfig);
511         updater.SetConfig(randomUpdaterConfig);
512         return;
513     }
514     auto randomConfigJsObject = JSRef<JSObject>::Cast(configJsValue);
515     auto rJsValue = randomConfigJsObject->GetProperty("r");
516     auto gJsValue = randomConfigJsObject->GetProperty("g");
517     auto bJsValue = randomConfigJsObject->GetProperty("b");
518     auto aJsValue = randomConfigJsObject->GetProperty("a");
519     std::pair<float, float> defaultPair(0.0f, 0.0f);
520     auto rRangeValue = ParseParticleRange(rJsValue, 0.0f);
521     auto gRangeValue = ParseParticleRange(gJsValue, 0.0f);
522     auto bRangeValue = ParseParticleRange(bJsValue, 0.0f);
523     auto aRangeValue = ParseParticleRange(aJsValue, 0.0f);
524     colorRandomConfig.SetRedRandom(rRangeValue.value_or(defaultPair));
525     colorRandomConfig.SetGreenRandom(gRangeValue.value_or(defaultPair));
526     colorRandomConfig.SetBlueRandom(bRangeValue.value_or(defaultPair));
527     colorRandomConfig.SetAlphaRandom(aRangeValue.value_or(defaultPair));
528     randomUpdaterConfig.SetRandomConfig(colorRandomConfig);
529     updater.SetConfig(randomUpdaterConfig);
530 }
531 
ParseColorCurveUpdater(JSRef<JSVal> configJsValue,OHOS::Ace::NG::ParticleColorPropertyUpdater & updater)532 void ParseColorCurveUpdater(JSRef<JSVal> configJsValue, OHOS::Ace::NG::ParticleColorPropertyUpdater& updater)
533 {
534     std::list<NG::ParticlePropertyAnimation<Color>> particleAnimationColorArray;
535     NG::ParticleColorPropertyUpdaterConfig randomUpdaterConfig;
536     if (!configJsValue->IsArray()) {
537         randomUpdaterConfig.SetAnimationArray(particleAnimationColorArray);
538         updater.SetConfig(randomUpdaterConfig);
539         return;
540     }
541     auto curveConfigJsArray = JSRef<JSArray>::Cast(configJsValue);
542     ParseAnimationColorArray(curveConfigJsArray, particleAnimationColorArray);
543     randomUpdaterConfig.SetAnimationArray(particleAnimationColorArray);
544     updater.SetConfig(randomUpdaterConfig);
545 }
546 
ParseColorUpdater(JSRef<JSObject> & updaterJsObject,OHOS::Ace::NG::ParticleColorPropertyUpdater & updater)547 bool ParseColorUpdater(JSRef<JSObject>& updaterJsObject, OHOS::Ace::NG::ParticleColorPropertyUpdater& updater)
548 {
549     auto typeJsValue = updaterJsObject->GetProperty("type");
550     if (typeJsValue->IsNumber()) {
551         auto typeIntValue = typeJsValue->ToNumber<int32_t>();
552         if (typeIntValue < NG::UpdaterType::NONE_UPDATER || typeIntValue > NG::UpdaterType::CURVE) {
553             typeIntValue = NG::UpdaterType::NONE_UPDATER;
554         }
555         auto type = static_cast<NG::UpdaterType>(typeIntValue);
556         updater.SetUpdateType(type);
557         auto configJsValue = updaterJsObject->GetProperty("config");
558         if (type == NG::UpdaterType::RANDOM) {
559             ParseColorRandomUpdater(configJsValue, updater);
560             return true;
561         } else if (type == NG::UpdaterType::CURVE) {
562             ParseColorCurveUpdater(configJsValue, updater);
563             return true;
564         }
565     }
566     return false;
567 }
568 
ParseColorInitRange(JSRef<JSVal> colorRangeJsValue,OHOS::Ace::NG::ParticleColorPropertyOption & colorOption)569 void ParseColorInitRange(JSRef<JSVal> colorRangeJsValue, OHOS::Ace::NG::ParticleColorPropertyOption& colorOption)
570 {
571     Color fromColor(DEFAULT_COLOR);
572     Color toColor(DEFAULT_COLOR);
573     auto defaultRange = std::pair<Color, Color>(fromColor, toColor);
574     if (!colorRangeJsValue->IsArray()) {
575         colorOption.SetRange(defaultRange);
576         return;
577     }
578     auto colorRangeJsArray = JSRef<JSArray>::Cast(colorRangeJsValue);
579     if (static_cast<int32_t>(colorRangeJsArray->Length()) != ARRAY_SIZE) {
580         colorOption.SetRange(defaultRange);
581         return;
582     }
583     JSParticle::ParseJsColor(colorRangeJsArray->GetValueAt(0), fromColor);
584     JSParticle::ParseJsColor(colorRangeJsArray->GetValueAt(1), toColor);
585     auto range = std::pair<Color, Color>(fromColor, toColor);
586     colorOption.SetRange(range);
587 }
588 
ParseColorOption(JSRef<JSObject> & colorJsObject,OHOS::Ace::NG::ParticleColorPropertyOption & colorOption)589 void ParseColorOption(JSRef<JSObject>& colorJsObject, OHOS::Ace::NG::ParticleColorPropertyOption& colorOption)
590 {
591     auto colorRangeJsValue = colorJsObject->GetProperty("range");
592     ParseColorInitRange(colorRangeJsValue, colorOption);
593 
594     auto colorDist = OHOS::Ace::NG::DistributionType::UNIFORM;
595     auto colorDistJsValue = colorJsObject->GetProperty("distributionType");
596     if (colorDistJsValue->IsNumber()) {
597         auto colorDistInt = colorDistJsValue->ToNumber<int32_t>();
598         if (colorDistInt >= static_cast<int32_t>(OHOS::Ace::NG::DistributionType::UNIFORM) &&
599             colorDistInt <= static_cast<int32_t>(OHOS::Ace::NG::DistributionType::GAUSSIAN)) {
600             colorDist = static_cast<OHOS::Ace::NG::DistributionType>(colorDistInt);
601         }
602     }
603     colorOption.SetDistribution(colorDist);
604 
605     auto updaterJsValue = colorJsObject->GetProperty("updater");
606     NG::ParticleColorPropertyUpdater updater;
607     if (updaterJsValue->IsObject()) {
608         auto updaterJsObject = JSRef<JSObject>::Cast(updaterJsValue);
609         if (ParseColorUpdater(updaterJsObject, updater)) {
610             colorOption.SetUpdater(updater);
611             return;
612         }
613     }
614     updater.SetUpdateType(NG::UpdaterType::NONE_UPDATER);
615     NG::ParticleColorPropertyUpdaterConfig noneUpdaterConfig;
616     noneUpdaterConfig.SetInValid(0);
617     updater.SetConfig(noneUpdaterConfig);
618     colorOption.SetUpdater(updater);
619 }
620 
ParseParticleVelocity(JSRef<JSVal> jsValue,OHOS::Ace::NG::VelocityProperty & velocity)621 void ParseParticleVelocity(JSRef<JSVal> jsValue, OHOS::Ace::NG::VelocityProperty& velocity)
622 {
623     auto defaultPair = std::pair<float, float>(0.0f, 0.0f);
624     if (!jsValue->IsObject()) {
625         velocity.SetSpeedRange(defaultPair);
626         velocity.SetAngleRange(defaultPair);
627         return;
628     }
629     auto jsValueObj = JSRef<JSObject>::Cast(jsValue);
630     auto speedJsValue = jsValueObj->GetProperty("speed");
631     auto angleJsValue = jsValueObj->GetProperty("angle");
632     auto speedPair = ParseParticleRange(speedJsValue, 0.0f);
633     if (speedPair.has_value()) {
634         velocity.SetSpeedRange(speedPair.value());
635     } else {
636         velocity.SetSpeedRange(defaultPair);
637     }
638     auto anglePair = ParseParticleRange(angleJsValue, 0.0f);
639     if (anglePair.has_value()) {
640         velocity.SetAngleRange(anglePair.value());
641     } else {
642         velocity.SetAngleRange(defaultPair);
643     }
644 }
645 
ParseParticleAcceleration(JSRef<JSVal> jsValue,OHOS::Ace::NG::AccelerationProperty & acceleration)646 void ParseParticleAcceleration(JSRef<JSVal> jsValue, OHOS::Ace::NG::AccelerationProperty& acceleration)
647 {
648     if (!jsValue->IsObject()) {
649         return;
650     }
651     auto jsValueObj = JSRef<JSObject>::Cast(jsValue);
652     auto speedValue = jsValueObj->GetProperty("speed");
653     auto alphaValue = jsValueObj->GetProperty("angle");
654     OHOS::Ace::NG::ParticleFloatPropertyOption speedOption;
655     if (speedValue->IsObject()) {
656         auto speedObject = JSRef<JSObject>::Cast(speedValue);
657         ParseFloatOption(speedObject, speedOption, DEFAULT_SPEED, MIN_SPEED, MAX_SPEED);
658         acceleration.SetSpeed(speedOption);
659     }
660     OHOS::Ace::NG::ParticleFloatPropertyOption angleOption;
661     if (alphaValue->IsObject()) {
662         auto alphaObject = JSRef<JSObject>::Cast(alphaValue);
663         ParseFloatOption(alphaObject, angleOption, DEFAULT_ANGLE, MIN_ANGLE, MAX_ANGLE);
664         acceleration.SetAngle(angleOption);
665     }
666 }
667 
ParseParticleOption(JSRef<JSObject> & particleJsObj,OHOS::Ace::NG::ParticleOption & particleOption)668 bool ParseParticleOption(JSRef<JSObject>& particleJsObj, OHOS::Ace::NG::ParticleOption& particleOption)
669 {
670     auto emitterJsValue = particleJsObj->GetProperty("emitter");
671     if (!emitterJsValue->IsObject()) {
672         return false;
673     }
674 
675     auto emitterJsObj = JSRef<JSObject>::Cast(emitterJsValue);
676     OHOS::Ace::NG::EmitterOption emitterOption;
677     if (!ParseEmitterOption(emitterJsObj, emitterOption)) {
678         return false;
679     }
680 
681     particleOption.SetEmitterOption(emitterOption);
682     auto colorJsValue = particleJsObj->GetProperty("color");
683     if (colorJsValue->IsObject()) {
684         auto colorJsObj = JSRef<JSObject>::Cast(colorJsValue);
685         OHOS::Ace::NG::ParticleColorPropertyOption colorOption;
686         ParseColorOption(colorJsObj, colorOption);
687         particleOption.SetParticleColorOption(colorOption);
688     }
689 
690     auto opacityJsValue = particleJsObj->GetProperty("opacity");
691     if (opacityJsValue->IsObject()) {
692         auto opacityJsObj = JSRef<JSObject>::Cast(opacityJsValue);
693         OHOS::Ace::NG::ParticleFloatPropertyOption opacityOption;
694         ParseFloatOption(opacityJsObj, opacityOption, DEFAULT_OPACITY, MIN_OPACITY, MAX_OPACITY);
695         particleOption.SetParticleOpacityOption(opacityOption);
696     }
697 
698     auto scaleJsValue = particleJsObj->GetProperty("scale");
699     if (scaleJsValue->IsObject()) {
700         auto scaleJsObj = JSRef<JSObject>::Cast(scaleJsValue);
701         OHOS::Ace::NG::ParticleFloatPropertyOption scaleOption;
702         ParseFloatOption(scaleJsObj, scaleOption, DEFAULT_SCALE, MIN_SCALE, MAX_SCALE);
703         particleOption.SetParticleScaleOption(scaleOption);
704     }
705 
706     auto velocityJsValue = particleJsObj->GetProperty("velocity");
707     OHOS::Ace::NG::VelocityProperty velocity;
708     ParseParticleVelocity(velocityJsValue, velocity);
709     particleOption.SetParticleVelocityOption(velocity);
710 
711     auto accelerationJsValue = particleJsObj->GetProperty("acceleration");
712     OHOS::Ace::NG::AccelerationProperty acceleration;
713     ParseParticleAcceleration(accelerationJsValue, acceleration);
714     particleOption.SetParticleAccelerationOption(acceleration);
715 
716     auto spinJsValue = particleJsObj->GetProperty("spin");
717     if (spinJsValue->IsObject()) {
718         auto spinJsObj = JSRef<JSObject>::Cast(spinJsValue);
719         OHOS::Ace::NG::ParticleFloatPropertyOption spinOption;
720         ParseFloatOption(spinJsObj, spinOption, DEFAULT_SPIN, MIN_SPIN, MAX_SPIN);
721         particleOption.SetParticleSpinOption(spinOption);
722     }
723     return true;
724 }
725 
ParseParticleArray(JSRef<JSArray> & paramArray,std::list<OHOS::Ace::NG::ParticleOption> & arrayValue)726 void ParseParticleArray(JSRef<JSArray>& paramArray, std::list<OHOS::Ace::NG::ParticleOption>& arrayValue)
727 {
728     for (size_t i = 0; i < paramArray->Length(); i++) {
729         if (!paramArray->GetValueAt(i)->IsObject()) {
730             continue;
731         }
732         OHOS::Ace::NG::ParticleOption option;
733         auto particleJsObj = JSRef<JSObject>::Cast(paramArray->GetValueAt(i));
734         if (!ParseParticleOption(particleJsObj, option)) {
735             continue;
736         }
737         arrayValue.emplace_back(option);
738     }
739 }
740 } // namespace
Create(const JSCallbackInfo & args)741 void JSParticle::Create(const JSCallbackInfo& args)
742 {
743     if (args.Length() < 1) {
744         return;
745     }
746     std::list<OHOS::Ace::NG::ParticleOption> arrayValue;
747     if (args[0]->IsObject()) {
748         JSRef<JSObject> paramObj = JSRef<JSObject>::Cast(args[0]);
749         auto particlesJsArray = paramObj->GetProperty("particles");
750         if (particlesJsArray->IsArray()) {
751             auto paramArray = JSRef<JSArray>::Cast(particlesJsArray);
752             ParseParticleArray(paramArray, arrayValue);
753         }
754     }
755     ParticleModel::GetInstance()->Create(arrayValue);
756 }
AddDisturbance(std::vector<OHOS::Ace::ParticleDisturbance> & dataArray,const JSRef<JSObject> & paramObj)757 void JSParticle::AddDisturbance(
758     std::vector<OHOS::Ace::ParticleDisturbance>& dataArray, const JSRef<JSObject>& paramObj)
759 {
760     float strength = paramObj->GetProperty("strength")->ToNumber<float>();
761     int shape = paramObj->GetProperty("shape")->ToNumber<int>();
762     int sizeXValue = 0;
763     int sizeYValue = 0;
764     int positionXValue = 0;
765     int positionYValue = 0;
766     GetSizeAndPositionValues(paramObj, sizeXValue, sizeYValue, positionXValue, positionYValue);
767     int feather = paramObj->GetProperty("feather")->ToNumber<int>();
768     feather = std::clamp(feather, 0, 100);
769     float noiseScale = 1.0f;
770     if (paramObj->GetProperty("noiseScale")->IsNumber()) {
771         noiseScale = paramObj->GetProperty("noiseScale")->ToNumber<float>();
772         if (noiseScale < 0.0f) {
773             noiseScale = 1.0f;
774         }
775     }
776     float noiseFrequency = 1.0f;
777     if (paramObj->GetProperty("noiseFrequency")->IsNumber()) {
778         noiseFrequency = paramObj->GetProperty("noiseFrequency")->ToNumber<float>();
779         if (noiseFrequency < 0.0f) {
780             noiseFrequency = 1.0f;
781         }
782     }
783     float noiseAmplitude = 1.0f;
784     if (paramObj->GetProperty("noiseAmplitude")->IsNumber()) {
785         noiseAmplitude = paramObj->GetProperty("noiseAmplitude")->ToNumber<float>();
786         if (noiseAmplitude < 0.0f) {
787             noiseAmplitude = 1.0f;
788         }
789     }
790     ParticleDisturbance disturbanceField;
791     disturbanceField.strength = strength;
792     disturbanceField.shape = static_cast<ParticleDisturbanceShapeType>(shape);
793     disturbanceField.size[0] = sizeXValue;
794     disturbanceField.size[1] = sizeYValue;
795     disturbanceField.position[0] = positionXValue;
796     disturbanceField.position[1] = positionYValue;
797     disturbanceField.feather = feather;
798     disturbanceField.noiseScale = noiseScale;
799     disturbanceField.noiseFrequency = noiseFrequency;
800     disturbanceField.noiseAmplitude = noiseAmplitude;
801     dataArray.push_back(disturbanceField);
802 }
803 
GetSizeAndPositionValues(const JSRef<JSObject> & paramObj,int & sizeXValue,int & sizeYValue,int & positionXValue,int & positionYValue)804 void JSParticle::GetSizeAndPositionValues(
805     const JSRef<JSObject>& paramObj, int& sizeXValue, int& sizeYValue, int& positionXValue, int& positionYValue)
806 {
807     JSRef<JSVal> sizeJsValue = paramObj->GetProperty("size");
808     if (sizeJsValue->IsObject()) {
809         JSRef<JSObject> sizeJsObject = JSRef<JSObject>::Cast(sizeJsValue);
810         sizeXValue = sizeJsObject->GetProperty("width")->ToNumber<int>();
811         sizeYValue = sizeJsObject->GetProperty("height")->ToNumber<int>();
812     }
813 
814     JSRef<JSVal> positionJsValue = paramObj->GetProperty("position");
815     if (positionJsValue->IsObject()) {
816         JSRef<JSObject> positionJsObject = JSRef<JSObject>::Cast(positionJsValue);
817         positionXValue = positionJsObject->GetProperty("x")->ToNumber<int>();
818         positionYValue = positionJsObject->GetProperty("y")->ToNumber<int>();
819     }
820 }
821 
JsDisturbanceFields(const JSCallbackInfo & args)822 void JSParticle::JsDisturbanceFields(const JSCallbackInfo& args)
823 {
824     if (args.Length() != 1 || !args[0]->IsArray()) {
825         return;
826     }
827     std::vector<ParticleDisturbance> dataArray;
828     JSRef<JSArray> dataJsArray = JSRef<JSArray>::Cast(args[0]);
829     for (size_t i = 0; i < dataJsArray->Length(); i++) {
830         auto jsObject = JSRef<JSObject>::Cast(dataJsArray->GetValueAt(i));
831         AddDisturbance(dataArray, jsObject);
832     }
833 
834     ParticleModel::GetInstance()->DisturbanceField(dataArray);
835 }
836 
ParseEmitterProperty(std::vector<OHOS::Ace::EmitterProperty> & data,const JSRef<JSObject> & paramObj)837 void JSParticle::ParseEmitterProperty(
838     std::vector<OHOS::Ace::EmitterProperty>& data, const JSRef<JSObject>& paramObj)
839 {
840     EmitterProperty emitterProperty;
841     uint32_t index = 0u;
842     if (paramObj->GetProperty("index")->IsNumber()) {
843         uint32_t indexJsValue = paramObj->GetProperty("index")->ToNumber<uint32_t>();
844         index = indexJsValue > 0 ? indexJsValue : 0;
845     }
846     emitterProperty.index = index;
847 
848     auto emitRateProperty = paramObj->GetProperty("emitRate");
849     if (emitRateProperty->IsNumber()) {
850         auto emitRateValue = emitRateProperty->ToNumber<int32_t>();
851         emitterProperty.emitRate = emitRateValue >= 0 ? emitRateValue : PARTICLE_DEFAULT_EMITTER_RATE;
852     }
853     auto positionProperty = paramObj->GetProperty("position");
854     if (positionProperty->IsObject()) {
855         auto positionValue = Framework::JSRef<Framework::JSObject>::Cast(positionProperty);
856         if (positionValue->GetProperty("x")->IsNumber() && positionValue->GetProperty("y")->IsNumber()) {
857             auto positionXValue = positionValue->GetProperty("x")->ToNumber<float>();
858             auto positonYValue = positionValue->GetProperty("y")->ToNumber<float>();
859             emitterProperty.position = { positionXValue, positonYValue };
860         }
861     }
862     auto sizeProperty = paramObj->GetProperty("size");
863     if (sizeProperty->IsObject()) {
864         auto sizeValue = Framework::JSRef<Framework::JSObject>::Cast(sizeProperty);
865         auto sizeXValue = sizeValue->GetProperty("width")->ToNumber<float>();
866         auto sizeYValue = sizeValue->GetProperty("height")->ToNumber<float>();
867         if (sizeXValue > 0 && sizeYValue > 0) {
868             emitterProperty.size = { sizeXValue, sizeYValue };
869         }
870     }
871     data.push_back(emitterProperty);
872 }
873 
JsEmitter(const JSCallbackInfo & args)874 void JSParticle::JsEmitter(const JSCallbackInfo& args)
875 {
876     if (args.Length() != 1 || !args[0]->IsArray()) {
877         return;
878     }
879     std::vector<EmitterProperty> dataArray;
880     JSRef<JSArray> dataJsArray = JSRef<JSArray>::Cast(args[0]);
881     size_t length = dataJsArray->Length();
882     for (size_t i = 0; i < length; i++) {
883         if (dataJsArray->GetValueAt(i)->IsObject()) {
884             auto jsObject = JSRef<JSObject>::Cast(dataJsArray->GetValueAt(i));
885             ParseEmitterProperty(dataArray, jsObject);
886         }
887     }
888     ParticleModel::GetInstance()->updateEmitter(dataArray);
889 }
890 
JSBind(BindingTarget globalObj)891 void JSParticle::JSBind(BindingTarget globalObj)
892 {
893     JSClass<JSParticle>::Declare("Particle");
894     JSClass<JSParticle>::StaticMethod("create", &JSParticle::Create);
895     JSClass<JSParticle>::StaticMethod("disturbanceFields", &JSParticle::JsDisturbanceFields);
896     JSClass<JSParticle>::StaticMethod("emitter", &JSParticle::JsEmitter);
897     JSClass<JSParticle>::InheritAndBind<JSViewAbstract>(globalObj);
898 }
899 } // namespace OHOS::Ace::Framework
900