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 { LinearGradient as LG } from "expo-linear-gradient";
|
||||||
import { MotiView, motify } from "moti";
|
import { memo, useEffect } from "react";
|
||||||
import { useState } from "react";
|
import { Platform, StyleSheet, View, type ViewProps } from "react-native";
|
||||||
import { Platform, 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";
|
import { em, percent, px, rem, useYoshiki } from "yoshiki/native";
|
||||||
|
|
||||||
const LinearGradient = motify(LG)();
|
const LinearGradient = Animated.createAnimatedComponent(LG);
|
||||||
|
|
||||||
export const SkeletonCss = () => (
|
export const SkeletonCss = () => (
|
||||||
<style jsx global>{`
|
<style jsx global>{`
|
||||||
@ -42,6 +50,39 @@ export const SkeletonCss = () => (
|
|||||||
`}</style>
|
`}</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 = ({
|
export const Skeleton = ({
|
||||||
children,
|
children,
|
||||||
show: forcedShow,
|
show: forcedShow,
|
||||||
@ -55,8 +96,7 @@ export const Skeleton = ({
|
|||||||
variant?: "text" | "header" | "round" | "custom" | "fill" | "filltext";
|
variant?: "text" | "header" | "round" | "custom" | "fill" | "filltext";
|
||||||
}) => {
|
}) => {
|
||||||
const { css, theme } = useYoshiki();
|
const { css, theme } = useYoshiki();
|
||||||
const [width, setWidth] = useState<number | undefined>(undefined);
|
const width = useSharedValue(0);
|
||||||
const perc = (v: number) => (v / 100) * width!;
|
|
||||||
|
|
||||||
if (forcedShow === undefined && children && children !== true) return <>{children}</>;
|
if (forcedShow === undefined && children && children !== true) return <>{children}</>;
|
||||||
|
|
||||||
@ -100,13 +140,11 @@ export const Skeleton = ({
|
|||||||
>
|
>
|
||||||
{(forcedShow || !children || children === true) &&
|
{(forcedShow || !children || children === true) &&
|
||||||
[...Array(lines)].map((_, i) => (
|
[...Array(lines)].map((_, i) => (
|
||||||
<MotiView
|
<View
|
||||||
key={`skeleton_${i}`}
|
key={`skeleton_${i}`}
|
||||||
// No clue why it is a number on mobile and a string on web but /shrug
|
onLayout={(e) => {
|
||||||
animate={{ opacity: Platform.OS === "web" ? ("1" as any) : 1 }}
|
width.value = e.nativeEvent.layout.width;
|
||||||
exit={{ opacity: 0 }}
|
}}
|
||||||
transition={{ type: "timing" }}
|
|
||||||
onLayout={(e) => setWidth(e.nativeEvent.layout.width)}
|
|
||||||
{...css([
|
{...css([
|
||||||
{
|
{
|
||||||
bg: (theme) => theme.overlay0,
|
bg: (theme) => theme.overlay0,
|
||||||
@ -127,28 +165,8 @@ export const Skeleton = ({
|
|||||||
},
|
},
|
||||||
])}
|
])}
|
||||||
>
|
>
|
||||||
<LinearGradient
|
<AnimatedGradient color={theme.overlay1} width={width} />
|
||||||
start={{ x: 0, y: 0.5 }}
|
</View>
|
||||||
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>
|
|
||||||
))}
|
))}
|
||||||
{children}
|
{children}
|
||||||
</View>
|
</View>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user