前言:这些天项目不是很紧,所以比较闲,然后就胡思乱想,居然还想去学校读书,有种想去考研的冲动了哈~~唉唉!又总感觉自己满腔热血想在这大城市闯出自己一片天地,但是又有心无力,尼玛~唯有撸代码才能平复我那不安的小内心啊~废话不多说了,赶紧进入我们今天的主题
项目中用到了progressBar,虽然说rn有ProgressBarAndroid跟ProgressViewIOS,但是当我用到ProgressBarAndroid的时候就会报一个啥“indeterminate属性已经过时的警告“,但是我需要用到水平的progressbar必须把indeterminate设置为false,在这种尴尬的场景下,我觉得有必要自定义一个progressbar。
先看看效果图:
嗯嗯~ 效果还是很不错的,让我们来实现一下吧..
首先,我们创建一个js文件叫CusProgressBar.js,然后定义一些我们需要传递的参数:
export default class CusProgressBar extends Component { static propTypes = { ...View.propTypes, //当前进度 progress: PropTypes.number, //second progress进度 buffer: PropTypes.number, //进度条颜色 progressColor: PropTypes.string, //buffer进度条颜色 bufferColor: PropTypes.string, //进度动画时长 progressAniDuration: PropTypes.number, //buffer动画时长 bufferAniDuration: PropTypes.number } static defaultProps = { //进度条颜色 progressColor: 'white', //buffer进度条颜色 bufferColor: 'rgba(255,0,0,0.7)', //进度条动画时长 progressAniDuration: 300, //buffer进度条动画时长 bufferAniDuration: 300 } // 构造,定义两个动画 constructor(props) { super(props); this._progressAni = new Animated.Value(0); this._bufferAni = new Animated.Value(0); } }然后当我们的自定义view收到外面传递的props的时候把当前进度跟buffer进度赋给类成员变量_progress跟_buffer:
componentWillReceiveProps(nextProps) { this._progress = nextProps.progress; this._buffer = nextProps.buffer; } componentWillMount() { this._progress = this.props.progress; this._buffer = this.props.buffer; }拿到外界传递的属性值,然后就是我们的核心代码render方法了:
render() { return ( <View style={[styles.container,this.props.style]} onLayout={this._onLayout.bind(this)} > <Animated.View ref="progress" style={{ position:'absolute', width:this._progressAni, backgroundColor:this.props.progressColor }} /> <Animated.View ref="buffer" style={{ position:'absolute', width:this._bufferAni, backgroundColor:this.props.bufferColor }} /> </View> ); }render也很简单,就是一个view包含了两个Animated.View ,通过动态控制这两个view的宽度就可以实现进度条了(是不是很简单)但是我们的进度条的高度怎么确定呢?? 没错,我们需要把父布局的高度赋给进度条view,我们知道rn中获取控件宽高的方法,就是监听view的onlayout方法,然后就可以获取宽度等属性值了。
_onLayout({nativeEvent:{layout:{width,height}}}) { //防止多次调用,当第一次获取后,后面就不再去获取了 if (width > 0 && this.totalWidth !== width) { //获取progress控件引用 let progress = this._getProgress(); //获取buffer控件引用 let buffer = this._getBuffer(); //获取父布局宽度 this.totalWidth = width; //给progress控件设置高度 progress.setNativeProps({ style: { height: height } }); //给buffer控件设置高度 buffer.setNativeProps({ style: { height: height } }); //开始执行进度条动画 this._startAniProgress(this.progress); //开始执行buffer动画 this._startAniBuffer(this.buffer); } }当我们获取到宽高的时候,然后就可以根据我们传递的progress去执行动画了,动画的值从(0-progress*父布局宽度)。
_startAniProgress(progress) { if (this._progress >= 0 && this.totalWidth != 0) { Animated.timing(this._progressAni, { toValue: progress * this.totalWidth, duration: this.props.progressAniDuration, easing: Easing.linear }).start(); } } _startAniBuffer(buffer) { if (this._buffer >= 0 && this.totalWidth != 0) { Animated.timing(this._bufferAni, { toValue: buffer * this.totalWidth, duration: this.props.bufferAniDuration, }).start(); } } _getProgress() { if (typeof this.refs.progress.refs.node !== 'undefined') { return this.refs.progress.refs.node; } return this.refs.progress._component; } _getBuffer() { if (typeof this.refs.buffer.refs.node !== 'undefined') { return this.refs.buffer.refs.node; } return this.refs.buffer._component; }好啦~~!! 到这里的话我们的控件就可以跑起来了。
怎么用呢??
首先引入我们的控件:
import Progress from '../Common/CusProgressBar';然后就可以渲染控件了:
render(){ return( <View> <Progress ref="progressBar" style={{ marginTop:100, }} progress={0.8} /> </View> ); }那如果我们要时时更新我们的progress,让它动起来的话,该怎么做呢?
1、定义一个state,然后动态修改state,最后赋给CusProgressBar即可。 2、通过直接设置控件的progress和buffer值,然后执行动画就可以了。
1跟2方法中,我们肯定推荐第二种,因为改变state方式的话程序性能上不太好,所以我们用第二种方式:
打开我们的CusProgressBar,加上如下代码
Object.defineProperty(CusProgressBar.prototype, 'progress', { set(value){ if (value >= 0 && this._progress != value) { this._progress = value; this._startAniProgress(value); } }, get() { return this._progress; }, enumerable: true, }); Object.defineProperty(CusProgressBar.prototype, 'buffer', { set(value){ if (value >= 0 && this._buffer != value) { this._buffer = value; this._startAniBuffer(value); } }, get() { return this._buffer; }, enumerable: true, });然后我们外面调用的时候可以这样写了:
1、获取控件的引用。 2、动态设置progress即可。
render(){ return( <View> <Progress ref="progressBar" style={{ marginTop:100, }} /> </View> ); } componentDidMount() { let self=this; this.timer=setInterval(()=>{ if(self.currProgress>=1){ clearTimeout(this.timer); } self.currProgress+=0.1; self.refs.progressBar.progress=self.currProgress; },1000); }对Object.defineProperty不懂的童鞋,可以看看这篇文章:
解析 神奇的 Object.defineProperty
最后附上demo的所有代码:
CusProgressBar.js:
/** * @author YASIN * @version [React Native PABank V01,17/3/10] * @date 17/3/10 * @description CusProgressBar */ import React,{Component,PropTypes}from 'react'; import { View, StyleSheet, Animated, Easing }from 'react-native'; export default class CusProgressBar extends Component { static propTypes = { ...View.propTypes, //当前进度 progress: PropTypes.number, //second progress进度 buffer: PropTypes.number, //进度条颜色 progressColor: PropTypes.string, //buffer进度条颜色 bufferColor: PropTypes.string, //进度动画时长 progressAniDuration: PropTypes.number, //buffer动画时长 bufferAniDuration: PropTypes.number } static defaultProps = { //进度条颜色 progressColor: 'white', //buffer进度条颜色 bufferColor: 'rgba(255,0,0,0.7)', //进度条动画时长 progressAniDuration: 300, //buffer进度条动画时长 bufferAniDuration: 300 } // 构造 constructor(props) { super(props); this._progressAni = new Animated.Value(0); this._bufferAni = new Animated.Value(0); } componentWillReceiveProps(nextProps) { this._progress = nextProps.progress; this._buffer = nextProps.buffer; } componentWillMount() { this._progress = this.props.progress; this._buffer = this.props.buffer; } render() { return ( <View style={[styles.container,this.props.style]} onLayout={this._onLayout.bind(this)} > <Animated.View ref="progress" style={{ position:'absolute', width:this._progressAni, backgroundColor:this.props.progressColor }} /> <Animated.View ref="buffer" style={{ position:'absolute', width:this._bufferAni, backgroundColor:this.props.bufferColor }} /> </View> ); } _onLayout({nativeEvent:{layout:{width,height}}}) { //防止多次调用,当第一次获取后,后面就不再去获取了 if (width > 0 && this.totalWidth !== width) { //获取progress控件引用 let progress = this._getProgress(); //获取buffer控件引用 let buffer = this._getBuffer(); //获取父布局宽度 this.totalWidth = width; //给progress控件设置高度 progress.setNativeProps({ style: { height: height } }); //给buffer控件设置高度 buffer.setNativeProps({ style: { height: height } }); //开始执行进度条动画 this._startAniProgress(this.progress); //开始执行buffer动画 this._startAniBuffer(this.buffer); } } _startAniProgress(progress) { if (this._progress >= 0 && this.totalWidth != 0) { Animated.timing(this._progressAni, { toValue: progress * this.totalWidth, duration: this.props.progressAniDuration, easing: Easing.linear }).start(); } } _startAniBuffer(buffer) { if (this._buffer >= 0 && this.totalWidth != 0) { Animated.timing(this._bufferAni, { toValue: buffer * this.totalWidth, duration: this.props.bufferAniDuration, }).start(); } } _getProgress() { if (typeof this.refs.progress.refs.node !== 'undefined') { return this.refs.progress.refs.node; } return this.refs.progress._component; } _getBuffer() { if (typeof this.refs.buffer.refs.node !== 'undefined') { return this.refs.buffer.refs.node; } return this.refs.buffer._component; } } Object.defineProperty(CusProgressBar.prototype, 'progress', { set(value){ if (value >= 0 && this._progress != value) { this._progress = value; this._startAniProgress(value); } }, get() { return this._progress; }, enumerable: true, }); Object.defineProperty(CusProgressBar.prototype, 'buffer', { set(value){ if (value >= 0 && this._buffer != value) { this._buffer = value; this._startAniBuffer(value); } }, get() { return this._buffer; }, enumerable: true, }); const styles = StyleSheet.create({ container: { height: 4, backgroundColor: 'black' } });测试页面:
export default class AppMore extends Component{ // 构造 constructor(props) { super(props); // 初始状态 this.state = { progress:0 }; this.currProgress=0; this.currBuffer=0; } render(){ return( <View> <Progress ref="progressBar" style={{ marginTop:100, }} /> </View> ); } componentDidMount() { let self=this; this.timer=setInterval(()=>{ if(self.currProgress>=1){ clearTimeout(this.timer); } self.currProgress+=0.1; self.refs.progressBar.progress=self.currProgress; },1000); } }好啦~~~~结束了,要rn学习资料的可以找我哦,一起学习,一起进步…….
vv_小虫 认证博客专家 JavaScript CSS 前端框架 6 年开发经验,前端架构师,目前主要负责企业级应用前端技术平台建设工作,在前端工程化实现、Node 应用开发、Android 技术、Vue 技术、React 技术、移动开发等方向有丰富实践。