使用 Reanimated 在 React Native 中构建具有自动滚动、无限循环、分页功能的可定制轮播
在 React Native 中创建自定义轮播是为应用程序添加视觉效果和交互性的好方法。在本博客中,我们将探索如何使用 React Native 的 Animated 和 Reanimated 库构建包含自动滚动功能的轮播。我们还将实现一个带有动画点和优雅的图像过渡效果的分页系统。
概述
在本教程中,我们将介绍以下内容:
- 设置自定义轮播组件。
- 使用 Reanimated 实现流畅的动画和插值。
- 自动滚动功能,可在图像之间自动旋转。
- 构建一个带有动画点指示器的分页系统。
我们将构建什么:
- 带有动画过渡的水平滚动轮播。
- 当用户与轮播交互时自动滚动暂停。
- 根据当前可见项目更新的分页点。
让我们开始吧!
1. 设置轮播组件
我们首先创建 CustomCarousel 组件,它将容纳轮播的核心逻辑。主要元素包括:
- 用于渲染项目的 Animated.FlatList。
- 使用 setInterval 的自动滚动机制。
- Reanimated的scrollTo和useSharedValue用于动画转换。
/* eslint-disable react-native/no-inline-styles */ import React, { useEffect, useRef, useState } from 'react'; import { StyleSheet, View, useWindowDimensions } from 'react-native'; import Animated, { scrollTo, useAnimatedRef, useAnimatedScrollHandler, useDerivedValue, useSharedValue, } from 'react-native-reanimated'; import { hpx } from '../../helpers'; import Pagination from './Pagination'; import RenderItem from './RenderItem'; import { animals } from './constants'; const CustomCarousel = () => { const x = useSharedValue(0); const [data, setData] = useState(animals); const { width } = useWindowDimensions(); const [currentIndex, setCurrentIndex] = useState(0); const [paginationIndex, setPaginationIndex] = useState(0); const ref = useAnimatedRef(); const [isAutoPlay, setIsAutoPlay] = useState(true); const interval = useRef(); const offset = useSharedValue(0); console.log('CURRENT_CAROUSEL_ITEM?', paginationIndex); const onViewableItemsChanged = ({ viewableItems }) => { if (viewableItems[0].index !== undefined && viewableItems[0].index !== null) { setCurrentIndex(viewableItems[0].index); setPaginationIndex(viewableItems[0].index % animals.length); } }; const viewabilityConfig = { itemVisiblePercentThreshold: 50, }; const viewabilityConfigCallbackPairs = useRef([{ viewabilityConfig, onViewableItemsChanged }]); const onScroll = useAnimatedScrollHandler({ onScroll: (e) => { x.value = e.contentOffset.x; }, onMomentumEnd: (e) => { offset.value = e.contentOffset.x; }, }); useDerivedValue(() => { scrollTo(ref, offset.value, 0, true); }); useEffect(() => { if (isAutoPlay === true) { interval.current = setInterval(() => { offset.value += width; }, 4000); } else { clearInterval(interval.current); } return () => { clearInterval(interval.current); }; }, [isAutoPlay, offset, width]); return ( <View style={styles.container}> <Animated.FlatList ref={ref} style={{ height: hpx(194), flexGrow: 0 }} onScrollBeginDrag={() => { setIsAutoPlay(false); }} onScrollEndDrag={() => { setIsAutoPlay(true); }} onScroll={onScroll} scrollEventThrottle={16} horizontal bounces={false} pagingEnabled showsHorizontalScrollIndicator={false} viewabilityConfigCallbackPairs={viewabilityConfigCallbackPairs.current} onEndReached={() => setData([...data, ...animals])} onEndReachedThreshold={0.5} data={data} keyExtractor={(_, index) => `list_item${index}`} renderItem={({ item, index }) => { return <RenderItem item={item} index={index} x={x} />; }} /> <Pagination paginationIndex={paginationIndex} /> </View> ); }; export default CustomCarousel; const styles = StyleSheet.create({ container: { flex: 1, }, buttonContainer: { justifyContent: 'center', alignItems: 'center', flexDirection: 'row', gap: 14, }, });
2. 分页组件
分页组件显示点来指示当前活动的幻灯片。每个点的不透明度都会根据轮播的当前索引而变化。
import React from 'react'; import { StyleSheet, View } from 'react-native'; import { hpx } from '../../helpers'; import Dot from './Dot'; import { animals } from './constants'; const Pagination = ({ paginationIndex }) => { return ( <View style={styles.container}> {animals.map((_, index) => { return <Dot index={index} key={index} paginationIndex={paginationIndex} />; })} </View> ); }; export default Pagination; const styles = StyleSheet.create({ container: { flexDirection: 'row', marginTop: hpx(16), justifyContent: 'center', alignItems: 'center', }, });
3. 点组件
Dot 组件处理分页系统中每个点的外观。它根据点是否处于活动状态(当前索引)来更改其样式。
import React from 'react'; import { StyleSheet, View } from 'react-native'; import { Colors } from '../../assets'; import { hpx, wpx } from '../../helpers'; const Dot = ({ index, paginationIndex }) => { return <View style={paginationIndex === index ? styles.dot : styles.dotOpacity} />; }; export default Dot; const styles = StyleSheet.create({ dot: { backgroundColor: Colors.white, height: hpx(3), width: wpx(12), marginHorizontal: 2, borderRadius: 8, }, dotOpacity: { backgroundColor: Colors.white, height: hpx(3), width: wpx(12), marginHorizontal: 2, borderRadius: 8, opacity: 0.5, }, });
4. RenderItem 组件
RenderItem 组件显示每个轮播项目。它利用 Reanimated 的插值函数来为项目滚动时的不透明度设置动画。
import React from 'react'; import { StyleSheet, useWindowDimensions, View } from 'react-native'; import Animated, { Extrapolation, interpolate, useAnimatedStyle } from 'react-native-reanimated'; import { hpx, nf, SCREEN_WIDTH, wpx } from '../../helpers/Scale'; const RenderItem = ({ item, index, x }) => { const { width } = useWindowDimensions(); const animatedStyle = useAnimatedStyle(() => { const opacityAnim = interpolate( x.value, [(index - 1) * width, index * width, (index + 1) * width], [-0.3, 1, -0.3], Extrapolation.CLAMP ); return { opacity: opacityAnim, }; }); return ( <View style={{ width }}> <Animated.Image resizeMode="cover" source={{ uri: item.image }} style={[styles.titleImage, animatedStyle]} /> </View> ); }; export default RenderItem; const styles = StyleSheet.create({ titleImage: { width: SCREEN_WIDTH - wpx(32), // adjust the width of the image and horizontal padding height: hpx(194), alignSelf: 'center', borderRadius: nf(16), }, });
5. 自动滚动
自动滚动功能是通过setInterval实现的。此方法可确保轮播每 4 秒自动从一张幻灯片移动到下一张幻灯片。如果用户通过拖动与轮播交互,自动滚动就会暂停。
6. 常量文件
/* eslint-disable react-native/no-inline-styles */ import React, { useEffect, useRef, useState } from 'react'; import { StyleSheet, View, useWindowDimensions } from 'react-native'; import Animated, { scrollTo, useAnimatedRef, useAnimatedScrollHandler, useDerivedValue, useSharedValue, } from 'react-native-reanimated'; import { hpx } from '../../helpers'; import Pagination from './Pagination'; import RenderItem from './RenderItem'; import { animals } from './constants'; const CustomCarousel = () => { const x = useSharedValue(0); const [data, setData] = useState(animals); const { width } = useWindowDimensions(); const [currentIndex, setCurrentIndex] = useState(0); const [paginationIndex, setPaginationIndex] = useState(0); const ref = useAnimatedRef(); const [isAutoPlay, setIsAutoPlay] = useState(true); const interval = useRef(); const offset = useSharedValue(0); console.log('CURRENT_CAROUSEL_ITEM?', paginationIndex); const onViewableItemsChanged = ({ viewableItems }) => { if (viewableItems[0].index !== undefined && viewableItems[0].index !== null) { setCurrentIndex(viewableItems[0].index); setPaginationIndex(viewableItems[0].index % animals.length); } }; const viewabilityConfig = { itemVisiblePercentThreshold: 50, }; const viewabilityConfigCallbackPairs = useRef([{ viewabilityConfig, onViewableItemsChanged }]); const onScroll = useAnimatedScrollHandler({ onScroll: (e) => { x.value = e.contentOffset.x; }, onMomentumEnd: (e) => { offset.value = e.contentOffset.x; }, }); useDerivedValue(() => { scrollTo(ref, offset.value, 0, true); }); useEffect(() => { if (isAutoPlay === true) { interval.current = setInterval(() => { offset.value += width; }, 4000); } else { clearInterval(interval.current); } return () => { clearInterval(interval.current); }; }, [isAutoPlay, offset, width]); return ( <View style={styles.container}> <Animated.FlatList ref={ref} style={{ height: hpx(194), flexGrow: 0 }} onScrollBeginDrag={() => { setIsAutoPlay(false); }} onScrollEndDrag={() => { setIsAutoPlay(true); }} onScroll={onScroll} scrollEventThrottle={16} horizontal bounces={false} pagingEnabled showsHorizontalScrollIndicator={false} viewabilityConfigCallbackPairs={viewabilityConfigCallbackPairs.current} onEndReached={() => setData([...data, ...animals])} onEndReachedThreshold={0.5} data={data} keyExtractor={(_, index) => `list_item${index}`} renderItem={({ item, index }) => { return <RenderItem item={item} index={index} x={x} />; }} /> <Pagination paginationIndex={paginationIndex} /> </View> ); }; export default CustomCarousel; const styles = StyleSheet.create({ container: { flex: 1, }, buttonContainer: { justifyContent: 'center', alignItems: 'center', flexDirection: 'row', gap: 14, }, });
7. 结论
在本教程中,我们使用 React Native 的 FlatList 以及 Reanimated 构建了一个自定义轮播以实现流畅的动画和插值。我们添加了带有动画点、自动滚动功能的分页系统,并确保用户交互会暂停和恢复自动滚动功能。
使用这些组件,您可以扩展轮播以包含其他功能,例如动态内容、可点击项目和更复杂的动画。 React Native 与 Reanimated 的灵活性允许以最低的性能成本实现高度可定制的轮播,这对于创建具有视觉吸引力的移动应用程序非常有用。
请随意在您的项目中尝试这一点,并自定义样式和行为以满足您的设计需求!
以上是使用 Reanimated 在 React Native 中构建具有自动滚动、无限循环、分页功能的可定制轮播的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

JavaScript是现代Web开发的基石,它的主要功能包括事件驱动编程、动态内容生成和异步编程。1)事件驱动编程允许网页根据用户操作动态变化。2)动态内容生成使得页面内容可以根据条件调整。3)异步编程确保用户界面不被阻塞。JavaScript广泛应用于网页交互、单页面应用和服务器端开发,极大地提升了用户体验和跨平台开发的灵活性。

JavaScript的最新趋势包括TypeScript的崛起、现代框架和库的流行以及WebAssembly的应用。未来前景涵盖更强大的类型系统、服务器端JavaScript的发展、人工智能和机器学习的扩展以及物联网和边缘计算的潜力。

不同JavaScript引擎在解析和执行JavaScript代码时,效果会有所不同,因为每个引擎的实现原理和优化策略各有差异。1.词法分析:将源码转换为词法单元。2.语法分析:生成抽象语法树。3.优化和编译:通过JIT编译器生成机器码。4.执行:运行机器码。V8引擎通过即时编译和隐藏类优化,SpiderMonkey使用类型推断系统,导致在相同代码上的性能表现不同。

Python更适合初学者,学习曲线平缓,语法简洁;JavaScript适合前端开发,学习曲线较陡,语法灵活。1.Python语法直观,适用于数据科学和后端开发。2.JavaScript灵活,广泛用于前端和服务器端编程。

JavaScript是现代Web开发的核心语言,因其多样性和灵活性而广泛应用。1)前端开发:通过DOM操作和现代框架(如React、Vue.js、Angular)构建动态网页和单页面应用。2)服务器端开发:Node.js利用非阻塞I/O模型处理高并发和实时应用。3)移动和桌面应用开发:通过ReactNative和Electron实现跨平台开发,提高开发效率。

本文展示了与许可证确保的后端的前端集成,并使用Next.js构建功能性Edtech SaaS应用程序。 前端获取用户权限以控制UI的可见性并确保API要求遵守角色库

从C/C 转向JavaScript需要适应动态类型、垃圾回收和异步编程等特点。1)C/C 是静态类型语言,需手动管理内存,而JavaScript是动态类型,垃圾回收自动处理。2)C/C 需编译成机器码,JavaScript则为解释型语言。3)JavaScript引入闭包、原型链和Promise等概念,增强了灵活性和异步编程能力。

我使用您的日常技术工具构建了功能性的多租户SaaS应用程序(一个Edtech应用程序),您可以做同样的事情。 首先,什么是多租户SaaS应用程序? 多租户SaaS应用程序可让您从唱歌中为多个客户提供服务
