Created
May 26, 2022 09:37
-
-
Save BubbleTrouble14/7d9d096b100677e77577ee9f01c6fb2a to your computer and use it in GitHub Desktop.
Something simliar to react-native-shadow-2 but using Shopify/react-native-skia. Not sure if the performance is better then react-native-svg but think so.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
Canvas, | |
Circle, | |
Paint, | |
RoundedRect, | |
Shadow as SkiaShadow, | |
useValue, | |
} from '@shopify/react-native-skia'; | |
import PropTypes from 'prop-types'; | |
import React, { useMemo, useState } from 'react'; | |
import { Pressable, StyleSheet } from 'react-native'; | |
import { View } from 'react-native-ui-lib'; | |
const isNegative = (val) => val < 0; | |
const ShadowView = ({ | |
shadows, | |
showShadow = true, | |
radius = 24, | |
backgroundColor = 'white', | |
underlayColor = '#dbd9d9', | |
pressable = false, | |
style, | |
onPress, | |
containerStyle, | |
strokeWidth = 0, | |
strokeColor = 'black', | |
circle = false, | |
children, | |
}) => { | |
const [dimensions, setDimensions] = useState(); | |
const colorBackground = useValue(backgroundColor); | |
let left = 0; | |
let top = 0; | |
let right = 0; | |
let bottom = 0; | |
if (!shadows) { | |
throw new Error('No Shadow Set'); | |
} | |
shadows.forEach((item) => { | |
if (item.inner) { | |
return; | |
} | |
if (item.dx === 0) { | |
left = left < item.blur ? item.blur : left; | |
right = right < item.blur ? item.blur : right; | |
} else { | |
if (isNegative(item.dx)) { | |
left = Math.abs(item.dx) + item.blur > left ? Math.abs(item.dx) + item.blur : left; | |
} else { | |
right = item.dx + item.blur > right ? item.dx + item.blur : right; | |
} | |
} | |
if (item.dy === 0) { | |
top = top < item.blur ? item.blur : top; | |
bottom = bottom < item.blur ? item.blur : bottom; | |
} else { | |
if (isNegative(item.dy)) { | |
top = Math.abs(item.dy) + item.blur > top ? Math.abs(item.dy) + item.blur : top; | |
} else { | |
bottom = item.dy + item.blur > bottom ? item.dy + item.blur : bottom; | |
} | |
} | |
}); | |
const shadow = useMemo(() => { | |
if (!dimensions) { | |
return; | |
} | |
return ( | |
<Canvas style={{ flex: 1 }}> | |
{circle ? ( | |
<Circle | |
cx={left * 2 + dimensions.width / 2} | |
cy={top * 2 + dimensions.width / 2} | |
r={dimensions.width / 2 - strokeWidth / 2} | |
color={colorBackground}> | |
<Paint color={colorBackground} /> | |
{strokeWidth > 0 && ( | |
<Paint color={strokeColor} style="stroke" strokeWidth={strokeWidth} /> | |
)} | |
{shadows.map(({ dx, dy, blur, color, inner }, index) => { | |
if (!showShadow) { | |
return null; | |
} | |
return ( | |
<SkiaShadow key={index} dx={dx} dy={dy} blur={blur} color={color} inner={inner} /> | |
); | |
})} | |
</Circle> | |
) : ( | |
<RoundedRect | |
x={left * 2} | |
y={top * 2} | |
width={dimensions.width} | |
height={dimensions.height} | |
r={radius} | |
color={colorBackground}> | |
{strokeWidth > 0 && ( | |
<Paint color={strokeColor} style="stroke" strokeWidth={strokeWidth} /> | |
)} | |
{shadows.map(({ dx, dy, blur, color, inner }, index) => { | |
if (!showShadow) { | |
return null; | |
} | |
return ( | |
<SkiaShadow key={index} dx={dx} dy={dy} blur={blur} color={color} inner={inner} /> | |
); | |
})} | |
</RoundedRect> | |
)} | |
</Canvas> | |
); | |
}, [ | |
dimensions, | |
circle, | |
left, | |
radius, | |
colorBackground, | |
shadows, | |
strokeColor, | |
top, | |
strokeWidth, | |
showShadow, | |
]); | |
return ( | |
<View style={style}> | |
<View | |
style={[ | |
{ | |
...StyleSheet.absoluteFillObject, | |
left: -left * 2, | |
top: -top * 2, | |
right: -right * 2, | |
bottom: -bottom * 2, | |
}, | |
]}> | |
{shadow} | |
</View> | |
<Pressable | |
onPress={onPress} | |
onPressIn={() => (colorBackground.current = underlayColor)} | |
onPressOut={() => (colorBackground.current = backgroundColor)} | |
disabled={!pressable} | |
style={[ | |
// { alignSelf: 'flex-start', alignItems: 'center', justifyContent: 'center' }, | |
containerStyle, | |
]} | |
onLayout={(e) => { | |
const layout = e.nativeEvent.layout; | |
setDimensions({ width: layout.width, height: layout.height }); | |
}}> | |
{children} | |
</Pressable> | |
</View> | |
); | |
}; | |
ShadowView.propTypes = { | |
shadows: PropTypes.arrayOf(PropTypes.object), | |
radius: PropTypes.number, | |
backgroundColor: PropTypes.string, | |
showShadow: PropTypes.bool, | |
underlayColor: PropTypes.string, | |
pressable: PropTypes.bool, | |
style: PropTypes.object, | |
onPress: PropTypes.func, | |
containerStyle: PropTypes.object, | |
strokeWidth: PropTypes.number, | |
strokeColor: PropTypes.string, | |
circle: PropTypes.bool, | |
children: PropTypes.object, | |
}; | |
export default ShadowView; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment