diff --git a/apps/fabric-example/ios/Podfile.lock b/apps/fabric-example/ios/Podfile.lock index 0633af6d8a80..f29f7e321633 100644 --- a/apps/fabric-example/ios/Podfile.lock +++ b/apps/fabric-example/ios/Podfile.lock @@ -2456,7 +2456,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: FBLazyVector: 4815a9b6c97de1f17c88b3cf7047fc0aa0018fb8 - hermes-engine: 371db9921d7c37c3b2a8d04b8cbe31156f0a277a + hermes-engine: 8b25cc623de93c12f190eb2db7a4f5d9ab813d2d MMKVCore: f2dd4c9befea04277a55e84e7812f930537993df NitroMmkv: ca0b67493c62d3f2b29457569ee48d24a1f7afd5 NitroModules: 42fb1740701d25672d7531652987eca15bbb181e @@ -2468,7 +2468,7 @@ SPEC CHECKSUMS: React: ce3b89b04d579b21ba990ff07a1d4cf0a68588dd React-callinvoker: 30be38ed761abcb8ae7edaa39e34cb9aade4b33b React-Core: 59c718ce09b1b83d4f07c0112b9c5f99ea39ee94 - React-Core-prebuilt: a76ea4e98bff53e6d1d86863e110eb50efc86fa8 + React-Core-prebuilt: 97958473b5725670045e4f7929d96ecfae31a367 React-CoreModules: bcb9314b39eaa9134487be0cb8c8ee4a6d2e5cd9 React-cxxreact: fee24034f430cfbcbedced4157eacd2be7ced010 React-debug: 5138aa953fe4a9db1690681de71577dd3b4f05b1 @@ -2530,7 +2530,7 @@ SPEC CHECKSUMS: ReactAppDependencyProvider: dd8001caba9cb3fbe49a61d234262eaaa3d67aee ReactCodegen: 3e81858c6242de4bbde48ee222523fe45c41a21a ReactCommon: 2f76f91c78f14d4a2c3bc841842a6145d3e99038 - ReactNativeDependencies: 1cc786bff619de11c58e19290cbdf3a38d0cadc7 + ReactNativeDependencies: d3786998f83c8bc5c95d1796dedf158532d28851 RNCAsyncStorage: 2ad919e88b8bc2cd80e8697ce66d04d006743283 RNCClipboard: 715fa7c6c8366f17d00f05a439ee7488f390fa5f RNCMaskedView: eb2b2e538afa907f05a5848a1a1ac26092e6fec9 diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/core/CSSAnimation.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/core/CSSAnimation.cpp index 8c3ddb74900e..616a67ebf554 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/core/CSSAnimation.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/core/CSSAnimation.cpp @@ -46,6 +46,22 @@ AnimationProgressState CSSAnimation::getState(double timestamp) const { return progressProvider_->getState(timestamp); } +unsigned CSSAnimation::getCurrentIteration() const { + return progressProvider_->getCurrentIteration(); +} + +double CSSAnimation::getDuration() const { + return progressProvider_->getDuration(); +} + +double CSSAnimation::getDelay() const { + return progressProvider_->getDelay(); +} + +double CSSAnimation::getIterationCount() const { + return progressProvider_->getIterationCount(); +} + bool CSSAnimation::isReversed() const { const auto direction = progressProvider_->getDirection(); return direction == AnimationDirection::Reverse || direction == AnimationDirection::AlternateReverse; diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/core/CSSAnimation.h b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/core/CSSAnimation.h index 0aa33fd601a7..04fd44a23b87 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/core/CSSAnimation.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/core/CSSAnimation.h @@ -26,6 +26,10 @@ class CSSAnimation { double getStartTimestamp(double timestamp) const; AnimationProgressState getState(double timestamp) const; + unsigned getCurrentIteration() const; + double getDuration() const; + double getDelay() const; + double getIterationCount() const; bool isReversed() const; bool hasForwardsFillMode() const; diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/events/CSSAnimationEvent.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/events/CSSAnimationEvent.cpp new file mode 100644 index 000000000000..5b90582f9283 --- /dev/null +++ b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/events/CSSAnimationEvent.cpp @@ -0,0 +1,27 @@ +#include +#include + +#include + +namespace reanimated::css { + +CSSEvent createAnimationStartEvent(const facebook::react::Tag viewTag, const std::shared_ptr &animation) { + const auto activeDuration = animation->getDuration() * animation->getIterationCount(); + const auto elapsedTime = std::min(std::max(-animation->getDelay(), 0.0), activeDuration); + return {viewTag, "animationstart", animation->getName(), elapsedTime}; +} + +CSSEvent createAnimationIterationEvent( + const facebook::react::Tag viewTag, + const std::shared_ptr &animation, + const unsigned iteration) { + const auto elapsedTime = static_cast(iteration - 1) * animation->getDuration(); + return {viewTag, "animationiteration", animation->getName(), elapsedTime}; +} + +CSSEvent createAnimationEndEvent(const facebook::react::Tag viewTag, const std::shared_ptr &animation) { + const auto elapsedTime = animation->getDuration() * animation->getIterationCount(); + return {viewTag, "animationend", animation->getName(), elapsedTime}; +} + +} // namespace reanimated::css diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/events/CSSAnimationEvent.h b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/events/CSSAnimationEvent.h new file mode 100644 index 000000000000..1295db3b501e --- /dev/null +++ b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/events/CSSAnimationEvent.h @@ -0,0 +1,33 @@ +#pragma once + +#include + +#include +#include + +namespace reanimated::css { + +class CSSAnimation; + +enum class CSSAnimationEventType : std::uint8_t { + AnimationStart = 1 << 0, + AnimationEnd = 1 << 1, + AnimationIteration = 1 << 2 +}; + +// Bitmask of CSSAnimationEventType values representing which events a view +// is listening for. A value of 0 means no listeners are registered. +using CSSAnimationEventListeners = std::uint8_t; + +inline bool hasListener(CSSAnimationEventListeners listeners, CSSAnimationEventType type) { + return (listeners & static_cast(type)) != 0; +} + +CSSEvent createAnimationStartEvent(facebook::react::Tag viewTag, const std::shared_ptr &animation); +CSSEvent createAnimationIterationEvent( + facebook::react::Tag viewTag, + const std::shared_ptr &animation, + unsigned iteration); +CSSEvent createAnimationEndEvent(facebook::react::Tag viewTag, const std::shared_ptr &animation); + +} // namespace reanimated::css diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/events/CSSEvent.h b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/events/CSSEvent.h new file mode 100644 index 000000000000..56e7c338261e --- /dev/null +++ b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/events/CSSEvent.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +#include + +namespace reanimated::css { + +struct CSSEvent { + facebook::react::Tag viewTag; + std::string type; // e.g. "animationstart", "transitionend" + std::string targetName; // animation name or property name + double elapsedTime; // in milliseconds (convert to seconds at dispatch time) +}; + +} // namespace reanimated::css diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/events/CSSEventsEmitter.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/events/CSSEventsEmitter.cpp new file mode 100644 index 000000000000..c442250ee4c6 --- /dev/null +++ b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/events/CSSEventsEmitter.cpp @@ -0,0 +1,58 @@ +#include + +#include + +using namespace facebook; + +namespace reanimated::css { + +namespace { + +jsi::Object eventToJSIObject(jsi::Runtime &rt, const CSSEvent &event) { + auto obj = jsi::Object(rt); + obj.setProperty(rt, "viewTag", event.viewTag); + obj.setProperty(rt, "type", jsi::String::createFromUtf8(rt, event.type)); + obj.setProperty(rt, "animationName", jsi::String::createFromUtf8(rt, event.targetName)); + obj.setProperty(rt, "elapsedTime", event.elapsedTime); + return obj; +} + +jsi::Array eventsToJSIArray(jsi::Runtime &rt, const std::vector &events) { + auto array = jsi::Array(rt, events.size()); + for (size_t i = 0; i < events.size(); ++i) { + array.setValueAtIndex(rt, i, eventToJSIObject(rt, events[i])); + } + return array; +} + +} // namespace + +CSSEventsEmitter::CSSEventsEmitter(const std::shared_ptr &jsInvoker) : jsInvoker_(jsInvoker) {} + +void CSSEventsEmitter::setEmitFunction(std::shared_ptr emitFunction) { + emitFunction_ = std::move(emitFunction); +} + +void CSSEventsEmitter::schedule(CSSEvent event) { + pendingEvents_.emplace_back(std::move(event)); +} + +void CSSEventsEmitter::emit() { + if (pendingEvents_.empty() || !emitFunction_) { + return; + } + + auto events = std::make_shared>(); + events->swap(pendingEvents_); + auto emitFunction = emitFunction_; + + jsInvoker_->invokeAsync([events = std::move(events), emitFunction = std::move(emitFunction)](jsi::Runtime &rt) { + emitFunction->call(rt, eventsToJSIArray(rt, *events)); + }); +} + +bool CSSEventsEmitter::hasEvents() const { + return !pendingEvents_.empty(); +} + +} // namespace reanimated::css diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/events/CSSEventsEmitter.h b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/events/CSSEventsEmitter.h new file mode 100644 index 000000000000..0ae28e1dbe5f --- /dev/null +++ b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/events/CSSEventsEmitter.h @@ -0,0 +1,30 @@ +#pragma once + +#include + +#include +#include + +#include +#include + +using namespace facebook; + +namespace reanimated::css { + +class CSSEventsEmitter { + public: + explicit CSSEventsEmitter(const std::shared_ptr &jsInvoker); + + void setEmitFunction(std::shared_ptr emitFunction); + void schedule(CSSEvent event); + void emit(); + bool hasEvents() const; + + private: + const std::shared_ptr jsInvoker_; + std::shared_ptr emitFunction_; + std::vector pendingEvents_; +}; + +} // namespace reanimated::css diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/progress/AnimationProgressProvider.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/progress/AnimationProgressProvider.cpp index 622a233845a3..e7cfdac763dd 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/progress/AnimationProgressProvider.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/progress/AnimationProgressProvider.cpp @@ -35,6 +35,14 @@ AnimationDirection AnimationProgressProvider::getDirection() const { return direction_; } +unsigned AnimationProgressProvider::getCurrentIteration() const { + return currentIteration_; +} + +double AnimationProgressProvider::getIterationCount() const { + return iterationCount_; +} + double AnimationProgressProvider::getGlobalProgress() const { return applyAnimationDirection(rawProgress_.value_or(0)); } diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/progress/AnimationProgressProvider.h b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/progress/AnimationProgressProvider.h index 6999c7e59308..f658eb90acf1 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/progress/AnimationProgressProvider.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/progress/AnimationProgressProvider.h @@ -33,6 +33,8 @@ class AnimationProgressProvider final : public KeyframeProgressProvider, public void setEasingFunction(const EasingFunction &easingFunction); AnimationDirection getDirection() const; + unsigned getCurrentIteration() const; + double getIterationCount() const; double getGlobalProgress() const override; double getKeyframeProgress(double fromOffset, double toOffset) const override; AnimationProgressState getState(double timestamp) const; diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/progress/RawProgressProvider.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/progress/RawProgressProvider.cpp index 2ab6dd440f9d..2c4dd2ba61c8 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/progress/RawProgressProvider.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/progress/RawProgressProvider.cpp @@ -5,6 +5,14 @@ namespace reanimated::css { RawProgressProvider::RawProgressProvider(const double timestamp, const double duration, const double delay) : duration_(duration), delay_(delay), creationTimestamp_(timestamp) {} +double RawProgressProvider::getDuration() const { + return duration_; +} + +double RawProgressProvider::getDelay() const { + return delay_; +} + void RawProgressProvider::setDuration(double duration) { duration_ = duration; } diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/progress/RawProgressProvider.h b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/progress/RawProgressProvider.h index 9256aad3e8fe..5c65a0f9369b 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/progress/RawProgressProvider.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/progress/RawProgressProvider.h @@ -7,6 +7,8 @@ class RawProgressProvider { public: RawProgressProvider(double timestamp, double duration, double delay); + double getDuration() const; + double getDelay() const; void setDuration(double duration); void setDelay(double delay); diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/registries/CSSAnimationsRegistry.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/registries/CSSAnimationsRegistry.cpp index 3fb3deb14506..0c97b49e3fc0 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/registries/CSSAnimationsRegistry.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/registries/CSSAnimationsRegistry.cpp @@ -8,6 +8,9 @@ namespace reanimated::css { +CSSAnimationsRegistry::CSSAnimationsRegistry(const std::shared_ptr &eventsEmitter) + : eventsEmitter_(eventsEmitter) {} + bool CSSAnimationsRegistry::isEmpty() const { // The registry is empty if has no registered animations and no updates // stored in the updates registry @@ -24,10 +27,19 @@ void CSSAnimationsRegistry::apply( const std::optional> &animationNames, const CSSAnimationsMap &newAnimations, const CSSAnimationSettingsUpdatesMap &settingsUpdates, - double timestamp) { + const CSSAnimationEventListeners eventListeners, + const double timestamp) { auto animationsVector = buildAnimationsVector(rt, shadowNode, animationNames, newAnimations); const auto viewTag = shadowNode->getTag(); + + // Update event listener tracking based on current callback props + if (eventListeners != 0) { + eventListenersMap_[viewTag] = eventListeners; + } else { + eventListenersMap_.erase(viewTag); + } + if (animationsVector.empty()) { remove(viewTag); return; @@ -60,6 +72,7 @@ void CSSAnimationsRegistry::remove(const Tag viewTag) { removeViewAnimations(viewTag); removeFromUpdatesRegistry(viewTag); registry_.erase(viewTag); + eventListenersMap_.erase(viewTag); } void CSSAnimationsRegistry::update(const double timestamp) { @@ -175,12 +188,17 @@ void CSSAnimationsRegistry::updateViewAnimations( std::shared_ptr shadowNode = nullptr; bool hasUpdates = false; + const bool detectEvents = eventListenersMap_.count(viewTag) > 0; + for (const auto animationIndex : animationIndices) { const auto &animation = registry_[viewTag].animationsVector[animationIndex]; if (!shadowNode) { shadowNode = animation->getShadowNode(); } - if (animation->getState(timestamp) == AnimationProgressState::Pending) { + + const AnimationStateSnapshot beforeSnapshot = {animation->getState(timestamp), animation->getCurrentIteration()}; + + if (beforeSnapshot.state == AnimationProgressState::Pending) { animation->run(timestamp); } @@ -188,6 +206,11 @@ void CSSAnimationsRegistry::updateViewAnimations( const auto updates = animation->update(timestamp); const auto newState = animation->getState(timestamp); + if (detectEvents) { + const AnimationStateSnapshot afterSnapshot = {newState, animation->getCurrentIteration()}; + scheduleAnimationEvents(viewTag, animation, beforeSnapshot, afterSnapshot); + } + if (newState == AnimationProgressState::Finished) { // Revert changes applied during animation if there is no forwards fill // mode @@ -318,6 +341,29 @@ void CSSAnimationsRegistry::handleAnimationsToRevert(const double timestamp) { animationsToRevertMap_.clear(); } +void CSSAnimationsRegistry::scheduleAnimationEvents( + const Tag viewTag, + const std::shared_ptr &animation, + const AnimationStateSnapshot &before, + const AnimationStateSnapshot &after) { + const auto listeners = eventListenersMap_.at(viewTag); + + if (hasListener(listeners, CSSAnimationEventType::AnimationStart) && + before.state == AnimationProgressState::Pending && after.state != AnimationProgressState::Pending) { + eventsEmitter_->schedule(createAnimationStartEvent(viewTag, animation)); + } + + if (hasListener(listeners, CSSAnimationEventType::AnimationIteration) && + after.state == AnimationProgressState::Running && after.iteration > before.iteration) { + eventsEmitter_->schedule(createAnimationIterationEvent(viewTag, animation, after.iteration)); + } + + if (hasListener(listeners, CSSAnimationEventType::AnimationEnd) && before.state != AnimationProgressState::Finished && + after.state == AnimationProgressState::Finished) { + eventsEmitter_->schedule(createAnimationEndEvent(viewTag, animation)); + } +} + bool CSSAnimationsRegistry::addStyleUpdates( folly::dynamic &target, const folly::dynamic &updates, diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/registries/CSSAnimationsRegistry.h b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/registries/CSSAnimationsRegistry.h index a0b161511222..d49d87fb11f9 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/registries/CSSAnimationsRegistry.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/registries/CSSAnimationsRegistry.h @@ -2,6 +2,8 @@ #include #include +#include +#include #include #include #include @@ -23,6 +25,8 @@ class CSSAnimationsRegistry : public UpdatesRegistry, std::enable_shared_from_th public: using SettingsUpdates = std::vector>; + explicit CSSAnimationsRegistry(const std::shared_ptr &eventsEmitter); + bool isEmpty() const override; bool hasUpdates() const; @@ -32,12 +36,18 @@ class CSSAnimationsRegistry : public UpdatesRegistry, std::enable_shared_from_th const std::optional> &animationNames, const CSSAnimationsMap &newAnimations, const CSSAnimationSettingsUpdatesMap &settingsUpdates, + CSSAnimationEventListeners eventListeners, double timestamp); void remove(Tag viewTag) override; void update(double timestamp); private: + struct AnimationStateSnapshot { + AnimationProgressState state; + unsigned iteration; + }; + using AnimationToIndexMap = std::unordered_map, size_t>; using RunningAnimationIndicesMap = std::unordered_map>; using AnimationsToRevertMap = std::unordered_map>; @@ -48,12 +58,16 @@ class CSSAnimationsRegistry : public UpdatesRegistry, std::enable_shared_from_th using Registry = std::unordered_map; + const std::shared_ptr eventsEmitter_; + Registry registry_; RunningAnimationIndicesMap runningAnimationIndicesMap_; AnimationsToRevertMap animationsToRevertMap_; DelayedItemsManager> delayedAnimationsManager_; + std::unordered_map eventListenersMap_; + CSSAnimationsVector buildAnimationsVector( jsi::Runtime &rt, const std::shared_ptr &shadowNode, @@ -74,6 +88,12 @@ class CSSAnimationsRegistry : public UpdatesRegistry, std::enable_shared_from_th void activateDelayedAnimations(double timestamp); void handleAnimationsToRevert(double timestamp); + void scheduleAnimationEvents( + Tag viewTag, + const std::shared_ptr &animation, + const AnimationStateSnapshot &before, + const AnimationStateSnapshot &after); + static bool addStyleUpdates(folly::dynamic &target, const folly::dynamic &updates, bool shouldOverride); }; diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp index f3731ebd2bd0..752463f2bda1 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp @@ -135,7 +135,8 @@ ReanimatedModuleProxy::ReanimatedModuleProxy( updatesRegistryManager_(std::make_shared(staticPropsRegistry_)), viewStylesRepository_(std::make_shared(staticPropsRegistry_, animatedPropsRegistry_)), cssAnimationKeyframesRegistry_(std::make_shared(viewStylesRepository_)), - cssAnimationsRegistry_(std::make_shared()), + cssEventsEmitter_(std::make_shared(jsCallInvoker)), + cssAnimationsRegistry_(std::make_shared(cssEventsEmitter_)), cssTransitionsRegistry_(std::make_shared(getAnimationTimestamp_, viewStylesRepository_)), synchronouslyUpdateUIPropsFunction_(platformDepMethodsHolder.synchronouslyUpdateUIPropsFunction), #ifdef ANDROID @@ -498,7 +499,14 @@ void ReanimatedModuleProxy::applyCSSAnimations( { auto lock = cssAnimationsRegistry_->lock(); cssAnimationsRegistry_->apply( - rt, shadowNode, updates.animationNames, newAnimations, updates.settingsUpdates, timestamp); + rt, + shadowNode, + updates.animationNames, + newAnimations, + updates.settingsUpdates, + // TODO: replace this placeholder with the actual event listeners + 0, + timestamp); } maybeRunCSSLoop(); diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.h b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.h index e156ec7ea8c9..2ef2fb5c25a5 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -199,6 +200,7 @@ class ReanimatedModuleProxy : public ReanimatedModuleProxySpec, const std::shared_ptr updatesRegistryManager_; const std::shared_ptr viewStylesRepository_; const std::shared_ptr cssAnimationKeyframesRegistry_; + const std::shared_ptr cssEventsEmitter_; const std::shared_ptr cssAnimationsRegistry_; const std::shared_ptr cssTransitionsRegistry_;