mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-07-09 03:04:20 -04:00
Rework skeleton to use reanimated instead of moti
This commit is contained in:
parent
6458f419aa
commit
26a3c2c984
@ -19,12 +19,20 @@
|
||||
*/
|
||||
|
||||
import { LinearGradient as LG } from "expo-linear-gradient";
|
||||
import { MotiView, motify } from "moti";
|
||||
import { useState } from "react";
|
||||
import { Platform, View, type ViewProps } from "react-native";
|
||||
import { memo, useEffect } from "react";
|
||||
import { Platform, StyleSheet, View, type ViewProps } from "react-native";
|
||||
import Animated, {
|
||||
SharedValue,
|
||||
useAnimatedStyle,
|
||||
useDerivedValue,
|
||||
useSharedValue,
|
||||
withDelay,
|
||||
withRepeat,
|
||||
withTiming,
|
||||
} from "react-native-reanimated";
|
||||
import { em, percent, px, rem, useYoshiki } from "yoshiki/native";
|
||||
|
||||
const LinearGradient = motify(LG)();
|
||||
const LinearGradient = Animated.createAnimatedComponent(LG);
|
||||
|
||||
export const SkeletonCss = () => (
|
||||
<style jsx global>{`
|
||||
@ -42,6 +50,39 @@ export const SkeletonCss = () => (
|
||||
`}</style>
|
||||
);
|
||||
|
||||
const AnimatedGradient = memo(
|
||||
function Gradient({ color, width }: { color: string; width: SharedValue<number> }) {
|
||||
const mult = useSharedValue(-1);
|
||||
const animated = useAnimatedStyle(() => ({
|
||||
transform: [
|
||||
{
|
||||
translateX: width.value * mult.value,
|
||||
},
|
||||
],
|
||||
}));
|
||||
|
||||
useEffect(() => {
|
||||
mult.value = withRepeat(withDelay(800, withTiming(1, { duration: 800 })), 0);
|
||||
});
|
||||
|
||||
return (
|
||||
<LinearGradient
|
||||
start={{ x: 0, y: 0.5 }}
|
||||
end={{ x: 1, y: 0.5 }}
|
||||
colors={["transparent", color, "transparent"]}
|
||||
style={[
|
||||
StyleSheet.absoluteFillObject,
|
||||
{ transform: [{ translateX: -width.value }] },
|
||||
animated,
|
||||
]}
|
||||
/>
|
||||
);
|
||||
},
|
||||
function propsAreEqual(prev, next) {
|
||||
return prev.color === next.color;
|
||||
},
|
||||
);
|
||||
|
||||
export const Skeleton = ({
|
||||
children,
|
||||
show: forcedShow,
|
||||
@ -55,8 +96,7 @@ export const Skeleton = ({
|
||||
variant?: "text" | "header" | "round" | "custom" | "fill" | "filltext";
|
||||
}) => {
|
||||
const { css, theme } = useYoshiki();
|
||||
const [width, setWidth] = useState<number | undefined>(undefined);
|
||||
const perc = (v: number) => (v / 100) * width!;
|
||||
const width = useSharedValue(0);
|
||||
|
||||
if (forcedShow === undefined && children && children !== true) return <>{children}</>;
|
||||
|
||||
@ -100,13 +140,11 @@ export const Skeleton = ({
|
||||
>
|
||||
{(forcedShow || !children || children === true) &&
|
||||
[...Array(lines)].map((_, i) => (
|
||||
<MotiView
|
||||
<View
|
||||
key={`skeleton_${i}`}
|
||||
// No clue why it is a number on mobile and a string on web but /shrug
|
||||
animate={{ opacity: Platform.OS === "web" ? ("1" as any) : 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
transition={{ type: "timing" }}
|
||||
onLayout={(e) => setWidth(e.nativeEvent.layout.width)}
|
||||
onLayout={(e) => {
|
||||
width.value = e.nativeEvent.layout.width;
|
||||
}}
|
||||
{...css([
|
||||
{
|
||||
bg: (theme) => theme.overlay0,
|
||||
@ -127,28 +165,8 @@ export const Skeleton = ({
|
||||
},
|
||||
])}
|
||||
>
|
||||
<LinearGradient
|
||||
start={{ x: 0, y: 0.5 }}
|
||||
end={{ x: 1, y: 0.5 }}
|
||||
colors={["transparent", theme.overlay1, "transparent"]}
|
||||
transition={{
|
||||
loop: true,
|
||||
repeatReverse: false,
|
||||
}}
|
||||
animate={{
|
||||
translateX: width
|
||||
? [perc(-100), { value: perc(100), type: "timing", duration: 800, delay: 800 }]
|
||||
: undefined,
|
||||
}}
|
||||
{...css({
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
})}
|
||||
/>
|
||||
</MotiView>
|
||||
<AnimatedGradient color={theme.overlay1} width={width} />
|
||||
</View>
|
||||
))}
|
||||
{children}
|
||||
</View>
|
||||
|
Loading…
x
Reference in New Issue
Block a user