diff --git a/example/app/_layout.tsx b/example/app/_layout.tsx index 3a27973..2a0c39b 100644 --- a/example/app/_layout.tsx +++ b/example/app/_layout.tsx @@ -10,6 +10,10 @@ export default function Layout() { name="scrollviews/vertical" options={{ title: 'Vertical Lazy' }} /> + + ); diff --git a/example/app/scrollviews/flatlist.tsx b/example/app/scrollviews/flatlist.tsx new file mode 100644 index 0000000..dc5b4ac --- /dev/null +++ b/example/app/scrollviews/flatlist.tsx @@ -0,0 +1,124 @@ +import React, { useCallback, useRef } from 'react'; +import { + ImageSourcePropType, + ListRenderItemInfo, + StyleSheet, + Text, + TouchableOpacity, + View, +} from 'react-native'; +import { + LazyFlatList, + LazyFlatListMethods, +} from 'react-native-lazy-scrollview'; +import { ALBUMS, PADDING_VERTICAL } from '../../constants'; +import shuffle from 'lodash/shuffle'; +import { FireOnceBlock } from '../../components/blocks/FireOnceBlock'; +import { ImageBlock } from '../../components/blocks/ImageBlock'; +import { NoLazyChild } from '../../components/blocks/NoLazyChild'; +import { Header } from '../../components/blocks/Header'; + +const OFFSET = -50; + +type Block = ImageSourcePropType | 'no-lazy' | 'fire-once'; + +export default function FlatList() { + const ref = useRef(null); + + const data: Block[] = shuffle( + ALBUMS.concat(shuffle(ALBUMS)).concat(shuffle(ALBUMS)) + ); + + const renderItem = useCallback( + ({ item: source, index }: ListRenderItemInfo) => { + if (source === 'no-lazy') { + return ; + } + + if (source === 'fire-once') { + return ( + + ); + } + + return ( + + ); + }, + [] + ); + + return ( + + + + ref.current?.scrollToStart({ animated: true })} + > + ⬆️ + + ref.current?.scrollToEnd({ animated: true })} + > + ⬇️ + + + + ); +} + +const styles = StyleSheet.create({ + scrollviewContainer: { + flex: 1, + backgroundColor: '#ecf0f1', + }, + offsetBar: { + position: 'absolute', + bottom: OFFSET * -1 + PADDING_VERTICAL, + borderBottomWidth: 1, + borderBottomColor: 'black', + left: 0, + right: 0, + opacity: 0.7, + height: 50, + justifyContent: 'flex-end', + }, + offsetText: { + color: 'white', + fontSize: 18, + fontWeight: '600', + backgroundColor: '#000', + padding: 8, + alignSelf: 'flex-start', + }, + arrowsContainer: { + top: 8, + right: 8, + position: 'absolute', + justifyContent: 'center', + flexDirection: 'row', + }, + arrowButton: { marginBottom: 8 }, + arrow: { fontSize: 32 }, +}); diff --git a/example/assets/flatlist.png b/example/assets/flatlist.png new file mode 100644 index 0000000..31d2c0d Binary files /dev/null and b/example/assets/flatlist.png differ diff --git a/example/assets/header.jpg b/example/assets/header.jpg new file mode 100644 index 0000000..747674f Binary files /dev/null and b/example/assets/header.jpg differ diff --git a/example/components/blocks/Header.tsx b/example/components/blocks/Header.tsx new file mode 100644 index 0000000..fc31773 --- /dev/null +++ b/example/components/blocks/Header.tsx @@ -0,0 +1,86 @@ +import React, { ComponentProps, useState } from 'react'; +import { + Dimensions, + Image, + ImageSourcePropType, + StyleSheet, + View, +} from 'react-native'; +import { LazyChild } from 'react-native-lazy-scrollview'; +import { SQUARE_SIZE } from '../../constants'; +import { Skeleton } from '../loaders/Skeleton'; + +const SCREEN_WIDTH = Dimensions.get('window').width; + +export function Header() { + const [triggered, setTriggered] = useState(false); + const [isVisible, setIsVisible] = useState(false); + + const onEnterThresholdPass = () => { + setTriggered(true); + }; + + const onExitThresholdPass = () => { + setTriggered(false); + }; + + const onVisibilityEnter = () => { + setIsVisible(true); + }; + + const onVisibilityExit = () => { + setIsVisible(false); + }; + + return ( + + + + + + + {!isVisible ? : null} + + + + ); +} + +const styles = StyleSheet.create({ + container: { + justifyContent: 'center', + alignItems: 'center', + alignSelf: 'center', + width: SCREEN_WIDTH, + }, + contentContainer: { + width: SCREEN_WIDTH, + height: SCREEN_WIDTH * 1.25, + justifyContent: 'center', + alignItems: 'center', + }, + image: { + width: SCREEN_WIDTH, + height: SCREEN_WIDTH * 1.25, + resizeMode: 'cover', + }, + percentTextWrapper: { + position: 'absolute', + top: 0, + right: 0, + bottom: 0, + left: 0, + backgroundColor: 'rgba(255, 255, 255, 0.5)', + justifyContent: 'center', + alignItems: 'center', + }, +}); diff --git a/example/components/cards/FlatListCard.tsx b/example/components/cards/FlatListCard.tsx new file mode 100644 index 0000000..d4450dc --- /dev/null +++ b/example/components/cards/FlatListCard.tsx @@ -0,0 +1,29 @@ +import React, { useEffect } from 'react'; +import { + interpolate, + useAnimatedStyle, + useSharedValue, + withRepeat, + withTiming, +} from 'react-native-reanimated'; +import { FLATLIST } from '../../constants'; +import { Card } from './Card'; + +export function FlatListCard() { + const animation = useSharedValue(0); + + useEffect(() => { + animation.value = withRepeat(withTiming(1, { duration: 1000 }), -1, true); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + const animatedStyle = useAnimatedStyle(() => { + return { + marginRight: 16, + transform: [ + { translateY: interpolate(animation.value, [0, 1], [-20, 20]) }, + ], + }; + }); + + return ; +} diff --git a/example/constants.ts b/example/constants.ts index 9c86c49..7208bac 100644 --- a/example/constants.ts +++ b/example/constants.ts @@ -44,6 +44,15 @@ export const VERTICAL = { image: require('./assets/vertical.png'), }; +export const FLATLIST = { + name: 'flatlist', + title: 'Lazy FlatList', + color: '#f8a5c2', + description: + 'Less use cases than LazyScrollView, but still useful for situations where you need to measure headers or footers, or have specific cells react granularly to scroll visibility. Not recommended to wrap every item in LazyChild.', + image: require('./assets/flatlist.png'), +}; + export const HORIZONTAL = { name: 'horizontal', title: 'Horizontal Lazy ScrollView',