React Native实现美团slider效果

官方效果

仿制效果

步骤解析

创建SwiperContent/Slider

这里我使用了react-native-pager-view制作Swiper组件

jsx 复制代码
import React from 'react'
import { StyleSheet, Text, View } from 'react-native'
import PagerView from 'react-native-pager-view'

const arr = Array.from({ length: 5 }, (_, i) => i)

const Swiper = () => {
	return (
		<View style={{ flex: 5 }}>
			<PagerView style={styles.viewPager}>
				{arr.map((item, index) => (
					<View key={index} style={styles.page}>
						<Text>{item}</Text>
					</View>
				))}
			</PagerView>
			<View style={styles.dotList}>
				{arr.map((_, index) => {
					return <View key={index} style={styles.dotStyle} />
				})}
			</View>
		</View>
	)
}
const styles = StyleSheet.create({
    viewPager: {
		flex: 1,
		justifyContent: 'center',
		alignItems: 'center',
	},
	page: {
		justifyContent: 'center',
		alignItems: 'center',
	},
	dotList: {
		flex: 2,
		flexDirection: 'row',
		justifyContent: 'center',
		gap: 10,
	},
	dotStyle: {
		width: 10,
		height: 10,
		borderRadius: 5,
		backgroundColor: 'gray',
	}
})

现在我们就拥有了以下界面:

关联Slider和Content

  • 在样式表中添加
jsx 复制代码
dotActive: {backgroundColor: 'rgb(255, 0, 0)'}
  • 增添PageIndex确定当前Content页面索引
    • setPage更改当前PageIndex
    • pageRefs绑定到<PagerView/>上,当onPageSelected事件触发时改变当前页面
jsx 复制代码
const [pageIndex, setPageIndex] = React.useState(0)
const pageRefs = React.useRef()

const setPage = (page) => {
		pageRefs.current.setPage(page)
		setPageIndex(page)
}
<PagerView
    style={styles.viewPager}
    initialPage={0}
    ref={pageRefs}
    onPageSelected={(event) => setPage(event.nativeEvent.position)}
    >
    {arr.map((item, index) => (
        <View key={index} style={styles.page}>
            <Text>{item}</Text>
        </View>
    ))}
</PagerView>
  • 改写dotList
jsx 复制代码
<View style={styles.dotList}>
    {arr.map((_, index) => {
        const isActive = index === pageIndex
        return (
            <View
                key={index}
                style={[
                    styles.dotStyle,
                    isActive && styles.dotActive,
                ]}
             />
        )
    })}
</View>

当前我们已经获得了这样一个能够反馈Content页面变化的dotList了

Slider Dot的宽度变化

在思考Dot的宽度变化前,我们首先要思考以下问题:

  1. 如何确定页面向左滑 or 向右滑?
  • PagerView有这样一个属性,onPageScroll,对应event拥有属性nativeEvent.offset,我们发现当==从右向左==滑动时,这个值==减小==,当==从左往右==滑动时,这个值==增大==。所以:
    • event.nativeEvent.offset增大时,页面向右
    • event.nativeEvent.offset减小时,页面向左
  1. 如何确定需要改变哪两个Dot的宽度?
  • 现在我们已经知道了页面是向左还是向右,也知道了当前是哪个Dot处于isActive状态。
  • 如果是往左滑,则改变的是==当前Dot==和==索引-1Dot==
  • 如果是往右滑,则改变的是==当前Dot==和==索引+1Dot==
  1. 如何改变这两个Dot的宽度?依据是什么?
  • 我们根据页面滑动的偏移量来确定滑块的宽度变化百分比,恰巧可以使用1中所提到的event.nativeEvent.offset
  • 我们需要细化2中所改变的滑块对象。其实无论是左滑还是右滑,我们所需要改变的两个滑块其中的一个已经确定,而且这个滑块必定==宽度减小==,而他的==目标滑块==必定==宽度增加==,我们就只需要关注当前滑块的==目标滑块==即可。
  • 如何关注目标滑块?通过isTarget: Boolean,若dotList中的index === pageIndex + 1orindex === pageIndex - 1,则他为当前滑块的目标滑块,仅需要添加上方向的判定,我们即可完成。如:const isTarget = isLeft ? index === pageIndex - 1 : index === pageIndex + 1
  • 但是我们此时仍然不能修改dot的宽度。我们需要关注==滑动方向==,==offset值==,如下:
jsx 复制代码
isActive && {
    // * max-width: 20
    width: isLeft ? 10 + 10 * offsetX : 20 - 10 * offsetX,
},
isTarget && {
    width: isLeft ? 20 - 10 * offsetX : 10 + 10 * offsetX,
}

好了,现在我们已经实现了所有的逻辑代码。全部代码奉上。

jsx 复制代码
import React from 'react'
import { StyleSheet, Text, View } from 'react-native'
import PagerView from 'react-native-pager-view'

const arr = Array.from({ length: 5 }, (_, i) => i)

const MyPager = () => {
	const pageRefs = React.useRef()
	const [pageIndex, setPageIndex] = React.useState(0)
	const [offsetX, setOffsetX] = React.useState(0)
	const [isLeft, setIsLeft] = React.useState(false)

	const setPage = (page) => {
		pageRefs.current.setPage(page)
		setPageIndex(page)
	}

	return (
		<View style={{ flex: 1 }}>
			<PagerView
				style={styles.viewPager}
				initialPage={0}
				ref={pageRefs}
				onPageScroll={(event) => {
					setOffsetX(event.nativeEvent.offset)
					setIsLeft(event.nativeEvent.position === pageIndex - 1)
					console.log(event.nativeEvent.offset)
				}}
				onPageSelected={(event) => setPage(event.nativeEvent.position)}
			>
				{arr.map((item, index) => (
					<View key={index} style={styles.page}>
						<Text>{item}</Text>
					</View>
				))}
			</PagerView>
			<View style={styles.dotList}>
				{arr.map((_, index) => {
					const isActive = index === pageIndex
					const isTarget = isLeft
						? index === pageIndex - 1
						: index === pageIndex + 1
					return (
						<View
							key={index}
							style={[
								styles.dotStyle,
								isActive && styles.dotActive,
								isActive && {
									// * max-width: 20
									width: isLeft ? 10 + 10 * offsetX : 20 - 10 * offsetX,
								},
								isTarget && {
									width: isLeft ? 20 - 10 * offsetX : 10 + 10 * offsetX,
								},
							]}
						/>
					)
				})}
			</View>
		</View>
	)
}

const styles = StyleSheet.create({
	viewPager: {
		borderBlockColor: 'red',
		borderBottomWidth: StyleSheet.hairlineWidth,
		borderStyle: 'solid',
		flex: 1,
	},
	page: {
		justifyContent: 'center',
		alignItems: 'center',
	},
	dotList: {
		flex: 2,
		flexDirection: 'row',
		justifyContent: 'center',
		gap: 10,
	},
	dotStyle: {
		width: 10,
		height: 10,
		borderRadius: 5,
		backgroundColor: '#000000',
	},
	dotActive: {
		backgroundColor: 'rgb(255, 0, 0)',
	},
})

export default MyPager

踩坑点!!!

const [isLeft, setIsLeft] = React.useState(false)中的false不可改为true,因为一开始我们的滑块处于最左方,一开始的isLeft必然为false,需要向右滑。

致谢

感谢别调P大佬的指导

相关推荐
诚实可靠王大锤18 小时前
react-native项目通过华为OBS预签名url实现前端直传
前端·react native·华为
三思而后行,慎承诺3 天前
Reactnative实现远程热更新的原理是什么
javascript·react native·react.js
木西5 天前
React Native DApp 开发全栈实战·从 0 到 1 系列(永续合约交易-前端部分)
react native·web3·智能合约
歪歪1005 天前
Redux和MobX在React Native状态管理中的优缺点对比
前端·javascript·react native·react.js·架构·前端框架
带娃的IT创业者5 天前
《AI大模型应知应会100篇》第68篇:移动应用中的大模型功能开发 —— 用 React Native 打造你的语音笔记摘要 App
人工智能·笔记·react native
sylvia_08155 天前
react native 初次使用Android Studio 打包
android·react native·android studio
索马里亚纳海参炒贩6 天前
useCallback useMemo memo 三个区别和作用
前端·react native
冰冷的bin7 天前
【React Native】点赞特效动画组件FlowLikeView
react native·react.js·typescript
懒人村杂货铺7 天前
[特殊字符] 跨端视频通话实战:腾讯云 TRTC + IM(React Native & Web)
react native·音视频·腾讯云