React Native实现banner轮播图

轮播图也叫焦点图,就是几张图片不断的来回切换,很多应用和网站中都可以看到。

如果是前端开发,使用JavaScript+HTML实现,可以将几张图横向拼装在一起,开启setInterval定时任务不断改变拼装图片的left位置。

如果是Android,即可以使用ViewPage,又可以使用ScrollView。如果使用ScrollView实现,则与前端实现方法基本相同,只不过需要自己控制手势操作。

本文中使用React Native的ScrollView组件实现轮播图,废话不说了,先来看效果:

效果图

动画很流畅(原谅我录屏的帧率比较小,转成gif就…),没有出现卡顿,同时支持手势滑动、点击事件,没有出现滑动冲突。总体来说效果还可以吧。

实现原理很简单,开启个定时任务不断来回滚动几张图片,同Android使用ScrollView实现以及前端实现方法基本一致。代码全部使用的官方API,比较容易理解。

轮播图组件代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
/**
* @Author: zhaoshuo
* @Description: 轮播图组件
*/


'use strict';

import React, {
PropTypes,
TouchableWithoutFeedback,
ScrollView,
Animated,
View,
Component,
} from 'react-native';

import Dimensions from 'Dimensions';

// 屏幕宽度
var screenWidth = Dimensions.get('window').width;

class FocusImage extends Component{
constructor(props) {
super(props);
this.state = {
images : ['#dfe24a','#68eaf9','#ef9af9'],// 使用颜色代替图片
selectedImageIndex: 0,
isNeedRun: true,
};

this._index = 0;// 当前正在显示的图片
this._max = this.state.images.length;// 图片总数
}

render(){

// 图片列表
let images = this.state.images.map((value,i) => {
return (
<TouchableWithoutFeedback onPress={()=>this._showLog(i)}>
<View style={{width:screenWidth,height:130,backgroundColor:value}}/>
</TouchableWithoutFeedback>);
});

// 小圆点指示器
let circles = this.state.images.map((value,i) => {
return (<View key={i} style={ (i == this.state.selectedImageIndex) ? styles.circleSelected : styles.circle}/>);
});

// 小圆点位置居中显示
let imageLength = this.state.images.length;
let circleLength = 6 * imageLength + 5 * 2 * imageLength;
let center = (screenWidth - circleLength) / 2;

return (
<View style={styles.container}>
<ScrollView horizontal={true}
showsHorizontalScrollIndicator={false}
showsVerticalScrollIndicator={false}
onTouchStart={()=>this._onTouchStart()}
onTouchMove={()=>console.log('onTouchMove')}
onTouchEnd={()=>this._onTouchEnd()}
onScroll={()=>this._onScroll()}
ref={(scrollView) => { this._scrollView = scrollView;}}>

<Animated.View style={{flexDirection:'row'}}>{images}</Animated.View>
</ScrollView>
<View style={{flexDirection:'row',position:'absolute',top:115,left:center}}>{circles}</View>
</View>
);
}

_onTouchStart(){
// 当手指按到scrollview时停止定时任务
clearInterval(this._timer);
}

_onTouchEnd(){
// 先滑动到指定index位置,再开启定时任务
this._scrollView.scrollTo({x:this._index * screenWidth},true);
// 重置小圆点指示器
this._refreshFocusIndicator();
this._runFocusImage();
}

_onScroll(){
this._contentOffsetX = this._scrollView.contentOffset.x;
this._index = Math.round(this._contentOffsetX / screenWidth);
}

_runFocusImage(){
if(this._max <= 1){ // 只有一个则不启动定时任务
return;
}
this._timer = setInterval(function () {
this._index++;
if(this._index >= this._max){
this._index = 0;
}
this._scrollView.scrollTo({x:this._index * screenWidth},true);
// 重置小圆点指示器
this._refreshFocusIndicator();
}.bind(this), 4000);
}

_stopFocusImage(){
clearInterval(this._timer);
}

_refreshFocusIndicator(){
this.setState({selectedImageIndex:this._index});
}

_showToast(i) {
//显示的内容
var message = '点击: ' + i;
console.log(message);
}

// 组件装载完成
componentDidMount(){
this._runFocusImage();
}

// 组件即将卸载
componentWillUnmount(){
clearInterval(this._timer);
}

// 组件接收到新属性
componentWillReceiveProps(nextProps) {
}
}

const styles = {
container: {
flex:1,
flexDirection:'row',
},
circleContainer: {
position:'absolute',
left:0,
top:120,
},
circle: {
width:6,
height:6,
borderRadius:6,
backgroundColor:'#f4797e',
marginHorizontal:5,
},
circleSelected: {
width:6,
height:6,
borderRadius:6,
backgroundColor:'#ffffff',
marginHorizontal:5,
}
};

FocusImage.defaultProps = {
isNeedRun : true,
};

FocusImage.propTypes = {
isNeedRun : PropTypes.bool,
onItemClick : PropTypes.func,
};

module.exports = FocusImage;

功能逻辑比较简单,代码里有注释,我就不再做多余的介绍了。

使用

1
2
3
4
5
6
7
8
9
import FocusImage from './../components/FocusImage';

...
render(
<View>
<FocusImage />
</View>
);

...