最近研究了一下倒计时,网上有不少第三方开源库,不过试了试,有的并不好用,写法也停留在ES5上,而我写的demo使用的是0.45版本的RN,改起来很是麻烦。于是就参考react-native-CountDowntimer实现了一下倒计时按钮,效果还不错~
一、效果图
1.ios效果图
2.android效果图
二、基本原理
1.作为一个组件,要做到灵活可变:
我们将要使其样式、提示文字及显示方式可以自定义。那就要将文字样式,提示文字(包括显示的时间数字,时间单位等)都定义为组件属性,可以由调用者灵活定义。
2.基本原理:
首先用户输入一个结束时间,我们需要声明一个state,再写一个计时器,我们每隔1s就通过输入的结束时间和当前时间计算出时间差,再通过算法将时间差计算成xx天xx时xx分xx秒的格式,更改state的值,通过state的更改及判断计算结果,在render()方法中控制时间的显示及更新。
三、代码实现
(一)自定义倒计时组件
新建countDown.js
/**
* Created by sybil052 on 2017/6/22.
*/
import React, {
Component,
PropTypes,
} from 'react';
import {
StyleSheet,
View,
Text,
} from 'react-native';
const styles = StyleSheet.create({
text: {
fontSize: 30,
color: '#FFF',
marginLeft: 7,
},
container: {
flexDirection: 'row',
},
// 时间文字
defaultTime: {
paddingHorizontal: 3,
backgroundColor: '#555555',
fontSize: 12,
color: '#fff',
marginHorizontal: 3,
borderRadius: 2,
},
// 冒号
defaultColon: {
fontSize: 12,
color: '#555555'
},
// 提示
defaultTip: {
fontSize: 12,
color: '#555555',
}
});
class CountDown extends Component {
static displayName = 'Simple countDown';
// 属性
static propTypes = {
tip: PropTypes.string,
date: PropTypes.string,
days: PropTypes.objectOf(PropTypes.string),
hours: PropTypes.string,
mins: PropTypes.string,
segs: PropTypes.string,
onEnd: PropTypes.func,
containerStyle: View.propTypes.style,
daysStyle: Text.propTypes.style,
hoursStyle: Text.propTypes.style,
minsStyle: Text.propTypes.style,
secsStyle: Text.propTypes.style,
firstColonStyle: Text.propTypes.style,
secondColonStyle: Text.propTypes.style,
tipStyle: Text.propTypes.style,
};
// 默认属性
static defaultProps = {
date: new Date(),
days: {
plural: '天',
singular: '天',
},
hours: ':',
mins: ':',
segs: ':',
tip: '',
onEnd: () => {},
containerStyle: styles.container, // container 的style
daysStyle: styles.defaultTime, // 天数 字体的style
hoursStyle: styles.defaultTime, // 小时 字体的style
minsStyle: styles.defaultTime, // 分钟 字体的style
secsStyle: styles.defaultTime, // 秒数 字体的style
firstColonStyle: styles.defaultColon, // 从左向右 第一个冒号 字体的style
secondColonStyle: styles.defaultColon, // 从左向右 第2个冒号 字体的style
tipStyle: styles.defaultTip, // 提示字体style
};
state = {
days: 0,
hours: 0,
min: 0,
sec: 0,
};
componentDidMount() {
this.interval = setInterval(()=> {
const date = this.getDateData(this.props.date);
if (date) {
this.setState(date);
} else {
this.stop();
this.props.onEnd();
}
}, 1000);
}
componentWillMount() {
const date = this.getDateData(this.props.date);
if (date) {
this.setState(date);
}
}
componentWillUnmount() {
this.stop();
}
getDateData(endDate) {
endDate = endDate.replace(/-/g,"/");
console.log('end',new Date(endDate));
console.log('now',new Date);
// console.log('end===',Date.parse(new Date(endDate)));
// console.log('now===',Date.parse(new Date));
let diff = (Date.parse(new Date(endDate)) - Date.parse(new Date)) / 1000;
if (diff <= 0) {
return false;
}
// console.log('===========',diff);
const timeLeft = {
years: 0,
days: 0,
hours: 0,
min: 0,
sec: 0,
millisec: 0,
};
if (diff >= (365.25 * 86400)) {
timeLeft.years = Math.floor(diff / (365.25 * 86400));
diff -= timeLeft.years * 365.25 * 86400;
}
if (diff >= 86400) {
timeLeft.days = Math.floor(diff / 86400);
diff -= timeLeft.days * 86400;
}
if (diff >= 3600) {
timeLeft.hours = Math.floor(diff / 3600);
diff -= timeLeft.hours * 3600;
}
if (diff >= 60) {
timeLeft.min = Math.floor(diff / 60);
diff -= timeLeft.min * 60;
}
timeLeft.sec = diff;
return timeLeft;
}
render() {
const countDown = this.state;
let days;
if (countDown.days === 1) {
days = this.props.days.singular;
} else {
days = this.props.days.plural;
}
return (
<View style={this.props.containerStyle}>
{ this.props.tip ? <Text style={this.props.tipStyle}>{this.props.tip}</Text> : null}
{ (countDown.days > 0) ? <Text style={this.props.daysStyle}>{ this.leadingZeros(countDown.days)+days}</Text> : null}
{ countDown.hours > 0 ? <Text style={this.props.hoursStyle}>{ this.leadingZeros(countDown.hours)}</Text> : null}
{ countDown.hours > 0 ? <Text style={ this.props.firstColonStyle}>{this.props.hours}</Text> : null}
<Text style={this.props.minsStyle}>{this.leadingZeros(countDown.min)}</Text>
<Text style={this.props.secondColonStyle}>{this.props.mins}</Text>
<Text style={this.props.secsStyle}>{this.leadingZeros(countDown.sec)}</Text>
<Text style={this.props.secondColonStyle}>{this.props.segs}</Text>
</View>
);
}
stop() {
clearInterval(this.interval);
}
// 数字前面补0
leadingZeros(num, length = null) {
let length_ = length;
let num_ = num;
if (length_ === null) {
length_ = 2;
}
num_ = String(num_);
while (num_.length < length_) {
num_ = '0' + num_;
}
return num_;
}
}
export default CountDown;
(二)具体使用
来说下我们要实现的效果,传入一个结束时间,开始倒计时,倒计时中,抢单按钮可以点击,当倒计时结束,按钮置灰且不可点击。
新建demo.js
/**
* Created by sybil052 on 2017/6/19.
*/
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {
View,
Text,
StyleSheet,
TouchableOpacity,
} from 'react-native';
import CountDown from '../../components/countDown';
class Demo extends Component {
constructor(props) {
super(props);
this.state={
isEnd: false
}
}
componentDidMount() {
}
render() {
return (
<View style={styles.container}>
<TouchableOpacity
disabled={this.state.isEnd}
onPress={() => {
alert('抢单');
}}>
<View style={{
backgroundColor: this.state.isEnd ? '#666666': '#008BCA',
borderRadius: 5,
margin:20,
padding:12,
flexDirection:'row',
justifyContent:'center',
}}>
<Text style={{
fontSize: 15,
color: 'white',
lineHeight: 17,
marginRight: 5
}}>抢单</Text>
<CountDown
date="2017-06-23 11:04:30"
days={{plural: '天 ',singular: '天 '}}
hours='时'
mins='分'
segs='秒'
tip='还剩'
daysStyle={styles.time}
hoursStyle={styles.time}
minsStyle={styles.time}
secsStyle={styles.time}
firstColonStyle={styles.colon}
secondColonStyle={styles.colon}
tipStyle={styles.tip}
onEnd={() => {
this.setState({
isEnd: true
})
}}
/>
</View>
</TouchableOpacity>
</View>
);
}
}
const styles =StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'white'
},
//时间文字
time: {
paddingHorizontal: 2,
fontSize: 13,
color: 'white',
textAlign: 'center',
lineHeight: 17,
},
//冒号
colon: {
fontSize: 13,
color:'white',
textAlign: 'center',
lineHeight: 17,
},
tip:{
color: 'white',
textAlign: 'center',
fontSize: 13,
lineHeight: 17
}
});
function mapStateToProps(state){
return {};
}
function mapDispatchToProps (dispatch){
return {};
}
export default connect(mapStateToProps, mapDispatchToProps)(Demo);
好了,自定义的倒计时按钮就写好了~希望对你们有所帮助!!!