From 6ab75d0c93a58de201ad6096f9934f4880901c6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81opaci=C5=84ski?= Date: Mon, 27 Jan 2025 16:35:48 +0100 Subject: [PATCH 1/6] Add boxSizing prop support --- .../apps/css/examples/animations/routes.ts | 5 ++ .../layoutAndPositioning/others/BoxSizing.tsx | 63 +++++++++++++++++++ .../layoutAndPositioning/others/index.ts | 2 + .../CSS/registry/CSSAnimationsRegistry.cpp | 5 ++ .../css/managers/CSSAnimationsManager.web.ts | 1 + .../src/css/platform/native/config.ts | 2 +- .../src/css/platform/web/config.ts | 2 +- .../src/propsAllowlists.ts | 1 + 8 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/layoutAndPositioning/others/BoxSizing.tsx diff --git a/apps/common-app/src/apps/css/examples/animations/routes.ts b/apps/common-app/src/apps/css/examples/animations/routes.ts index d15150c9c654..3a1929483308 100644 --- a/apps/common-app/src/apps/css/examples/animations/routes.ts +++ b/apps/common-app/src/apps/css/examples/animations/routes.ts @@ -147,6 +147,11 @@ const layoutAndPositioningRoutes = { name: 'Aspect Ratio', Component: animatedProperties.layoutAndPositioning.others.AspectRatio, }, + BoxSizing: { + name: 'Box Sizing', + labelTypes: ['web'], + Component: animatedProperties.layoutAndPositioning.others.BoxSizing, + }, }, }, } satisfies Routes; diff --git a/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/layoutAndPositioning/others/BoxSizing.tsx b/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/layoutAndPositioning/others/BoxSizing.tsx new file mode 100644 index 000000000000..842ec0b7b3b0 --- /dev/null +++ b/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/layoutAndPositioning/others/BoxSizing.tsx @@ -0,0 +1,63 @@ +import { StyleSheet, View } from 'react-native'; +import Animated from 'react-native-reanimated'; + +import { ExamplesScreen, VerticalExampleCard } from '@/apps/css/components'; +import { colors, radius, sizes, spacing } from '@/theme'; + +export default function BoxSizing() { + return ( + ({ + animationDuration: '1s', + animationIterationCount: 'infinite', + animationName: { + from: { + boxSizing: 'border-box', + }, + to: { + boxSizing: 'content-box', + }, + }, + animationTimingFunction: 'linear', + })} + renderExample={({ animation }) => ( + + + + )} + sections={[ + { + description: + "`boxSizing` is a **discrete** property. That means, it **can't be smoothly animated** between values. However, we can still change this property in the animation keyframes but the change will be **abrupt**.", + examples: [ + { + // 'In this example, the component in the **foreground** is rendered inside of the component in the **background** with some offset applied. The part that is **outside** of the **background** component is **clipped**.', + title: 'Changing Box Sizing', + }, + ], + labelTypes: ['web'], + title: 'Box Sizing', + }, + ]} + /> + ); +} + +const styles = StyleSheet.create({ + child: { + borderColor: colors.primary, + height: sizes.lg, + width: '100%', + }, + common: { + borderRadius: radius.md, + borderWidth: spacing.sm, + }, + parent: { + backgroundColor: colors.primaryLight, + borderColor: colors.primaryDark, + height: sizes.xxl, + width: sizes.xxl, + }, +}); diff --git a/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/layoutAndPositioning/others/index.ts b/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/layoutAndPositioning/others/index.ts index dc54c41d36ca..3cac9198d976 100644 --- a/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/layoutAndPositioning/others/index.ts +++ b/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/layoutAndPositioning/others/index.ts @@ -1,4 +1,5 @@ import AspectRatio from './AspectRatio'; +import BoxSizing from './BoxSizing'; import Display from './Display'; import Overflow from './Overflow'; import Position from './Position'; @@ -6,6 +7,7 @@ import ZIndex from './ZIndex'; export default { AspectRatio, + BoxSizing, Display, Overflow, Position, diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/registry/CSSAnimationsRegistry.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/registry/CSSAnimationsRegistry.cpp index 8e4282f02718..291439c72538 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/registry/CSSAnimationsRegistry.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/registry/CSSAnimationsRegistry.cpp @@ -1,8 +1,11 @@ #ifdef RCT_NEW_ARCH_ENABLED #include +#include namespace reanimated { +using namespace worklets; + bool CSSAnimationsRegistry::hasUpdates() const { return !runningAnimationsMap_.empty() || !delayedAnimationsManager_.empty(); } @@ -114,6 +117,8 @@ void CSSAnimationsRegistry::updateViewAnimations( const auto updates = animation->update(rt, timestamp); const auto newState = animation->getState(timestamp); + LOG(INFO) << "updates: " << stringifyJSIValue(rt, updates); + if (newState == AnimationProgressState::Finished) { // Revert changes applied during animation if there is no forwards fill // mode diff --git a/packages/react-native-reanimated/src/css/managers/CSSAnimationsManager.web.ts b/packages/react-native-reanimated/src/css/managers/CSSAnimationsManager.web.ts index 03e7cd485166..08a629b3d48e 100644 --- a/packages/react-native-reanimated/src/css/managers/CSSAnimationsManager.web.ts +++ b/packages/react-native-reanimated/src/css/managers/CSSAnimationsManager.web.ts @@ -129,6 +129,7 @@ export default class CSSAnimationsManager { const rule = processedAnimation.keyframesRule; if (rule.processedKeyframes) { // We always call insert as it will insert animation only if it doesn't exist + console.log('insertCSSAnimation', rule.name, rule.processedKeyframes); insertCSSAnimation(rule.name, rule.processedKeyframes); } newAttachedAnimations[rule.processedKeyframes] = processedAnimation; diff --git a/packages/react-native-reanimated/src/css/platform/native/config.ts b/packages/react-native-reanimated/src/css/platform/native/config.ts index 4ddf2f5e1332..cb403494fbb1 100644 --- a/packages/react-native-reanimated/src/css/platform/native/config.ts +++ b/packages/react-native-reanimated/src/css/platform/native/config.ts @@ -102,7 +102,7 @@ export const PROPERTIES_CONFIG: StyleBuilderConfig = { overflow: true, zIndex: true, aspectRatio: { process: processAspectRatio }, - boxSizing: false, // TODO + boxSizing: false, // web only /** Appearance */ // COLORS diff --git a/packages/react-native-reanimated/src/css/platform/web/config.ts b/packages/react-native-reanimated/src/css/platform/web/config.ts index 89b651c6156b..4528aaf06aa4 100644 --- a/packages/react-native-reanimated/src/css/platform/web/config.ts +++ b/packages/react-native-reanimated/src/css/platform/web/config.ts @@ -103,7 +103,7 @@ export const PROPERTIES_CONFIG: StyleBuilderConfig = { overflow: true, zIndex: true, aspectRatio: true, - boxSizing: false, // TODO + boxSizing: true, /** Appearance */ // COLORS diff --git a/packages/react-native-reanimated/src/propsAllowlists.ts b/packages/react-native-reanimated/src/propsAllowlists.ts index 68c079cb450e..d3b9d789908f 100644 --- a/packages/react-native-reanimated/src/propsAllowlists.ts +++ b/packages/react-native-reanimated/src/propsAllowlists.ts @@ -89,6 +89,7 @@ export const PropsAllowlists: AllowlistsHolder = { textShadowOffset: true, letterSpacing: true, aspectRatio: true, + boxSizing: true, columnGap: true, // iOS only end: true, // number or string flexBasis: true, // number or string From 277065dbf23508ecd8e5a03c4878c61b08a72c75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81opaci=C5=84ski?= Date: Mon, 27 Jan 2025 18:21:56 +0100 Subject: [PATCH 2/6] Some progress --- apps/common-app/src/App.tsx | 6 +++++- .../appearance/colors/ColorProperties.tsx | 20 +++++++++++++++++++ apps/web-example/metro.config.js | 8 +++++++- .../CSS/config/PropertyInterpolatorsConfig.h | 1 + .../css/managers/CSSAnimationsManager.web.ts | 1 - .../src/css/platform/native/config.ts | 2 +- .../src/css/platform/web/config.ts | 8 ++++---- .../src/propsAllowlists.ts | 3 ++- 8 files changed, 40 insertions(+), 9 deletions(-) diff --git a/apps/common-app/src/App.tsx b/apps/common-app/src/App.tsx index 9cf2edd81ccb..bf0fc836a0e3 100644 --- a/apps/common-app/src/App.tsx +++ b/apps/common-app/src/App.tsx @@ -121,7 +121,11 @@ function useNavigationState() { >(); const updateNavigationState = useCallback((state?: NavigationState) => { - AsyncStorage.setItem(PERSISTENCE_KEY, JSON.stringify(state)).catch(noop); + if (IS_WEB) { + localStorage.setItem(PERSISTENCE_KEY, JSON.stringify(state)); + } else { + AsyncStorage.setItem(PERSISTENCE_KEY, JSON.stringify(state)).catch(noop); + } }, []); useEffect(() => { diff --git a/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/colors/ColorProperties.tsx b/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/colors/ColorProperties.tsx index af18731dfb65..41a1d140cffd 100644 --- a/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/colors/ColorProperties.tsx +++ b/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/colors/ColorProperties.tsx @@ -296,6 +296,26 @@ function OtherColors() { shadowRadius: spacing.sm, }} /> + (BLACK)}, {"borderBlockColor", value(BLACK)}, // Other + {"outlineColor", value(BLACK)}, {"shadowColor", value(BLACK)}, {"overlayColor", value(BLACK)}, {"tintColor", value(BLACK)}, diff --git a/packages/react-native-reanimated/src/css/managers/CSSAnimationsManager.web.ts b/packages/react-native-reanimated/src/css/managers/CSSAnimationsManager.web.ts index 08a629b3d48e..03e7cd485166 100644 --- a/packages/react-native-reanimated/src/css/managers/CSSAnimationsManager.web.ts +++ b/packages/react-native-reanimated/src/css/managers/CSSAnimationsManager.web.ts @@ -129,7 +129,6 @@ export default class CSSAnimationsManager { const rule = processedAnimation.keyframesRule; if (rule.processedKeyframes) { // We always call insert as it will insert animation only if it doesn't exist - console.log('insertCSSAnimation', rule.name, rule.processedKeyframes); insertCSSAnimation(rule.name, rule.processedKeyframes); } newAttachedAnimations[rule.processedKeyframes] = processedAnimation; diff --git a/packages/react-native-reanimated/src/css/platform/native/config.ts b/packages/react-native-reanimated/src/css/platform/native/config.ts index cb403494fbb1..8aab9877b24f 100644 --- a/packages/react-native-reanimated/src/css/platform/native/config.ts +++ b/packages/react-native-reanimated/src/css/platform/native/config.ts @@ -124,7 +124,7 @@ export const PROPERTIES_CONFIG: StyleBuilderConfig = { borderStartColor: colorAttributes, borderBlockColor: colorAttributes, // Other - outlineColor: false, // TODO + outlineColor: colorAttributes, shadowColor: colorAttributes, overlayColor: IS_ANDROID ? colorAttributes : false, tintColor: colorAttributes, diff --git a/packages/react-native-reanimated/src/css/platform/web/config.ts b/packages/react-native-reanimated/src/css/platform/web/config.ts index 4528aaf06aa4..31ba459ef9b7 100644 --- a/packages/react-native-reanimated/src/css/platform/web/config.ts +++ b/packages/react-native-reanimated/src/css/platform/web/config.ts @@ -125,7 +125,7 @@ export const PROPERTIES_CONFIG: StyleBuilderConfig = { borderStartColor: { as: 'borderLeftColor' }, borderBlockColor: colorAttributes, // Other - outlineColor: false, // TODO + outlineColor: colorAttributes, shadowColor: boxShadowBuilder, overlayColor: colorAttributes, tintColor: colorAttributes, @@ -178,9 +178,9 @@ export const PROPERTIES_CONFIG: StyleBuilderConfig = { borderStyle: true, // OUTLINES - outlineOffset: false, // TODO - outlineStyle: false, // TODO - outlineWidth: false, // TODO + outlineOffset: 'px', + outlineStyle: true, + outlineWidth: 'px', // TRANSFORMS transformOrigin: { process: processTransformOrigin }, diff --git a/packages/react-native-reanimated/src/propsAllowlists.ts b/packages/react-native-reanimated/src/propsAllowlists.ts index d3b9d789908f..d2b916bc7679 100644 --- a/packages/react-native-reanimated/src/propsAllowlists.ts +++ b/packages/react-native-reanimated/src/propsAllowlists.ts @@ -22,6 +22,8 @@ export const PropsAllowlists: AllowlistsHolder = { /* ios styles */ shadowOpacity: true, shadowRadius: true, + outlineColor: true, + shadowColor: true, /* legacy android transform properties */ scaleX: true, scaleY: true, @@ -124,7 +126,6 @@ export const PropsAllowlists: AllowlistsHolder = { /* text color */ color: true, tintColor: true, - shadowColor: true, textShadowColor: true, placeholderTextColor: true, textDecorationColor: true, From 05ac6a0ea18de1cd4c9dfc65b6f46dedb8bc5d7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81opaci=C5=84ski?= Date: Tue, 28 Jan 2025 12:29:07 +0100 Subject: [PATCH 3/6] Some improvements to web navigator --- apps/common-app/src/App.tsx | 6 +----- apps/web-example/metro.config.js | 8 +------- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/apps/common-app/src/App.tsx b/apps/common-app/src/App.tsx index bf0fc836a0e3..9cf2edd81ccb 100644 --- a/apps/common-app/src/App.tsx +++ b/apps/common-app/src/App.tsx @@ -121,11 +121,7 @@ function useNavigationState() { >(); const updateNavigationState = useCallback((state?: NavigationState) => { - if (IS_WEB) { - localStorage.setItem(PERSISTENCE_KEY, JSON.stringify(state)); - } else { - AsyncStorage.setItem(PERSISTENCE_KEY, JSON.stringify(state)).catch(noop); - } + AsyncStorage.setItem(PERSISTENCE_KEY, JSON.stringify(state)).catch(noop); }, []); useEffect(() => { diff --git a/apps/web-example/metro.config.js b/apps/web-example/metro.config.js index fb9270229fc0..47c511c2d484 100644 --- a/apps/web-example/metro.config.js +++ b/apps/web-example/metro.config.js @@ -11,11 +11,10 @@ const escape = require('escape-string-regexp'); const projectRoot = __dirname; // This can be replaced with `find-yarn-workspace-root` const monorepoRoot = path.resolve(projectRoot, '../..'); -const libraryRoot = path.resolve(monorepoRoot, './packages/react-native-reanimated'); const config = getDefaultConfig(projectRoot); // 1. Watch all files within the monorepo -config.watchFolders = [monorepoRoot, libraryRoot]; +config.watchFolders = [monorepoRoot]; // 2. Let Metro know where to resolve packages and in what order // @ts-expect-error config.resolver.nodeModulesPaths = [ @@ -23,11 +22,6 @@ config.resolver.nodeModulesPaths = [ path.resolve(monorepoRoot, 'node_modules'), ]; -config.resolver.extraNodeModules = { - ...config.resolver.extraNodeModules, - 'react-native-reanimated': path.resolve(libraryRoot, 'src'), -}; - const hasReactNative = require.resolve('react-native/package.json', { paths: [projectRoot], }); From 9fc8dcc3175bcea8348ff153318ce26e75ed430a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81opaci=C5=84ski?= Date: Tue, 28 Jan 2025 14:01:26 +0100 Subject: [PATCH 4/6] Add remaining outline properties support --- .../apps/css/examples/animations/routes.ts | 17 ++++++ .../animatedProperties/appearance/index.ts | 2 + .../appearance/outlines/OutlineOffset.tsx | 55 ++++++++++++++++++ .../appearance/outlines/OutlineStyle.tsx | 58 +++++++++++++++++++ .../appearance/outlines/OutlineWidth.tsx | 55 ++++++++++++++++++ .../appearance/outlines/index.ts | 5 ++ .../CSS/config/PropertyInterpolatorsConfig.h | 5 ++ .../src/css/platform/native/config.ts | 6 +- .../src/propsAllowlists.ts | 4 ++ 9 files changed, 204 insertions(+), 3 deletions(-) create mode 100644 apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/outlines/OutlineOffset.tsx create mode 100644 apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/outlines/OutlineStyle.tsx create mode 100644 apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/outlines/OutlineWidth.tsx create mode 100644 apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/outlines/index.ts diff --git a/apps/common-app/src/apps/css/examples/animations/routes.ts b/apps/common-app/src/apps/css/examples/animations/routes.ts index 3a1929483308..8016f438a028 100644 --- a/apps/common-app/src/apps/css/examples/animations/routes.ts +++ b/apps/common-app/src/apps/css/examples/animations/routes.ts @@ -248,6 +248,23 @@ const appearanceRoutes = { }, }, }, + Outlines: { + name: 'Outlines', + routes: { + OutlineOffset: { + name: 'Outline Offset', + Component: animatedProperties.appearance.outlines.OutlineOffset, + }, + OutlineStyle: { + name: 'Outline Style', + Component: animatedProperties.appearance.outlines.OutlineStyle, + }, + OutlineWidth: { + name: 'Outline Width', + Component: animatedProperties.appearance.outlines.OutlineWidth, + }, + }, + }, Transforms: { name: 'Transforms', flatten: true, diff --git a/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/index.ts b/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/index.ts index eea20d8d6bf1..d5263272247f 100644 --- a/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/index.ts +++ b/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/index.ts @@ -1,6 +1,7 @@ import borders from './borders'; import colors from './colors'; import others from './others'; +import outlines from './outlines'; import shadows from './shadows'; import transforms from './transforms'; @@ -8,6 +9,7 @@ export default { borders, colors, others, + outlines, shadows, transforms, }; diff --git a/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/outlines/OutlineOffset.tsx b/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/outlines/OutlineOffset.tsx new file mode 100644 index 000000000000..2fb5fe04061b --- /dev/null +++ b/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/outlines/OutlineOffset.tsx @@ -0,0 +1,55 @@ +import { StyleSheet } from 'react-native'; +import type { CSSAnimationKeyframes } from 'react-native-reanimated'; +import Animated from 'react-native-reanimated'; + +import { ExamplesScreen, VerticalExampleCard } from '@/apps/css/components'; +import { colors, radius, sizes, spacing } from '@/theme'; + +export default function OutlineOffset() { + return ( + + CardComponent={VerticalExampleCard} + buildAnimation={({ keyframes }) => ({ + animationDirection: 'alternate', + animationDuration: '1s', + animationIterationCount: 'infinite', + animationName: keyframes, + animationTimingFunction: 'linear', + })} + renderExample={({ animation }) => ( + + )} + sections={[ + { + examples: [ + { + keyframes: { + from: { + outlineOffset: 0, + }, + to: { + outlineOffset: spacing.md, + }, + }, + title: 'Changing Outline Offset', + }, + ], + title: 'Outline Offset', + }, + ]} + /> + ); +} + +const styles = StyleSheet.create({ + box: { + backgroundColor: colors.primary, + borderColor: colors.primaryDark, + borderRadius: radius.sm, + height: sizes.xl, + outlineColor: colors.primaryDark, + outlineStyle: 'solid', + outlineWidth: spacing.xxs, + width: sizes.xl, + }, +}); diff --git a/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/outlines/OutlineStyle.tsx b/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/outlines/OutlineStyle.tsx new file mode 100644 index 000000000000..76e8654ae4cf --- /dev/null +++ b/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/outlines/OutlineStyle.tsx @@ -0,0 +1,58 @@ +import { StyleSheet } from 'react-native'; +import type { CSSAnimationKeyframes } from 'react-native-reanimated'; +import Animated from 'react-native-reanimated'; + +import { ExamplesScreen, VerticalExampleCard } from '@/apps/css/components'; +import { colors, radius, sizes, spacing } from '@/theme'; + +export default function OutlineStyle() { + return ( + + CardComponent={VerticalExampleCard} + buildAnimation={({ keyframes }) => ({ + animationDuration: '3s', + animationIterationCount: 'infinite', + animationName: keyframes, + animationTimingFunction: 'linear', + })} + renderExample={({ animation }) => ( + + )} + sections={[ + { + examples: [ + { + description: + "`outlineStyle` is a **discrete** property. That means, it **can't be smoothly animated** between values. However, we can still change this property in the animation keyframes but the change will be **abrupt**.", + keyframes: { + '0%, 100%': { + outlineStyle: 'solid', + }, + '33.3%': { + outlineStyle: 'dotted', + }, + '66.6%': { + outlineStyle: 'dashed', + }, + }, + title: 'Changing Outline Style', + }, + ], + title: 'Outline Style', + }, + ]} + /> + ); +} + +const styles = StyleSheet.create({ + box: { + backgroundColor: colors.primary, + borderColor: colors.primaryDark, + borderRadius: radius.sm, + height: sizes.xl, + outlineColor: colors.primaryDark, + outlineWidth: spacing.xxs, + width: sizes.xl, + }, +}); diff --git a/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/outlines/OutlineWidth.tsx b/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/outlines/OutlineWidth.tsx new file mode 100644 index 000000000000..794291209cad --- /dev/null +++ b/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/outlines/OutlineWidth.tsx @@ -0,0 +1,55 @@ +import { StyleSheet } from 'react-native'; +import type { CSSAnimationKeyframes } from 'react-native-reanimated'; +import Animated from 'react-native-reanimated'; + +import { ExamplesScreen, VerticalExampleCard } from '@/apps/css/components'; +import { colors, radius, sizes, spacing } from '@/theme'; + +export default function OutlineWidth() { + return ( + + CardComponent={VerticalExampleCard} + buildAnimation={({ keyframes }) => ({ + animationDirection: 'alternate', + animationDuration: '1s', + animationIterationCount: 'infinite', + animationName: keyframes, + animationTimingFunction: 'linear', + })} + renderExample={({ animation }) => ( + + )} + sections={[ + { + examples: [ + { + keyframes: { + from: { + outlineWidth: 0, + }, + to: { + outlineWidth: spacing.md, + }, + }, + title: 'Changing Outline Width', + }, + ], + title: 'Outline Width', + }, + ]} + /> + ); +} + +const styles = StyleSheet.create({ + box: { + backgroundColor: colors.primary, + borderColor: colors.primaryDark, + borderRadius: radius.sm, + height: sizes.xl, + outlineColor: colors.primaryDark, + outlineStyle: 'solid', + outlineWidth: spacing.xxs, + width: sizes.xl, + }, +}); diff --git a/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/outlines/index.ts b/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/outlines/index.ts new file mode 100644 index 000000000000..103d169de429 --- /dev/null +++ b/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/outlines/index.ts @@ -0,0 +1,5 @@ +import OutlineOffset from './OutlineOffset'; +import OutlineStyle from './OutlineStyle'; +import OutlineWidth from './OutlineWidth'; + +export default { OutlineOffset, OutlineStyle, OutlineWidth }; diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/config/PropertyInterpolatorsConfig.h b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/config/PropertyInterpolatorsConfig.h index ccb746ee0ed4..d47594ad66dd 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/config/PropertyInterpolatorsConfig.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/config/PropertyInterpolatorsConfig.h @@ -227,6 +227,11 @@ const InterpolatorFactoriesRecord PROPERTY_INTERPOLATORS_CONFIG = []() { // Decoration {"borderStyle", value("solid")}, + // OUTLINES + {"outlineOffset", value(0)}, + {"outlineStyle", value("solid")}, + {"outlineWidth", value(0)}, + // TRANSFORMS {"transformOrigin", array( diff --git a/packages/react-native-reanimated/src/css/platform/native/config.ts b/packages/react-native-reanimated/src/css/platform/native/config.ts index 8aab9877b24f..9d685ee8a969 100644 --- a/packages/react-native-reanimated/src/css/platform/native/config.ts +++ b/packages/react-native-reanimated/src/css/platform/native/config.ts @@ -177,9 +177,9 @@ export const PROPERTIES_CONFIG: StyleBuilderConfig = { borderStyle: true, // OUTLINES - outlineOffset: false, // TODO - outlineStyle: false, // TODO - outlineWidth: false, // TODO + outlineOffset: true, + outlineStyle: true, + outlineWidth: true, // TRANSFORMS transformOrigin: { process: processTransformOrigin }, diff --git a/packages/react-native-reanimated/src/propsAllowlists.ts b/packages/react-native-reanimated/src/propsAllowlists.ts index d2b916bc7679..341d347b13b7 100644 --- a/packages/react-native-reanimated/src/propsAllowlists.ts +++ b/packages/react-native-reanimated/src/propsAllowlists.ts @@ -29,6 +29,10 @@ export const PropsAllowlists: AllowlistsHolder = { scaleY: true, translateX: true, translateY: true, + outlineStyle: true, + outlineWidth: true, + outlineOffset: true, + borderStyle: true, }, /** * Whitelist of view props that can be updated in native thread via From b703a1fb3b53e18e9fff47f2f7f8659245149d4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81opaci=C5=84ski?= Date: Tue, 28 Jan 2025 14:16:39 +0100 Subject: [PATCH 5/6] Add mixBlendMode support --- .../apps/css/examples/animations/routes.ts | 4 + .../appearance/others/MixBlendMode.tsx | 110 ++++++++++++++++++ .../appearance/others/index.ts | 2 + .../others/image/ResizeMode.tsx | 3 +- .../CSS/config/PropertyInterpolatorsConfig.h | 1 + .../src/css/platform/native/config.ts | 2 +- .../src/css/platform/web/config.ts | 2 +- .../src/propsAllowlists.ts | 1 + 8 files changed, 121 insertions(+), 4 deletions(-) create mode 100644 apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/others/MixBlendMode.tsx diff --git a/apps/common-app/src/apps/css/examples/animations/routes.ts b/apps/common-app/src/apps/css/examples/animations/routes.ts index 8016f438a028..bbcbcb3b690d 100644 --- a/apps/common-app/src/apps/css/examples/animations/routes.ts +++ b/apps/common-app/src/apps/css/examples/animations/routes.ts @@ -333,6 +333,10 @@ const appearanceRoutes = { name: 'Backface Visibility', Component: animatedProperties.appearance.others.BackfaceVisibility, }, + MixBlendMode: { + name: 'Mix Blend Mode', + Component: animatedProperties.appearance.others.MixBlendMode, + }, }, }, } satisfies Routes; diff --git a/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/others/MixBlendMode.tsx b/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/others/MixBlendMode.tsx new file mode 100644 index 000000000000..e31b6c9b4848 --- /dev/null +++ b/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/others/MixBlendMode.tsx @@ -0,0 +1,110 @@ +import { StyleSheet, View } from 'react-native'; +import type { CSSAnimationKeyframes } from 'react-native-reanimated'; +import Animated from 'react-native-reanimated'; + +import { balloonsImage } from '@/apps/css/assets'; +import { ExamplesScreen, VerticalExampleCard } from '@/apps/css/components'; +import { colors, radius, sizes } from '@/theme'; + +export default function MixBlendMode() { + return ( + + CardComponent={VerticalExampleCard} + buildAnimation={({ keyframes }) => ({ + animationDirection: 'alternate', + animationDuration: '14s', + animationIterationCount: 'infinite', + animationName: keyframes, + animationTimingFunction: 'linear', + })} + renderExample={({ animation }) => ( + + + + + )} + sections={[ + { + examples: [ + { + description: + '`mix-blend-mode` is a **continuous** property. That means, it **can be smoothly animated** between values.', + keyframes: { + '0%, 100%': { + mixBlendMode: 'normal', + }, + '6.67%': { + mixBlendMode: 'multiply', + }, + '13.33%': { + mixBlendMode: 'screen', + }, + '20%': { + mixBlendMode: 'overlay', + }, + '26.67%': { + mixBlendMode: 'darken', + }, + '33.33%': { + mixBlendMode: 'lighten', + }, + '40%': { + mixBlendMode: 'color-dodge', + }, + '46.67%': { + mixBlendMode: 'color-burn', + }, + '53.33%': { + mixBlendMode: 'hard-light', + }, + '60%': { + mixBlendMode: 'soft-light', + }, + '66.67%': { + mixBlendMode: 'difference', + }, + '73.33%': { + mixBlendMode: 'exclusion', + }, + '80%': { + mixBlendMode: 'hue', + }, + '86.67%': { + mixBlendMode: 'saturation', + }, + '93.33%': { + mixBlendMode: 'color', + }, + '100%': { + mixBlendMode: 'luminosity', + }, + }, + minExampleHeight: 1.5 * sizes.xxxl, + title: 'Changing Mix Blend Mode', + }, + ], + title: 'Mix Blend Mode', + }, + ]} + /> + ); +} + +const styles = StyleSheet.create({ + box: { + backgroundColor: colors.primary, + borderRadius: radius.md, + height: sizes.xxxl, + position: 'absolute', + transform: [{ translateX: '-15%' }, { translateY: '-15%' }], + width: sizes.xxxl, + }, + image: { + borderRadius: radius.md, + height: sizes.xxxl, + width: sizes.xxxl, + }, +}); diff --git a/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/others/index.ts b/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/others/index.ts index 5555398dc893..0091a6f14e46 100644 --- a/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/others/index.ts +++ b/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/appearance/others/index.ts @@ -1,7 +1,9 @@ import BackfaceVisibility from './BackfaceVisibility'; +import MixBlendMode from './MixBlendMode'; import Opacity from './Opacity'; export default { BackfaceVisibility, + MixBlendMode, Opacity, }; diff --git a/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/others/image/ResizeMode.tsx b/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/others/image/ResizeMode.tsx index 6a421ff5916f..fb8fe389cce2 100644 --- a/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/others/image/ResizeMode.tsx +++ b/apps/common-app/src/apps/css/examples/animations/screens/animatedProperties/others/image/ResizeMode.tsx @@ -4,7 +4,7 @@ import Animated from 'react-native-reanimated'; import { balloonsImage } from '@/apps/css/assets'; import { ExamplesScreen, VerticalExampleCard } from '@/apps/css/components'; -import { colors, radius, sizes } from '@/theme'; +import { radius, sizes } from '@/theme'; export default function ResizeMode() { return ( @@ -55,7 +55,6 @@ export default function ResizeMode() { } const styles = StyleSheet.create({ image: { - backgroundColor: colors.primaryLight, borderRadius: radius.md, height: sizes.xxxl, width: sizes.xxxl, diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/config/PropertyInterpolatorsConfig.h b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/config/PropertyInterpolatorsConfig.h index d47594ad66dd..d3ffbe83fa93 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/config/PropertyInterpolatorsConfig.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/config/PropertyInterpolatorsConfig.h @@ -261,6 +261,7 @@ const InterpolatorFactoriesRecord PROPERTY_INTERPOLATORS_CONFIG = []() { // OTHERS {"backfaceVisibility", value("visible")}, {"opacity", value(1)}, + {"mixBlendMode", value("normal")}, /** * Typography diff --git a/packages/react-native-reanimated/src/css/platform/native/config.ts b/packages/react-native-reanimated/src/css/platform/native/config.ts index 9d685ee8a969..ace61000c471 100644 --- a/packages/react-native-reanimated/src/css/platform/native/config.ts +++ b/packages/react-native-reanimated/src/css/platform/native/config.ts @@ -194,7 +194,7 @@ export const PROPERTIES_CONFIG: StyleBuilderConfig = { // OTHERS backfaceVisibility: true, opacity: true, - mixBlendMode: false, // TODO + mixBlendMode: true, experimental_backgroundImage: false, // TODO /** Typography */ diff --git a/packages/react-native-reanimated/src/css/platform/web/config.ts b/packages/react-native-reanimated/src/css/platform/web/config.ts index 31ba459ef9b7..dd4ab4c264f7 100644 --- a/packages/react-native-reanimated/src/css/platform/web/config.ts +++ b/packages/react-native-reanimated/src/css/platform/web/config.ts @@ -195,7 +195,7 @@ export const PROPERTIES_CONFIG: StyleBuilderConfig = { // OTHERS backfaceVisibility: true, opacity: true, - mixBlendMode: false, // TODO + mixBlendMode: true, experimental_backgroundImage: false, // TODO /** Typography */ diff --git a/packages/react-native-reanimated/src/propsAllowlists.ts b/packages/react-native-reanimated/src/propsAllowlists.ts index 341d347b13b7..f5fa7c56fa8b 100644 --- a/packages/react-native-reanimated/src/propsAllowlists.ts +++ b/packages/react-native-reanimated/src/propsAllowlists.ts @@ -33,6 +33,7 @@ export const PropsAllowlists: AllowlistsHolder = { outlineWidth: true, outlineOffset: true, borderStyle: true, + mixBlendMode: true, }, /** * Whitelist of view props that can be updated in native thread via From b61d74a9f5fc4ac7d959842b56640db2005f2ba8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=81opaci=C5=84ski?= Date: Tue, 28 Jan 2025 14:29:00 +0100 Subject: [PATCH 6/6] Cleanup --- .../cpp/reanimated/CSS/registry/CSSAnimationsRegistry.cpp | 5 ----- packages/react-native-reanimated/src/propsAllowlists.ts | 1 - 2 files changed, 6 deletions(-) diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/registry/CSSAnimationsRegistry.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/registry/CSSAnimationsRegistry.cpp index 291439c72538..8e4282f02718 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/CSS/registry/CSSAnimationsRegistry.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/CSS/registry/CSSAnimationsRegistry.cpp @@ -1,11 +1,8 @@ #ifdef RCT_NEW_ARCH_ENABLED #include -#include namespace reanimated { -using namespace worklets; - bool CSSAnimationsRegistry::hasUpdates() const { return !runningAnimationsMap_.empty() || !delayedAnimationsManager_.empty(); } @@ -117,8 +114,6 @@ void CSSAnimationsRegistry::updateViewAnimations( const auto updates = animation->update(rt, timestamp); const auto newState = animation->getState(timestamp); - LOG(INFO) << "updates: " << stringifyJSIValue(rt, updates); - if (newState == AnimationProgressState::Finished) { // Revert changes applied during animation if there is no forwards fill // mode diff --git a/packages/react-native-reanimated/src/propsAllowlists.ts b/packages/react-native-reanimated/src/propsAllowlists.ts index f5fa7c56fa8b..2e6d5a963f29 100644 --- a/packages/react-native-reanimated/src/propsAllowlists.ts +++ b/packages/react-native-reanimated/src/propsAllowlists.ts @@ -96,7 +96,6 @@ export const PropsAllowlists: AllowlistsHolder = { textShadowOffset: true, letterSpacing: true, aspectRatio: true, - boxSizing: true, columnGap: true, // iOS only end: true, // number or string flexBasis: true, // number or string