Skip to content

Instantly share code, notes, and snippets.

@tchayen
Created August 18, 2020 14:16
Show Gist options
  • Save tchayen/8ad67faa629ce21bf1c754908d1db3a6 to your computer and use it in GitHub Desktop.
Save tchayen/8ad67faa629ce21bf1c754908d1db3a6 to your computer and use it in GitHub Desktop.
import React, {useState} from 'react';
import Animated, {
useAnimatedScrollHandler,
useSharedValue,
useAnimatedStyle,
withTiming,
} from 'react-native-reanimated';
import {Image, Pressable, Dimensions} from 'react-native';
import FastImage from 'react-native-fast-image';
const stories = [
{
avatar: 'https://api.adorable.io/avatars/24/1',
nickname: 'test',
pictures: [
'https://unsplash.com/photos/ICAZVQNBPnQ/download?force=true&w=640',
'https://unsplash.com/photos/TW_5JjAKaPc/download?force=true&w=640',
],
},
{
avatar: 'https://api.adorable.io/avatars/24/2',
nickname: 'other',
pictures: [
'https://unsplash.com/photos/ZtORJBpQljA/download?force=true&w=640',
],
},
{
avatar: 'https://api.adorable.io/avatars/24/3',
nickname: 'next',
pictures: [
'https://unsplash.com/photos/zMDBwRBZKQI/download?force=true&w=640',
],
},
{
avatar: 'https://api.adorable.io/avatars/24/4',
nickname: 'next',
pictures: [],
},
{
avatar: 'https://api.adorable.io/avatars/24/5',
nickname: 'next',
pictures: [],
},
];
const WIDTH = Dimensions.get('screen').width;
const HEIGHT = Dimensions.get('screen').height;
const AVATAR_MARGIN = 16;
const AVATAR_SIZE = 64;
const Dismiss3 = () => {
const openingAnimation = useSharedValue(0);
const [selectedAvatar, setSelectedAvatar] = useState(-1);
const selectedAvatarA = useSharedValue(-1);
const x = useSharedValue(0);
const scrollHandler = useAnimatedScrollHandler((event) => {
x.value = event.contentOffset.x;
});
const storyStyle = useAnimatedStyle(() => {
return {
opacity: selectedAvatarA.value === -1 ? 0 : 1,
backgroundColor: `rgba(0, 0, 0, ${openingAnimation.value})`,
};
});
const backgroundStyle = useAnimatedStyle(() => {
const left =
-x.value +
(AVATAR_SIZE + AVATAR_MARGIN) * selectedAvatarA.value +
AVATAR_MARGIN;
return {
transform: [
{translateX: left - WIDTH / 2},
{translateY: -HEIGHT / 2},
{scale: 0.2 + 0.8 * openingAnimation.value},
{translateX: -(left - WIDTH / 2)},
{translateY: HEIGHT / 2},
{translateX: left - openingAnimation.value * left},
],
};
});
const sharedAvatarStyle = useAnimatedStyle(() => {
const left =
-x.value +
(AVATAR_SIZE + AVATAR_MARGIN) * selectedAvatarA.value +
AVATAR_MARGIN;
const margin = 16;
return {
transform: [
{translateX: -AVATAR_SIZE / 2},
{translateY: -AVATAR_SIZE / 2},
{scale: 1 - 0.4 * openingAnimation.value},
{translateX: AVATAR_SIZE / 2},
{translateY: AVATAR_SIZE / 2},
{translateX: left - openingAnimation.value * (left - margin)},
{translateY: margin * openingAnimation.value},
],
};
});
return (
<>
<Animated.ScrollView
horizontal
onScroll={scrollHandler}
scrollEventThrottle={16}
showsHorizontalScrollIndicator={false}
contentContainerStyle={{
width: (stories.length + 1) * (AVATAR_SIZE + AVATAR_MARGIN),
}}>
{stories.map((story, index) => {
return (
<Pressable
key={index}
onPress={() => {
setSelectedAvatar(index);
selectedAvatarA.value = index;
openingAnimation.value = withTiming(1, {duration: 400});
}}>
<Image
style={{
opacity: selectedAvatar === index ? 0 : 1,
width: AVATAR_SIZE,
height: AVATAR_SIZE,
borderRadius: AVATAR_SIZE / 2,
marginLeft: AVATAR_MARGIN,
}}
source={{uri: `https://api.adorable.io/avatars/100/${index}`}}
/>
</Pressable>
);
})}
</Animated.ScrollView>
<Animated.View
pointerEvents={selectedAvatar === -1 ? 'none' : 'auto'}
style={[
{
position: 'absolute',
left: 0,
top: 0,
width: WIDTH,
height: HEIGHT,
},
storyStyle,
]}>
{/* overlay */}
<Animated.View
style={[
{
width: WIDTH,
height: HEIGHT,
backgroundColor: '#000',
},
backgroundStyle,
]}>
{/* background */}
{selectedAvatar !== -1 && (
<Pressable
style={{
width: WIDTH,
height: HEIGHT,
}}
onPress={() => {
openingAnimation.value = withTiming(0, null, () => {
selectedAvatarA.value = -1;
setSelectedAvatar(-1);
});
}}>
<FastImage
style={{flex: 1}}
source={{uri: stories[selectedAvatar].pictures[0]}}
/>
</Pressable>
)}
</Animated.View>
</Animated.View>
<Animated.Image
style={[
{
width: AVATAR_SIZE,
height: AVATAR_SIZE,
borderRadius: AVATAR_SIZE / 2,
position: 'absolute',
left: 0,
top: 0,
},
sharedAvatarStyle,
]}
source={{
uri: `https://api.adorable.io/avatars/100/${selectedAvatar}`,
}}
/>
</>
);
};
export default Dismiss3;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment