React Native实现美团slider效果

官方效果

React Native实现美团slider效果

仿制效果

React Native实现美团slider效果

步骤解析

创建SwiperContent/Slider

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

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',
	}
})

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

React Native实现美团slider效果

关联Slider和Content

  • 在样式表中添加
dotActive: {backgroundColor: 'rgb(255, 0, 0)'}
  • 增添PageIndex确定当前Content页面索引
    • setPage更改当前PageIndex
    • pageRefs绑定到<PagerView/>上,当onPageSelected事件触发时改变当前页面
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
<View style={styles.dotList}>
    {arr.map((_, index) => {
        const isActive = index === pageIndex
        return (
            <View
                key={index}
                style={[
                    styles.dotStyle,
                    isActive && styles.dotActive,
                ]}
             />
        )
    })}
</View>

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

React Native实现美团slider效果

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值==,如下:
isActive && {
    // * max-width: 20
    width: isLeft ? 10 + 10 * offsetX : 20 - 10 * offsetX,
},
isTarget && {
    width: isLeft ? 20 - 10 * offsetX : 10 + 10 * offsetX,
}

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

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

原文链接:https://juejin.cn/post/7342705422530461731 作者:Apries

(0)
上一篇 2024年3月6日 上午11:03
下一篇 2024年3月6日 上午11:13

相关推荐

发表评论

登录后才能评论