轮播图也叫焦点图,就是几张图片不断的来回切换,很多应用和网站中都可以看到。
如果是前端开发,使用JavaScript+HTML实现,可以将几张图横向拼装在一起,开启setInterval定时任务不断改变拼装图片的left位置。
如果是Android,即可以使用ViewPage,又可以使用ScrollView。如果使用ScrollView实现,则与前端实现方法基本相同,只不过需要自己控制手势操作。
本文中使用React Native的ScrollView组件实现轮播图,废话不说了,先来看效果:
动画很流畅(原谅我录屏的帧率比较小,转成gif就...),没有出现卡顿,同时支持手势滑动、点击事件,没有出现滑动冲突。总体来说效果还可以吧。
实现原理很简单,开启个定时任务不断来回滚动几张图片,同Android使用ScrollView实现以及前端实现方法基本一致。代码全部使用的官方API,比较容易理解。
轮播图组件代码
/**
* @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;
功能逻辑比较简单,代码里有注释,我就不再做多余的介绍了。
使用
import FocusImage from './../components/FocusImage';
...
render(
<View>
<FocusImage />
</View>
);
...