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..bbcbcb3b690d 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; @@ -243,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, @@ -311,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/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, }} /> + + 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/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/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/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 2d50a20ceecf..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 @@ -143,6 +143,7 @@ const InterpolatorFactoriesRecord PROPERTY_INTERPOLATORS_CONFIG = []() { {"borderStartColor", value(BLACK)}, {"borderBlockColor", value(BLACK)}, // Other + {"outlineColor", value(BLACK)}, {"shadowColor", value(BLACK)}, {"overlayColor", value(BLACK)}, {"tintColor", value(BLACK)}, @@ -226,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( @@ -255,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 4ddf2f5e1332..ace61000c471 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 @@ -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, @@ -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 }, @@ -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 89b651c6156b..dd4ab4c264f7 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 @@ -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 }, @@ -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 68c079cb450e..2e6d5a963f29 100644 --- a/packages/react-native-reanimated/src/propsAllowlists.ts +++ b/packages/react-native-reanimated/src/propsAllowlists.ts @@ -22,11 +22,18 @@ export const PropsAllowlists: AllowlistsHolder = { /* ios styles */ shadowOpacity: true, shadowRadius: true, + outlineColor: true, + shadowColor: true, /* legacy android transform properties */ scaleX: true, scaleY: true, translateX: true, translateY: true, + outlineStyle: true, + outlineWidth: true, + outlineOffset: true, + borderStyle: true, + mixBlendMode: true, }, /** * Whitelist of view props that can be updated in native thread via @@ -123,7 +130,6 @@ export const PropsAllowlists: AllowlistsHolder = { /* text color */ color: true, tintColor: true, - shadowColor: true, textShadowColor: true, placeholderTextColor: true, textDecorationColor: true,