Skip to content

Instantly share code, notes, and snippets.

@kharioki
Forked from EvanBacon/skeleton.tsx
Created October 28, 2024 13:40
Show Gist options
  • Save kharioki/94b074c0a9892b5f413ddfefea2a1690 to your computer and use it in GitHub Desktop.
Save kharioki/94b074c0a9892b5f413ddfefea2a1690 to your computer and use it in GitHub Desktop.
Animated skeleton component with Expo SDK 52
"use client";
import React from "react";
import { View, StyleSheet, Animated, Easing, ViewStyle } from "react-native";
const BASE_COLORS = {
dark: { primary: "rgb(17, 17, 17)", secondary: "rgb(51, 51, 51)" },
light: {
primary: "rgb(250, 250, 250)",
secondary: "rgb(205, 205, 205)",
},
} as const;
const makeColors = (mode: keyof typeof BASE_COLORS) => [
BASE_COLORS[mode].primary,
BASE_COLORS[mode].secondary,
BASE_COLORS[mode].secondary,
BASE_COLORS[mode].primary,
BASE_COLORS[mode].secondary,
BASE_COLORS[mode].primary,
];
const DARK_COLORS = new Array(3)
.fill(0)
.map(() => makeColors("dark"))
.flat();
const LIGHT_COLORS = new Array(3)
.fill(0)
.map(() => makeColors("light"))
.flat();
const Skeleton = ({
style,
delay,
dark,
}: {
style?: ViewStyle;
delay?: number;
dark?: boolean;
} = {}) => {
const translateX = React.useRef(new Animated.Value(-1)).current;
const [width, setWidth] = React.useState(150);
const colors = dark ? DARK_COLORS : LIGHT_COLORS;
const targetRef = React.useRef<View>(null);
const onLayout = React.useCallback(() => {
targetRef.current?.measureInWindow((_x, _y, width, _height) => {
setWidth(width);
});
}, []);
React.useEffect(() => {
Animated.loop(
Animated.sequence([
Animated.timing(translateX, {
delay: delay || 0,
toValue: 1,
duration: 5000,
useNativeDriver: process.env.EXPO_OS !== "web",
// Ease in
easing: Easing.in(Easing.ease),
}),
])
).start();
}, [translateX]);
const translateXStyle = React.useMemo(
() => ({
transform: [
{
translateX: translateX.interpolate({
inputRange: [-1, 1],
outputRange: [-width * 8, width],
}),
},
],
}),
[translateX, width]
);
return (
<View
ref={targetRef}
style={[
{
height: 32,
borderRadius: 8,
borderCurve: "continuous",
overflow: "hidden",
backgroundColor: "transparent",
},
style,
]}
onLayout={onLayout}
>
<Animated.View
style={[
translateXStyle,
{ width: "800%", height: "100%", backgroundColor: "transparent" },
]}
>
<Animated.View
style={[
StyleSheet.absoluteFill,
{
[process.env.EXPO_OS === "web"
? `backgroundImage`
: `experimental_backgroundImage`]: `linear-gradient(to right, ${colors.join(
", "
)})`,
},
]}
/>
</Animated.View>
</View>
);
};
export default Skeleton;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment