Open CavalcanteLeo opened 2 months ago
Thank you @CavalcanteLeo, clipping currently is partially supported, didn't try the Image component case. but overall the clipping method currently used is not the best and would love to revisit it later. will keep this open for others 🙏
<SquircleView cornerSmoothing={100} className="h-24 w-24 overflow-hidden bg-gray-850 rounded-lg" preserveSmoothing={false} borderRadius={20} borderWidth={10} borderColor={'red'} backgroundColor={'red'} > <Image source={item.logo} fallbackSource={fallbackImage} className="flex-1" /> </SquircleView>
I solved it with maskedView
import MaskedView from '@react-native-masked-view/masked-view';
import { requireNativeViewManager } from 'expo-modules-core';
import * as React from 'react';
import type {
AnimatableNumericValue,
ColorValue,
DimensionValue,
TouchableOpacityProps,
ViewProps,
} from 'react-native';
import { processColor, StyleSheet, TouchableOpacity } from 'react-native';
type SquircleProps = {
cornerSmoothing?: number;
borderRadius?: AnimatableNumericValue;
borderWidth?: number;
preserveSmoothing?: boolean;
enabledIOSAnimation?: boolean;
ignoreBorderWidthFromPadding?: boolean;
};
export type ExpoSquircleNativeViewProps = {
squircleBackgroundColor?: ReturnType<typeof processColor>;
squircleBorderColor?: ReturnType<typeof processColor>;
squircleBorderWidth?: number;
} & ViewProps &
SquircleProps;
export type SquircleViewProps = {
backgroundColor?: ColorValue;
borderColor?: ColorValue;
} & ViewProps &
SquircleProps;
export type SquircleButtonProps = {
backgroundColor?: ColorValue;
borderColor?: ColorValue;
} & TouchableOpacityProps &
SquircleProps;
const NativeView: React.ComponentType<ExpoSquircleNativeViewProps> =
requireNativeViewManager('ExpoSquircleView');
const ExpoSquircleViewNativeWrapper = (
props: React.PropsWithChildren<SquircleViewProps | SquircleButtonProps>,
) => {
const {
cornerSmoothing,
backgroundColor,
borderRadius,
borderColor,
borderWidth,
preserveSmoothing,
enabledIOSAnimation,
} = props;
return (
<NativeView
squircleBackgroundColor={processColor(backgroundColor)}
squircleBorderColor={processColor(borderColor)}
squircleBorderWidth={borderWidth}
borderRadius={borderRadius}
cornerSmoothing={cornerSmoothing}
preserveSmoothing={preserveSmoothing}
enabledIOSAnimation={enabledIOSAnimation}
style={StyleSheet.absoluteFill}
/>
);
};
export const SquircleButton = (
props: React.PropsWithChildren<SquircleButtonProps>,
) => {
const { children } = props;
const { squircleProps, wrapperStyle } = useSquircleProps(props);
return (
<TouchableOpacity {...props} style={wrapperStyle}>
<ExpoSquircleViewNativeWrapper {...squircleProps} />
{children}
</TouchableOpacity>
);
};
export const SquircleView = (
props: React.PropsWithChildren<SquircleViewProps>,
) => {
const { children } = props;
const { squircleProps, wrapperStyle } = useSquircleProps(props);
return (
<MaskedView
style={wrapperStyle}
maskElement={
<NativeView
squircleBackgroundColor={processColor('black')}
squircleBorderColor={processColor('black')}
borderRadius={squircleProps.borderRadius}
cornerSmoothing={squircleProps.cornerSmoothing}
preserveSmoothing={squircleProps.preserveSmoothing}
enabledIOSAnimation={squircleProps.enabledIOSAnimation}
style={StyleSheet.absoluteFill}
/>
}
>
{children}
</MaskedView>
);
};
const useSquircleProps = (props: SquircleViewProps | SquircleButtonProps) => {
const style = props.style ? StyleSheet.flatten(props.style) : undefined;
const {
cornerSmoothing,
borderRadius,
borderWidth,
backgroundColor,
borderColor,
ignoreBorderWidthFromPadding,
} = props;
const {
padding,
paddingVertical,
paddingHorizontal,
paddingBottom,
paddingEnd,
paddingLeft,
paddingRight,
paddingStart,
paddingTop,
} = style || {};
const calculatedPadding = React.useMemo(() => {
const extraPadding = borderWidth || style?.borderWidth || 0;
const calculatePadding = (_paddingValue: DimensionValue) => {
if (typeof _paddingValue === 'number') {
return _paddingValue + extraPadding;
}
return _paddingValue;
};
return {
padding: padding ? calculatePadding(padding) : extraPadding,
paddingVertical: paddingVertical
? calculatePadding(paddingVertical)
: undefined,
paddingHorizontal: paddingHorizontal
? calculatePadding(paddingHorizontal)
: undefined,
paddingBottom: paddingBottom
? calculatePadding(paddingBottom)
: undefined,
paddingEnd: paddingEnd ? calculatePadding(paddingEnd) : undefined,
paddingLeft: paddingLeft ? calculatePadding(paddingLeft) : undefined,
paddingRight: paddingRight ? calculatePadding(paddingRight) : undefined,
paddingStart: paddingStart ? calculatePadding(paddingStart) : undefined,
paddingTop: paddingTop ? calculatePadding(paddingTop) : undefined,
};
}, [
borderWidth,
style?.borderWidth,
padding,
paddingVertical,
paddingHorizontal,
paddingBottom,
paddingEnd,
paddingLeft,
paddingRight,
paddingStart,
paddingTop,
]);
return {
squircleProps: {
...props,
borderRadius: borderRadius || style?.borderRadius || 0,
borderWidth: borderWidth || style?.borderWidth || 0,
backgroundColor:
backgroundColor || style?.backgroundColor || 'transparent',
borderColor: borderColor || style?.borderColor || 'transparent',
cornerSmoothing: cornerSmoothing !== undefined ? cornerSmoothing : 100,
preserveSmoothing: props.preserveSmoothing || false,
enabledIOSAnimation: props.enabledIOSAnimation || false,
},
wrapperStyle: [
styles.container,
style,
{
borderWidth: undefined,
borderColor: undefined,
backgroundColor: undefined,
overflow: 'hidden' as const,
...(ignoreBorderWidthFromPadding === true
? undefined
: calculatedPadding),
},
],
};
};
const styles = StyleSheet.create({
container: { backgroundColor: 'transparent' },
});