Skip to content

Commit d518f6b

Browse files
zuojiahui秋小白.丶
andauthored
feat(Swiper): 添加轮播滑动,点击事件,添加组件文档 (#185)
* fix: 修改首页 * 添加Android(Windows)环境安装文档 * 添加轮播图组件 * 添加轮播滑动,点击事件,添加组件文档 Co-authored-by: 秋小白.丶 <[email protected]>
1 parent 8e2776f commit d518f6b

File tree

7 files changed

+444
-700
lines changed

7 files changed

+444
-700
lines changed

example/examples/src/routes/Swiper/index.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,8 @@ const SwiperDemo = () => {
99
]
1010
return (
1111
<View>
12-
<Swiper dataSource={data} height={150} />
12+
<Swiper width={200} dataSource={data} height={150} borderRadius={24} />
1313
</View>
14-
1514
)
1615
}
1716
export default SwiperDemo

packages/core/src/Swiper/README.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
Swiper 轮播图
2+
---
3+
4+
最基础的轮播图,可承载图片。
5+
6+
### 基础示例
7+
8+
<!--DemoStart-->
9+
```jsx
10+
import React from 'react';
11+
import { Swiper } from '@uiw/react-native';
12+
const SwiperDemo = () => {
13+
const data = [
14+
{ url: "https://reactnative.dev/img/tiny_logo.png", onClick: () => { console.log("你好!uiw/react-native") } },
15+
{ url: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADMAAAAzCAYAAAA6oTAqAAAAEXRFWHRTb2Z0d2FyZQBwbmdjcnVzaEB1SfMAAABQSURBVGje7dSxCQBACARB+2/ab8BEeQNhFi6WSYzYLYudDQYGBgYGBgYGBgYGBgYGBgZmcvDqYGBgmhivGQYGBgYGBgYGBgYGBgYGBgbmQw+P/eMrC5UTVAAAAABJRU5ErkJggg==' },
16+
{ url: require('@expo/snack-static/react-native-logo.png') }
17+
]
18+
return (
19+
<View>
20+
<Swiper width={200} dataSource={data} height={150} borderRadius={24} />
21+
</View>
22+
)
23+
}
24+
export default SwiperDemo
25+
```
26+
<!--End-->
27+
28+
## Props
29+
30+
属性 | 说明 | 类型 | 默认值
31+
----|-----|------|------
32+
| dataSource | 数据源 | Array | [ ] |
33+
| width | 宽度 | Number |屏幕宽度|
34+
| height | 高度 | Number | 130 |
35+
| time | 执行时间 | Number | 3000(3s) |
36+
| borderRadius | 圆角边框 | Number | 0 |
37+
| autoplay | 是否开启定时器 | Boolean | true|
38+
| dotStyle | 圆点类型 ( dot : 圆点, block : 方块 ) | String | dot |

packages/core/src/Swiper/index.tsx

Lines changed: 101 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,98 +1,151 @@
11
import React, { useState, useEffect, useRef, useCallback, } from 'react'
2-
import { StyleSheet, View, Image } from 'react-native';
2+
import { StyleSheet, View, Image, ScrollView, Dimensions, TouchableOpacity, NativeSyntheticEvent, NativeScrollEvent } from 'react-native';
33
export interface dataSourceType {
44
url: string
5-
// onClick?: () => void
5+
onClick?: () => void
66
}
7+
export type dotType = 'dot' | 'block'
78
export interface SwiperProps {
8-
dataSource: dataSourceType[];
9+
// 数据源
10+
dataSource?: dataSourceType[];
11+
// 轮播图宽度
12+
width?: number,
13+
// 轮播图高度
14+
height?: number,
15+
// 播放时间
916
time?: number;
10-
width?: number;
11-
height?: number;
12-
autoplay?: boolean
17+
// 圆角边框
18+
borderRadius?: number,
19+
// 是否开启自动播放
20+
autoplay?: boolean,
21+
// 指示点样式 dot: 圆点 block: 方块
22+
dotStyle?: dotType
1323
}
1424
const Swiper = (porps: SwiperProps) => {
15-
const { dataSource = [], time = 3000, autoplay = true, width = "100%", height = 130 } = porps
25+
const gitwidth = Dimensions.get('window').width;
26+
const { dataSource = [], width = gitwidth, height = 130, time = 3000, autoplay = true, borderRadius = 0, dotStyle = "dot" } = porps
1627
let [curIndex, setCurIndex] = useState(0)
1728
let timer = useRef<NodeJS.Timeout | undefined>();
29+
const scrollToRef: any = useRef()
30+
31+
// 自动播放
1832
const autoPlay = useCallback(() => {
1933
clearInterval(timer.current as unknown as number);
2034
timer.current = setInterval(() => {
35+
let index = curIndex + 1
2136
if (curIndex === dataSource.length - 1) {
37+
index = 0
2238
setCurIndex(0);
2339
} else {
2440
setCurIndex(curIndex + 1);
2541
}
42+
scrollToRef.current.scrollTo({ x: width * index, y: 0, animated: true })
2643
}, time);
27-
}, [dataSource, curIndex]);
44+
}, [curIndex]);
45+
46+
// 开启播放
2847
useEffect(() => {
29-
if (autoplay) {
30-
autoPlay()
31-
}
48+
if (autoplay) autoPlay()
3249
}, [autoPlay]);
50+
51+
// 页面离开停止播放
3352
useEffect(() => {
3453
return () => {
3554
clearInterval(timer.current as unknown as number);
3655
};
3756
}, []);
57+
58+
// 开始拖拽终止定时器
59+
const onScrollBeginDrag = () => {
60+
if (autoplay) clearInterval(timer.current as unknown as number)
61+
}
62+
63+
// 停止拖拽开启定时器
64+
const onScrollEndDrag = () => {
65+
if (autoplay) autoPlay()
66+
}
67+
68+
// 拖拽成功
69+
const onMomentumScrollEnd = (e: NativeSyntheticEvent<NativeScrollEvent>) => {
70+
e.persist();
71+
let offSetX = e.nativeEvent.contentOffset.x;
72+
let mentWidth = e.nativeEvent.layoutMeasurement.width;
73+
let page = offSetX / mentWidth;
74+
setCurIndex(page)
75+
}
3876
return (
39-
<View style={StyleSheet.flatten([styles.box, { height }])}>
40-
<View style={StyleSheet.flatten([styles.banner, { width }])}>
41-
<View style={StyleSheet.flatten([styles.bannerOut, { marginLeft: curIndex * -100 + '%' }])}>
42-
{dataSource.map((item: any, index: number) => {
43-
return <Image style={styles.tinyLogo} key={index} source={{
44-
uri: item.url,
45-
}} />
46-
})}
47-
</View>
48-
<View style={styles.dotBox}>
49-
{dataSource.map((_: any, index: number) => {
50-
return <View key={index} style={StyleSheet.flatten([styles.dot, index === curIndex ? styles.dotSetColor : styles.dotColor])} />
51-
})}
52-
</View>
77+
<View style={StyleSheet.flatten([styles.banner, { width, height }])}>
78+
<ScrollView
79+
ref={scrollToRef}
80+
horizontal={true}
81+
showsHorizontalScrollIndicator={false}
82+
pagingEnabled={true}
83+
onScrollBeginDrag={onScrollBeginDrag}
84+
onScrollEndDrag={onScrollEndDrag}
85+
onMomentumScrollEnd={onMomentumScrollEnd}
86+
>
87+
{dataSource.map((item: dataSourceType, index: number) => {
88+
return (
89+
<View key={index} style={{ width, height }}>
90+
<View style={{ padding: 12 }}>
91+
<TouchableOpacity activeOpacity={1} onPress={() => {
92+
item.onClick && item.onClick()
93+
}}>
94+
<Image
95+
key={index}
96+
style={StyleSheet.flatten([{ borderRadius, width: "100%", height: "100%", }])}
97+
resizeMode="cover"
98+
source={typeof item.url === "number" ? item.url : { uri: item.url, }}
99+
/>
100+
</TouchableOpacity>
101+
</View>
102+
</View>
103+
)
104+
})}
105+
</ScrollView>
106+
<View style={styles.dotBox}>
107+
{dataSource.map((_: dataSourceType, index: number) => {
108+
return <View
109+
key={index}
110+
style={
111+
StyleSheet.flatten([dotStyle === "block" ? styles.block : styles.dot,
112+
index === curIndex ? styles.dotSetColor : styles.dotColor])
113+
} />
114+
})}
53115
</View>
54116
</View>
55-
56117
)
57118
}
58119
const styles = StyleSheet.create({
59-
box: {
60-
margin: 12,
61-
height: 130,
62-
},
63120
banner: {
64121
width: "100%",
65-
borderRadius: 25,
66122
position: "relative",
67123
overflow: "hidden",
68124
},
69-
bannerOut: {
70-
width: "100%",
71-
height: "100%",
72-
flexDirection: 'row',
73-
alignItems: 'center',
74-
marginLeft: 0
75-
},
76-
tinyLogo: {
77-
width: "100%",
78-
height: "100%",
79-
},
80125
dotBox: {
81126
width: "100%",
82127
flexDirection: 'row',
83128
alignItems: 'center',
84129
justifyContent: "center",
85130
position: "absolute",
86-
bottom: 12
131+
bottom: 22
87132
},
88133
dot: {
89-
width: 10,
90-
height: 10,
91-
borderRadius: 5,
134+
width: 8,
135+
height: 8,
136+
borderRadius: 4,
137+
marginTop: 0,
138+
marginBottom: 0,
139+
marginLeft: 8,
140+
marginRight: 8
141+
},
142+
block: {
143+
width: 16,
144+
height: 3,
92145
marginTop: 0,
93146
marginBottom: 0,
94-
marginLeft: 12,
95-
marginRight: 12
147+
marginLeft: 8,
148+
marginRight: 8
96149
},
97150
dotColor: {
98151
backgroundColor: "lightgray",
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import Markdown, { importAll } from '../../../component/Markdown';
2+
3+
export default class Page extends Markdown {
4+
path="/packages/core/src/Swiper/README.md"
5+
getMarkdown = async () => {
6+
const md = await import('@uiw/react-native/lib/Swiper/README.md');
7+
// 支持 markdown 中,相对于当前 index.tsx 相对路径引入图片资源
8+
importAll((require as any).context('./', true, /\.(png|gif|jpg|svg)$/), this.imageFiles);
9+
return md.default || md;
10+
}
11+
}

website/src/routes/menus.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export const componentMenus: MenuData[] = [
3939
{ path: "/components/quicklist", name: "QuicList 快速列表" },
4040
{ path: "/components/card", name: "Card 卡片" },
4141
{ path: "/components/noticebar", name: "NoticeBar 通告栏" },
42+
{ path: "/components/swiper", name: "Swiper 轮播图" },
4243
{ divider: true, name: "Feedback" },
4344
{ path: "/components/loader", name: "Loader 加载" },
4445
{ path: "/components/modal", name: "Modal 模态框" },

website/src/routes/router.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,4 +196,7 @@ export const getRouterData = {
196196
'/components/stepper': {
197197
component: dynamicWrapper([], () => import('../pages/components/stepper')),
198198
},
199+
'/components/swiper': {
200+
component: dynamicWrapper([], () => import('../pages/components/swiper')),
201+
},
199202
};

0 commit comments

Comments
 (0)