diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/config/CSSTransitionConfig.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/config/CSSTransitionConfig.cpp index d46678a2aa0..d590f93205d 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/config/CSSTransitionConfig.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/config/CSSTransitionConfig.cpp @@ -3,6 +3,22 @@ namespace reanimated { +std::optional getTransitionPropertySettings( + const CSSTransitionPropertiesSettings &propertiesSettings, + const std::string &propName) { + // Try to use property specific settings first + const auto &propIt = propertiesSettings.find(propName); + if (propIt != propertiesSettings.end()) { + return propertiesSettings.at(propName); + } + // Fallback to "all" settings if no property specific settings are available + if (propertiesSettings.find("all") != propertiesSettings.end()) { + return propertiesSettings.at("all"); + } + // Or return nullopt if no settings are available + return std::nullopt; +} + TransitionProperties getProperties( jsi::Runtime &rt, const jsi::Object &config) { @@ -24,6 +40,10 @@ TransitionProperties getProperties( return std::nullopt; } +bool getAllowDiscrete(jsi::Runtime &rt, const jsi::Object &config) { + return config.getProperty(rt, "allowDiscrete").asBool(); +} + CSSTransitionPropertiesSettings parseCSSTransitionPropertiesSettings( jsi::Runtime &rt, const jsi::Object &settings) { @@ -44,7 +64,8 @@ CSSTransitionPropertiesSettings parseCSSTransitionPropertiesSettings( CSSTransitionPropertySettings{ getDuration(rt, propertySettings), getTimingFunction(rt, propertySettings), - getDelay(rt, propertySettings)}); + getDelay(rt, propertySettings), + getAllowDiscrete(rt, propertySettings)}); } return result; @@ -57,8 +78,7 @@ CSSTransitionConfig parseCSSTransitionConfig( return CSSTransitionConfig{ getProperties(rt, configObj), parseCSSTransitionPropertiesSettings( - rt, configObj.getProperty(rt, "settings").asObject(rt)), - configObj.getProperty(rt, "allowDiscrete").asBool()}; + rt, configObj.getProperty(rt, "settings").asObject(rt))}; } PartialCSSTransitionConfig parsePartialCSSTransitionConfig( @@ -75,9 +95,6 @@ PartialCSSTransitionConfig parsePartialCSSTransitionConfig( result.settings = parseCSSTransitionPropertiesSettings( rt, partialObj.getProperty(rt, "settings").asObject(rt)); } - if (partialObj.hasProperty(rt, "allowDiscrete")) { - result.allowDiscrete = partialObj.getProperty(rt, "allowDiscrete").asBool(); - } return result; } diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/config/CSSTransitionConfig.h b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/config/CSSTransitionConfig.h index 9ec3f9dbc58..a28e980b4ef 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/config/CSSTransitionConfig.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/config/CSSTransitionConfig.h @@ -14,6 +14,7 @@ struct CSSTransitionPropertySettings { double duration; EasingFunction easingFunction; double delay; + bool allowDiscrete; }; using CSSTransitionPropertiesSettings = @@ -22,15 +23,17 @@ using CSSTransitionPropertiesSettings = struct CSSTransitionConfig { TransitionProperties properties; CSSTransitionPropertiesSettings settings; - bool allowDiscrete; }; struct PartialCSSTransitionConfig { std::optional properties; std::optional settings; - std::optional allowDiscrete; }; +std::optional getTransitionPropertySettings( + const CSSTransitionPropertiesSettings &propertiesSettings, + const std::string &propName); + TransitionProperties getProperties(jsi::Runtime &rt, const jsi::Object &config); CSSTransitionPropertiesSettings parseCSSTransitionPropertiesSettings( diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/core/CSSTransition.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/core/CSSTransition.cpp index c7e5181bfe1..2c21de802ff 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/core/CSSTransition.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/core/CSSTransition.cpp @@ -8,10 +8,10 @@ CSSTransition::CSSTransition( const CSSTransitionConfig &config, const std::shared_ptr &viewStylesRepository) : shadowNode_(std::move(shadowNode)), - properties_(config.properties), - allowDiscrete_(config.allowDiscrete), viewStylesRepository_(viewStylesRepository), - progressProvider_(TransitionProgressProvider(config.settings)), + properties_(config.properties), + settings_(config.settings), + progressProvider_(TransitionProgressProvider()), styleInterpolator_(TransitionStyleInterpolator(viewStylesRepository)) {} Tag CSSTransition::getViewTag() const { @@ -22,14 +22,6 @@ ShadowNode::Shared CSSTransition::getShadowNode() const { return shadowNode_; } -const TransitionProperties &CSSTransition::getProperties() const { - return properties_; -} - -bool CSSTransition::getAllowDiscrete() const { - return allowDiscrete_; -} - double CSSTransition::getMinDelay(double timestamp) const { return progressProvider_.getMinDelay(timestamp); } @@ -42,15 +34,57 @@ jsi::Value CSSTransition::getCurrentInterpolationStyle(jsi::Runtime &rt) const { return styleInterpolator_.getCurrentInterpolationStyle(rt, shadowNode_); } +PropertyNames CSSTransition::getAllowedProperties( + jsi::Runtime &rt, + const jsi::Value &oldProps, + const jsi::Value &newProps) { + if (!oldProps.isObject() || !newProps.isObject()) { + return {}; + } + + // If specific properties are set, process only those + if (properties_.has_value()) { + PropertyNames allowedProps; + const auto &properties = properties_.value(); + allowedProps.reserve(properties.size()); + + for (const auto &prop : properties) { + if (isAllowedProperty(prop)) { + allowedProps.push_back(prop); + } + } + + return allowedProps; + } + + // Process all properties from both old and new props + std::unordered_set allAllowedProps; + + auto processProps = [&](const jsi::Object &propsObj) { + const auto &propertyNames = propsObj.getPropertyNames(rt); + const size_t size = propertyNames.size(rt); + + for (size_t i = 0; i < size; i++) { + const auto &propertyName = + propertyNames.getValueAtIndex(rt, i).asString(rt).utf8(rt); + if (isAllowedProperty(propertyName)) { + allAllowedProps.insert(propertyName); + } + } + }; + + processProps(oldProps.asObject(rt)); + processProps(newProps.asObject(rt)); + + return {allAllowedProps.begin(), allAllowedProps.end()}; +} + void CSSTransition::updateSettings(const PartialCSSTransitionConfig &config) { if (config.properties.has_value()) { updateTransitionProperties(config.properties.value()); } - if (config.allowDiscrete.has_value()) { - allowDiscrete_ = config.allowDiscrete.value(); - } if (config.settings.has_value()) { - progressProvider_.setSettings(config.settings.value()); + settings_ = config.settings.value(); } } @@ -60,6 +94,7 @@ jsi::Value CSSTransition::run( const double timestamp) { progressProvider_.runProgressProviders( timestamp, + settings_, changedProps.changedPropertyNames, styleInterpolator_.getReversedPropertyNames(rt, changedProps.newProps)); styleInterpolator_.updateInterpolatedProperties( @@ -85,10 +120,25 @@ void CSSTransition::updateTransitionProperties( const std::unordered_set transitionPropertyNames( properties_->begin(), properties_->end()); + styleInterpolator_.discardIrrelevantInterpolators(transitionPropertyNames); progressProvider_.discardIrrelevantProgressProviders(transitionPropertyNames); } +bool CSSTransition::isAllowedProperty(const std::string &propName) const { + if (!isDiscreteProperty(propName)) { + return true; + } + + const auto &propertySettings = + getTransitionPropertySettings(settings_, propName); + + if (!propertySettings.has_value()) { + return false; + } + return propertySettings.value().allowDiscrete; +} + } // namespace reanimated #endif // RCT_NEW_ARCH_ENABLED diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/core/CSSTransition.h b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/core/CSSTransition.h index ee0499a05aa..cf9608aed7b 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/core/CSSTransition.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/core/CSSTransition.h @@ -22,11 +22,13 @@ class CSSTransition { Tag getViewTag() const; ShadowNode::Shared getShadowNode() const; - const TransitionProperties &getProperties() const; - bool getAllowDiscrete() const; double getMinDelay(double timestamp) const; TransitionProgressState getState() const; jsi::Value getCurrentInterpolationStyle(jsi::Runtime &rt) const; + PropertyNames getAllowedProperties( + jsi::Runtime &rt, + const jsi::Value &oldProps, + const jsi::Value &newProps); void updateSettings(const PartialCSSTransitionConfig &config); jsi::Value @@ -35,13 +37,14 @@ class CSSTransition { private: const ShadowNode::Shared shadowNode_; - TransitionProperties properties_; - bool allowDiscrete_; const std::shared_ptr viewStylesRepository_; + TransitionProperties properties_; + CSSTransitionPropertiesSettings settings_; TransitionProgressProvider progressProvider_; TransitionStyleInterpolator styleInterpolator_; void updateTransitionProperties(const TransitionProperties &properties); + bool isAllowedProperty(const std::string &propName) const; }; } // namespace reanimated diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/misc/ViewStylesRepository.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/misc/ViewStylesRepository.cpp index e11d395c001..b24cb0cb012 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/misc/ViewStylesRepository.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/misc/ViewStylesRepository.cpp @@ -128,7 +128,7 @@ void ViewStylesRepository::updateCacheIfNeeded( jsi::Value ViewStylesRepository::getPropertyValue( jsi::Runtime &rt, const folly::dynamic &value, - const std::vector &propertyPath) { + const PropertyPath &propertyPath) { const folly::dynamic *currentValue = &value; for (size_t i = 0; i < propertyPath.size(); ++i) { diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/progress/TransitionProgressProvider.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/progress/TransitionProgressProvider.cpp index 20eb14b8297..58361b4b092 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/progress/TransitionProgressProvider.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/progress/TransitionProgressProvider.cpp @@ -76,15 +76,6 @@ double TransitionPropertyProgressProvider::getElapsedTime( // TransitionProgressProvider -TransitionProgressProvider::TransitionProgressProvider( - const CSSTransitionPropertiesSettings &settings) - : settings_(std::move(settings)) {} - -void TransitionProgressProvider::setSettings( - const CSSTransitionPropertiesSettings &settings) { - settings_ = settings; -} - TransitionProgressState TransitionProgressProvider::getState() const { for (const auto &[_, propertyProgressProvider] : propertyProgressProviders_) { if (propertyProgressProvider->getState() == @@ -139,15 +130,24 @@ void TransitionProgressProvider::discardIrrelevantProgressProviders( void TransitionProgressProvider::runProgressProviders( const double timestamp, + const CSSTransitionPropertiesSettings &propertiesSettings, const PropertyNames &changedPropertyNames, const std::unordered_set &reversedPropertyNames) { for (const auto &propertyName : changedPropertyNames) { - const auto propertySettings = getPropertySettings(propertyName); - const auto progressProviderIt = - propertyProgressProviders_.find(propertyName); + const auto propertySettingsOptional = + getTransitionPropertySettings(propertiesSettings, propertyName); + + if (!propertySettingsOptional.has_value()) { + throw std::invalid_argument( + "[Reanimated] Property '" + propertyName + + "' is not a valid transition property"); + } + + const auto &propertySettings = propertySettingsOptional.value(); + const auto it = propertyProgressProviders_.find(propertyName); - if (progressProviderIt != propertyProgressProviders_.end()) { - const auto &progressProvider = progressProviderIt->second; + if (it != propertyProgressProviders_.end()) { + const auto &progressProvider = it->second; progressProvider->update(timestamp); if (reversedPropertyNames.find(propertyName) != @@ -192,15 +192,6 @@ void TransitionProgressProvider::update(const double timestamp) { } } -CSSTransitionPropertySettings TransitionProgressProvider::getPropertySettings( - const std::string &propertyName) const { - // Find property settings or fallback to "all" settings if no property - // specific settings are available - const auto propertySettingsIt = settings_.find(propertyName); - return (propertySettingsIt != settings_.end()) ? propertySettingsIt->second - : settings_.at("all"); -} - std::shared_ptr TransitionProgressProvider::createReversingShorteningProgressProvider( const double timestamp, diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/progress/TransitionProgressProvider.h b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/progress/TransitionProgressProvider.h index eb6deb341dc..a9543e1b223 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/progress/TransitionProgressProvider.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/progress/TransitionProgressProvider.h @@ -59,11 +59,6 @@ using TransitionPropertyProgressProviders = std::unordered_map< class TransitionProgressProvider final { public: - explicit TransitionProgressProvider( - const CSSTransitionPropertiesSettings &settings); - - void setSettings(const CSSTransitionPropertiesSettings &settings); - TransitionProgressState getState() const; double getMinDelay(double timestamp) const; TransitionPropertyProgressProviders getPropertyProgressProviders() const; @@ -73,18 +68,16 @@ class TransitionProgressProvider final { const std::unordered_set &transitionPropertyNames); void runProgressProviders( double timestamp, + const CSSTransitionPropertiesSettings &propertiesSettings, const PropertyNames &changedPropertyNames, const std::unordered_set &reversedPropertyNames); void update(double timestamp); private: - CSSTransitionPropertiesSettings settings_; TransitionPropertyProgressProviders propertyProgressProviders_; std::unordered_set removedProperties_; - CSSTransitionPropertySettings getPropertySettings( - const std::string &propertyName) const; std::shared_ptr createReversingShorteningProgressProvider( double timestamp, diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/registry/CSSTransitionsRegistry.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/registry/CSSTransitionsRegistry.cpp index c582436c13b..f17864be46b 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/registry/CSSTransitionsRegistry.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/registry/CSSTransitionsRegistry.cpp @@ -128,12 +128,11 @@ PropsObserver CSSTransitionsRegistry::createPropsObserver(const Tag viewTag) { } const auto &transition = strongThis->registry_.at(viewTag); - const auto changedProps = getChangedProps( - rt, - oldProps, - newProps, - transition->getAllowDiscrete(), - transition->getProperties()); + const auto allowedProperties = + transition->getAllowedProperties(rt, oldProps, newProps); + + const auto changedProps = + getChangedProps(rt, oldProps, newProps, allowedProperties); if (changedProps.changedPropertyNames.empty()) { return; diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/util/interpolators.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/util/interpolators.cpp index 1d536c4d38e..d798a6b6885 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/util/interpolators.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/util/interpolators.cpp @@ -5,7 +5,7 @@ namespace reanimated { std::shared_ptr createPropertyInterpolator( const std::string &propertyName, - const std::vector &propertyPath, + const PropertyPath &propertyPath, const InterpolatorFactoriesRecord &factories, const std::shared_ptr &progressProvider, const std::shared_ptr &viewStylesRepository) { @@ -26,7 +26,7 @@ std::shared_ptr createPropertyInterpolator( std::shared_ptr createPropertyInterpolator( size_t arrayIndex, - const std::vector &propertyPath, + const PropertyPath &propertyPath, const InterpolatorFactoriesArray &factories, const std::shared_ptr &progressProvider, const std::shared_ptr &viewStylesRepository) { diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/util/interpolators.h b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/util/interpolators.h index b9c62a4dbb0..fdc32e8d884 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/util/interpolators.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/util/interpolators.h @@ -11,14 +11,14 @@ namespace reanimated { std::shared_ptr createPropertyInterpolator( const std::string &propertyName, - const std::vector &propertyPath, + const PropertyPath &propertyPath, const InterpolatorFactoriesRecord &factories, const std::shared_ptr &progressProvider, const std::shared_ptr &viewStylesRepository); std::shared_ptr createPropertyInterpolator( size_t arrayIndex, - const std::vector &propertyPath, + const PropertyPath &propertyPath, const InterpolatorFactoriesArray &factories, const std::shared_ptr &progressProvider, const std::shared_ptr &viewStylesRepository); diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/util/props.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/util/props.cpp index ac6afdb97a0..4ad1d7d0837 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/util/props.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/util/props.cpp @@ -3,6 +3,14 @@ namespace reanimated { +bool isDiscreteProperty(const std::string &propName) { + const auto it = PROPERTY_INTERPOLATORS_CONFIG.find(propName); + if (it == PROPERTY_INTERPOLATORS_CONFIG.end()) { + return false; + } + return it->second->isDiscreteProperty(); +} + bool areArraysDifferentRecursive( jsi::Runtime &rt, const jsi::Array &oldArray, @@ -126,7 +134,7 @@ std::pair getChangedValueForProp( return std::make_pair(jsi::Value::undefined(), jsi::Value::undefined()); } - // Check for property existing in only one of the objects + // Check if property exists in only one of the objects if (oldHasProperty) { jsi::Value oldVal = oldObject.getProperty(rt, propName.c_str()); if (!oldVal.isUndefined()) { @@ -142,16 +150,19 @@ std::pair getChangedValueForProp( return std::make_pair(jsi::Value::undefined(), jsi::Value::undefined()); } -ChangedProps processPropertyChanges( +ChangedProps getChangedProps( jsi::Runtime &rt, - const jsi::Object &oldObject, - const jsi::Object &newObject, - const PropertyNames &propertyNames) { + const jsi::Value &oldProps, + const jsi::Value &newProps, + const PropertyNames &allowedProperties) { + const auto oldObject = oldProps.asObject(rt); + const auto newObject = newProps.asObject(rt); + auto oldResult = jsi::Object(rt); auto newResult = jsi::Object(rt); PropertyNames changedPropertyNames; - for (const auto &propName : propertyNames) { + for (const auto &propName : allowedProperties) { const auto [oldChangedProp, newChangedProp] = getChangedValueForProp(rt, oldObject, newObject, propName); @@ -175,76 +186,6 @@ ChangedProps processPropertyChanges( std::move(changedPropertyNames)}; } -bool isDiscreteProperty(const std::string &propName) { - const auto it = PROPERTY_INTERPOLATORS_CONFIG.find(propName); - if (it == PROPERTY_INTERPOLATORS_CONFIG.end()) { - return false; - } - return it->second->isDiscreteProperty(); -} - -ChangedProps getChangedProps( - jsi::Runtime &rt, - const jsi::Value &oldProps, - const jsi::Value &newProps, - bool allowDiscrete) { - const auto &oldObject = oldProps.asObject(rt); - const auto &newObject = newProps.asObject(rt); - - const auto oldPropertyNames = oldObject.getPropertyNames(rt); - const auto newPropertyNames = newObject.getPropertyNames(rt); - - std::unordered_set allPropNames; - - const size_t oldSize = oldPropertyNames.size(rt); - const size_t newSize = newPropertyNames.size(rt); - - for (size_t i = 0; i < oldSize; i++) { - const auto propName = - oldPropertyNames.getValueAtIndex(rt, i).asString(rt).utf8(rt); - if (allowDiscrete || !isDiscreteProperty(propName)) { - allPropNames.insert(propName); - } - } - for (size_t i = 0; i < newSize; i++) { - const auto propName = - newPropertyNames.getValueAtIndex(rt, i).asString(rt).utf8(rt); - if (allowDiscrete || !isDiscreteProperty(propName)) { - allPropNames.insert(propName); - } - } - - const PropertyNames propNamesVec(allPropNames.begin(), allPropNames.end()); - - return processPropertyChanges(rt, oldObject, newObject, propNamesVec); -} - -ChangedProps getChangedProps( - jsi::Runtime &rt, - const jsi::Value &oldProps, - const jsi::Value &newProps, - bool allowDiscrete, - const std::optional &propertyNames) { - if (!propertyNames.has_value()) { - return getChangedProps(rt, oldProps, newProps, allowDiscrete); - } - - const auto &transitionProperties = propertyNames.value(); - PropertyNames propNamesVec; - propNamesVec.reserve(transitionProperties.size()); - for (const auto &propName : transitionProperties) { - if (allowDiscrete || !isDiscreteProperty(propName)) { - propNamesVec.emplace_back(propName); - } - } - - return processPropertyChanges( - rt, - oldProps.asObject(rt), - newProps.asObject(rt), - std::move(propNamesVec)); -} - void updateJSIObject( jsi::Runtime &rt, const jsi::Object &target, diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/util/props.h b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/util/props.h index 59e04764f0f..63270a8987e 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/util/props.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/util/props.h @@ -20,6 +20,8 @@ struct ChangedProps { const PropertyNames changedPropertyNames; }; +bool isDiscreteProperty(const std::string &propName); + // We need to specify it here because there are 2 methods referencing // each other in the recursion and areArraysDifferentRecursive must be // aware that getChangedPropsRecursive exists @@ -32,14 +34,7 @@ ChangedProps getChangedProps( jsi::Runtime &rt, const jsi::Value &oldProps, const jsi::Value &newProps, - bool allowDiscrete, - const std::optional &propertyNames); - -ChangedProps getChangedProps( - jsi::Runtime &rt, - const jsi::Value &oldProps, - const jsi::Value &newProps, - bool allowDiscrete); + const PropertyNames &allowedProperties); void updateJSIObject( jsi::Runtime &rt, diff --git a/packages/react-native-reanimated/src/css/managers/__tests__/CSSTransitionManager.test.ts b/packages/react-native-reanimated/src/css/managers/__tests__/CSSTransitionManager.test.ts index 42f1f55bbe2..7f6493048d5 100644 --- a/packages/react-native-reanimated/src/css/managers/__tests__/CSSTransitionManager.test.ts +++ b/packages/react-native-reanimated/src/css/managers/__tests__/CSSTransitionManager.test.ts @@ -85,6 +85,7 @@ describe('CSSTransitionManager', () => { duration: 1500, delay: 0, timingFunction: 'ease', + allowDiscrete: false, }, }, }); diff --git a/packages/react-native-reanimated/src/css/platform/native/normalization/transition/__tests__/config.test.ts b/packages/react-native-reanimated/src/css/platform/native/normalization/transition/__tests__/config.test.ts index 7b52f1786d6..4b33f21a3e4 100644 --- a/packages/react-native-reanimated/src/css/platform/native/normalization/transition/__tests__/config.test.ts +++ b/packages/react-native-reanimated/src/css/platform/native/normalization/transition/__tests__/config.test.ts @@ -29,12 +29,12 @@ describe(normalizeCSSTransitionProperties, () => { expect(normalizeCSSTransitionProperties(config)).toEqual({ properties: 'all', - allowDiscrete: true, settings: { all: { duration: 1500, timingFunction: cubicBezier(0.4, 0, 0.2, 1).normalize(), delay: 300, + allowDiscrete: true, }, }, }); @@ -47,12 +47,12 @@ describe(normalizeCSSTransitionProperties, () => { expect(normalizeCSSTransitionProperties(config)).toEqual({ properties: ['opacity'], - allowDiscrete: false, settings: { opacity: { duration: 0, timingFunction: 'ease', delay: 0, + allowDiscrete: false, }, }, }); @@ -84,12 +84,12 @@ describe(normalizeCSSTransitionProperties, () => { expect(normalizeCSSTransitionProperties(config)).toEqual({ properties: 'all', - allowDiscrete: false, settings: { all: { duration: 1500, timingFunction: 'ease', delay: 300, + allowDiscrete: false, }, }, }); @@ -103,21 +103,23 @@ describe(normalizeCSSTransitionProperties, () => { transitionDuration: ['1.5s', '2s'], transitionTimingFunction: ['ease-in', cubicBezier(0.4, 0, 0.2, 1)], transitionDelay: ['300ms', '500ms'], + transitionBehavior: ['allow-discrete', 'normal'], }; expect(normalizeCSSTransitionProperties(config)).toEqual({ properties: ['opacity', 'transform'], - allowDiscrete: false, settings: { opacity: { duration: 1500, timingFunction: 'ease-in', delay: 300, + allowDiscrete: true, }, transform: { duration: 2000, timingFunction: cubicBezier(0.4, 0, 0.2, 1).normalize(), delay: 500, + allowDiscrete: false, }, }, }); @@ -129,21 +131,23 @@ describe(normalizeCSSTransitionProperties, () => { transitionDuration: '1.5s', transitionTimingFunction: 'ease-in', transitionDelay: ['300ms', '300ms'], + transitionBehavior: 'allow-discrete', }; expect(normalizeCSSTransitionProperties(config)).toEqual({ properties: ['opacity', 'width'], - allowDiscrete: false, settings: { opacity: { duration: 1500, timingFunction: 'ease-in', delay: 300, + allowDiscrete: true, }, width: { duration: 1500, timingFunction: 'ease-in', delay: 300, + allowDiscrete: true, }, }, }); @@ -155,26 +159,29 @@ describe(normalizeCSSTransitionProperties, () => { transitionDuration: ['1.5s', '2s'], transitionTimingFunction: 'ease-in', transitionDelay: ['300ms', '500ms'], + transitionBehavior: ['allow-discrete', 'normal'], }; expect(normalizeCSSTransitionProperties(config)).toEqual({ properties: ['width', 'opacity', 'transform'], - allowDiscrete: false, settings: { width: { duration: 1500, timingFunction: 'ease-in', delay: 300, + allowDiscrete: true, }, opacity: { duration: 2000, timingFunction: 'ease-in', delay: 500, + allowDiscrete: false, }, transform: { duration: 1500, timingFunction: 'ease-in', delay: 300, + allowDiscrete: true, }, }, }); @@ -186,16 +193,17 @@ describe(normalizeCSSTransitionProperties, () => { transitionDuration: ['1.5s', '2s'], transitionTimingFunction: ['ease-in', cubicBezier(0.4, 0, 0.2, 1)], transitionDelay: '300ms', + transitionBehavior: ['normal', 'allow-discrete', 'normal'], }; expect(normalizeCSSTransitionProperties(config)).toEqual({ properties: ['opacity'], - allowDiscrete: false, settings: { opacity: { duration: 2000, timingFunction: cubicBezier(0.4, 0, 0.2, 1).normalize(), delay: 300, + allowDiscrete: true, }, }, }); @@ -207,21 +215,23 @@ describe(normalizeCSSTransitionProperties, () => { transitionDuration: ['1.5s', '2s'], transitionTimingFunction: ['ease-in', cubicBezier(0.4, 0, 0.2, 1)], transitionDelay: ['300ms', '500ms'], + transitionBehavior: ['normal', 'allow-discrete'], }; expect(normalizeCSSTransitionProperties(config)).toEqual({ properties: 'all', - allowDiscrete: false, settings: { all: { duration: 1500, timingFunction: 'ease-in', delay: 300, + allowDiscrete: false, }, opacity: { duration: 2000, timingFunction: cubicBezier(0.4, 0, 0.2, 1).normalize(), delay: 500, + allowDiscrete: true, }, }, }); @@ -248,23 +258,23 @@ describe(getNormalizedCSSTransitionConfigUpdates, () => { it('returns empty object if nothing changed', () => { const oldConfig: NormalizedCSSTransitionConfig = { properties: 'all', - allowDiscrete: false, settings: { all: { duration: 1500, timingFunction: cubicBezier(0.4, 0, 0.2, 1).normalize(), delay: 300, + allowDiscrete: false, }, }, }; const newConfig: NormalizedCSSTransitionConfig = { properties: 'all', - allowDiscrete: false, settings: { all: { duration: 1500, timingFunction: cubicBezier(0.4, 0, 0.2, 1).normalize(), delay: 300, + allowDiscrete: false, }, }, }; @@ -288,12 +298,10 @@ describe(getNormalizedCSSTransitionConfigUpdates, () => { const oldConfig: NormalizedCSSTransitionConfig = { properties: oldProperties, settings: {}, - allowDiscrete: false, }; const newConfig: NormalizedCSSTransitionConfig = { properties: newProperties, settings: {}, - allowDiscrete: false, }; expect( @@ -312,12 +320,10 @@ describe(getNormalizedCSSTransitionConfigUpdates, () => { const oldConfig: NormalizedCSSTransitionConfig = { properties, settings: {}, - allowDiscrete: false, }; const newConfig: NormalizedCSSTransitionConfig = { properties, settings: {}, - allowDiscrete: false, }; expect( @@ -337,9 +343,9 @@ describe(getNormalizedCSSTransitionConfigUpdates, () => { duration: 1500, timingFunction: cubicBezier(0.4, 0, 0.2, 1).normalize(), delay: 300, + allowDiscrete: false, }, }, - allowDiscrete: false, }; const newConfig: NormalizedCSSTransitionConfig = { properties: 'all', @@ -348,9 +354,9 @@ describe(getNormalizedCSSTransitionConfigUpdates, () => { duration: 1500, timingFunction: 'ease-in', // changed delay: 300, + allowDiscrete: false, }, }, - allowDiscrete: false, }; expect( @@ -361,6 +367,7 @@ describe(getNormalizedCSSTransitionConfigUpdates, () => { duration: 1500, timingFunction: 'ease-in', delay: 300, + allowDiscrete: false, }, }, }); @@ -374,9 +381,9 @@ describe(getNormalizedCSSTransitionConfigUpdates, () => { duration: 1500, timingFunction: cubicBezier(0.4, 0, 0.2, 1).normalize(), delay: 300, + allowDiscrete: false, }, }, - allowDiscrete: false, }; const newConfig: NormalizedCSSTransitionConfig = { properties: 'all', @@ -385,9 +392,9 @@ describe(getNormalizedCSSTransitionConfigUpdates, () => { duration: 1500, timingFunction: cubicBezier(0.4, 0, 0.2, 1).normalize(), delay: 300, + allowDiscrete: false, }, }, - allowDiscrete: false, }; expect( @@ -405,19 +412,21 @@ describe(getNormalizedCSSTransitionConfigUpdates, () => { duration: 1500, timingFunction: cubicBezier(0.4, 0, 0.2, 1).normalize(), delay: 300, + allowDiscrete: false, }, transform: { duration: 2000, timingFunction: 'ease-in', delay: 500, + allowDiscrete: false, }, width: { duration: 1000, timingFunction: 'ease-out', delay: 200, + allowDiscrete: false, }, }, - allowDiscrete: false, }; const newConfig: NormalizedCSSTransitionConfig = { properties: ['transform', 'width', 'opacity'], @@ -426,41 +435,45 @@ describe(getNormalizedCSSTransitionConfigUpdates, () => { duration: 1500, timingFunction: cubicBezier(0.4, 0, 0.2, 1).normalize(), delay: 500, + allowDiscrete: false, }, transform: { duration: 2000, timingFunction: 'ease-in', delay: 500, + allowDiscrete: true, }, width: { duration: 500, timingFunction: 'ease', delay: 200, + allowDiscrete: false, }, }, - allowDiscrete: true, }; expect( getNormalizedCSSTransitionConfigUpdates(oldConfig, newConfig) ).toEqual({ properties: ['transform', 'width', 'opacity'], - allowDiscrete: true, settings: { opacity: { duration: 1500, timingFunction: cubicBezier(0.4, 0, 0.2, 1).normalize(), delay: 500, + allowDiscrete: false, }, transform: { duration: 2000, timingFunction: 'ease-in', delay: 500, + allowDiscrete: true, }, width: { duration: 500, timingFunction: 'ease', delay: 200, + allowDiscrete: false, }, }, }); diff --git a/packages/react-native-reanimated/src/css/platform/native/normalization/transition/config.ts b/packages/react-native-reanimated/src/css/platform/native/normalization/transition/config.ts index 5c4da169604..e2c6ef57c74 100644 --- a/packages/react-native-reanimated/src/css/platform/native/normalization/transition/config.ts +++ b/packages/react-native-reanimated/src/css/platform/native/normalization/transition/config.ts @@ -39,6 +39,7 @@ export function normalizeCSSTransitionProperties( transitionDuration, transitionTimingFunction, transitionDelay, + transitionBehavior, } = convertConfigPropertiesToArrays(config); if (hasNoTransitionProperties(transitionProperty)) { @@ -50,7 +51,9 @@ export function normalizeCSSTransitionProperties( const settings: Record = {}; // Go from the last to the first property to ensure that the last - // one overrides previous ones in case of duplicate properties + // one entry for the same property is used without having to override + // it multiple times if specified more than once (we just take the last + // occurrence and ignore remaining ones) for (let i = transitionProperty.length - 1; i >= 0; i--) { const property = transitionProperty[i]; @@ -77,6 +80,9 @@ export function normalizeCSSTransitionProperties( transitionTimingFunction?.[i % transitionTimingFunction.length] ), delay: normalizeDelay(transitionDelay?.[i % transitionDelay.length]), + allowDiscrete: normalizeTransitionBehavior( + transitionBehavior?.[i % transitionBehavior.length] + ), }; // 'all' transition property overrides all properties before it, @@ -89,7 +95,6 @@ export function normalizeCSSTransitionProperties( return { properties: allPropertiesTransition ? 'all' : specificProperties.reverse(), settings, - allowDiscrete: normalizeTransitionBehavior(config.transitionBehavior), }; } @@ -128,9 +133,5 @@ export function getNormalizedCSSTransitionConfigUpdates( } } - if (oldConfig.allowDiscrete !== newConfig.allowDiscrete) { - configUpdates.allowDiscrete = newConfig.allowDiscrete; - } - return configUpdates; } diff --git a/packages/react-native-reanimated/src/css/platform/native/types/transition.ts b/packages/react-native-reanimated/src/css/platform/native/types/transition.ts index 9cfde43e619..ec257e63af7 100644 --- a/packages/react-native-reanimated/src/css/platform/native/types/transition.ts +++ b/packages/react-native-reanimated/src/css/platform/native/types/transition.ts @@ -6,6 +6,7 @@ export type NormalizedSingleCSSTransitionSettings = { duration: number; timingFunction: NormalizedCSSTimingFunction; delay: number; + allowDiscrete: boolean; }; export type NormalizedCSSTransitionPropertyNames = 'all' | (keyof PlainStyle)[]; @@ -13,7 +14,6 @@ export type NormalizedCSSTransitionPropertyNames = 'all' | (keyof PlainStyle)[]; export type NormalizedCSSTransitionConfig = { properties: NormalizedCSSTransitionPropertyNames; settings: Record; - allowDiscrete: boolean; }; export type NormalizedCSSTransitionConfigUpdates = diff --git a/packages/react-native-reanimated/src/css/types/transition.ts b/packages/react-native-reanimated/src/css/types/transition.ts index 51abb736a22..ea0ea992659 100644 --- a/packages/react-native-reanimated/src/css/types/transition.ts +++ b/packages/react-native-reanimated/src/css/types/transition.ts @@ -17,6 +17,7 @@ type SingleCSSTransitionSettings = { transitionDuration?: CSSTransitionDuration; transitionTimingFunction?: CSSTransitionTimingFunction; transitionDelay?: CSSTransitionDelay; + transitionBehavior?: CSSTransitionBehavior; }; export type SingleCSSTransitionConfig = @@ -25,14 +26,11 @@ export type SingleCSSTransitionConfig = }; export type CSSTransitionSettings = - AddArrayPropertyTypes & { - transitionBehavior?: CSSTransitionBehavior; - }; + AddArrayPropertyTypes; export type CSSTransitionProperties = - AddArrayPropertyTypes & { + CSSTransitionSettings & { transitionProperty?: CSSTransitionProperty; - transitionBehavior?: CSSTransitionBehavior; }; export type CSSTransitionProp = keyof CSSTransitionProperties;