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