官方效果
仿制效果
步骤解析
创建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',
}
})
现在我们就拥有了以下界面:
关联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了
Slider Dot的宽度变化
在思考Dot的宽度变化前,我们首先要思考以下问题:
- 如何确定页面向左滑 or 向右滑?
PagerView
有这样一个属性,onPageScroll
,对应event
拥有属性nativeEvent.offset
,我们发现当==从右向左==滑动时,这个值==减小==,当==从左往右==滑动时,这个值==增大==。所以:- 当
event.nativeEvent.offset
增大时,页面向右 - 当
event.nativeEvent.offset
减小时,页面向左
- 当
- 如何确定需要改变哪两个Dot的宽度?
- 现在我们已经知道了页面是向左还是向右,也知道了当前是哪个Dot处于
isActive
状态。 - 如果是往左滑,则改变的是==当前Dot==和==索引-1Dot==
- 如果是往右滑,则改变的是==当前Dot==和==索引+1Dot==
- 如何改变这两个Dot的宽度?依据是什么?
- 我们根据页面滑动的偏移量来确定滑块的宽度变化百分比,恰巧可以使用
1
中所提到的event.nativeEvent.offset
- 我们需要细化
2
中所改变的滑块对象。其实无论是左滑还是右滑,我们所需要改变的两个滑块其中的一个已经确定,而且这个滑块必定==宽度减小==,而他的==目标滑块==必定==宽度增加==,我们就只需要关注当前滑块的==目标滑块==即可。 - 如何关注目标滑块?通过
isTarget: Boolean
,若dotList
中的index === pageIndex + 1
orindex === 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