Skip to content

Instantly share code, notes, and snippets.

@BubbleTrouble14
Created May 26, 2022 09:37
Show Gist options
  • Save BubbleTrouble14/7d9d096b100677e77577ee9f01c6fb2a to your computer and use it in GitHub Desktop.
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.
import { Canvas, Circle, useTouchHandler, useValue } from '@shopify/react-native-skia';
import PropTypes from 'prop-types';
import React from 'react';
import { StyleSheet } from 'react-native';
import { View } from 'react-native-ui-lib';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import ShadowView from './ShadowView';
const FloatingActionButton = ({
size = 42,
iconColor = '#404040',
backgroundColor = 'white',
onPress,
style,
showShadow,
children,
}) => {
const radius = size / 2;
return (
<ShadowView
style={style}
showShadow={showShadow}
containerStyle={{
alignItems: 'center',
justifyContent: 'center',
width: size,
height: size,
}}
backgroundColor={backgroundColor}
onPress={onPress}
pressable
circle
shadows={[
{
dx: 0,
dy: 0.8,
blur: 0.5,
color: '#bdbdbd',
},
]}>
{children ? (
typeof children === 'function' ? (
children({ size: radius, color: iconColor })
) : (
children
)
) : (
<MaterialCommunityIcons name="plus" size={radius} color={iconColor} />
)}
</ShadowView>
);
};
FloatingActionButton.propTypes = {
size: PropTypes.number,
iconColor: PropTypes.string,
showShadow: PropTypes.bool,
backgroundColor: PropTypes.string,
onPress: PropTypes.func,
style: PropTypes.object,
children: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),
};
export default FloatingActionButton;
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