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大佬的指导

相关推荐
Misha韩14 小时前
React Native 亲切的组件们(函数式组件/class组件)和陌生的样式
react native·函数式组件·class组件
1234Wu14 小时前
React Native 接入 eCharts
javascript·react native·react.js
wen's2 天前
React Native 0.79.4 中 [RCTView setColor:] 崩溃问题完整解决方案
javascript·react native·react.js
朝阳392 天前
ReactNative【实战系列教程】我的小红书 3 -- 自定义底栏Tab导航(含图片选择 expo-image-picker 的使用)
react native
冰冷的bin3 天前
【React Native】自定义倒计时组件CountdownView
react native
朝阳3912 天前
React Native【实用教程】(含图标方案,常用第三库,动画,内置组件,内置Hooks,内置API,自定义组件,创建项目等)
react native
朝阳3913 天前
React Native【实战范例】同步跟随滚动
react native
朝阳3914 天前
React Native【详解】动画
react native
朝阳3916 天前
React Native【详解】内置 API
react native
xx240616 天前
React Native学习笔记
笔记·学习·react native