900字范文,内容丰富有趣,生活中的好帮手!
900字范文 > -09-26 工作记录--React-js将时间戳转换成“时分秒”+“时分秒”的倒计时(含tab切换)

-09-26 工作记录--React-js将时间戳转换成“时分秒”+“时分秒”的倒计时(含tab切换)

时间:2019-01-09 05:24:14

相关推荐

-09-26 工作记录--React-js将时间戳转换成“时分秒”+“时分秒”的倒计时(含tab切换)

一、实现效果

假如后端返了两个数据:当前时间13位时间戳currentTimestamp秒抢时间13位时间戳seckillTimestamp,想实现“距开始秒抢”的倒计时,即:秒抢时间与当前时间之间的时间差以“时分秒”的形式进行倒计时,如下动图:

二、实现方式

第一步、封装方法——js将时间戳转换成“时分秒” ⭐️

【注意】:是“时分秒”,不是 “天时分秒”哦~ (二者写法是有区别滴哦)

utils.js

/** 1、js获取 倒计时 时分秒通过时间戳的方式来 let h = Math.trunc(dec / 3600); // 时let m = Math.trunc(dec % 3600 / 60); // 分let s = Math.trunc(dec % 3600 % 60); // 秒2、参数分析:@param {Num} inputTime: 需要转换成 时分秒 的 13位时间戳@param {Boolean} isPop: 便于实现两种返回结果:true -> `${h}小时${m}分${s}秒` ; false -> 包含时分秒的finalDateObj对象*/export function timestampFormatter(inputTime, isPop = false) {// 最终时间结果对象const finalDateObj = {h: null, // 小时m: null, // 分钟s: null, // 秒}// 剩余时间总的毫秒数 除以 1000 变为总秒数(时间戳为13位 需要除以1000,为10位 则不需要)let dec = inputTime / 1000;if (dec <= 0) {dec = 0;}// 得到小时 格式化成前缀加零的样式let h = Math.trunc(dec / 3600);h = h < 10 ? '0' + h : h;// 得到分钟 格式化成前缀加零的样式let m = Math.trunc(dec % 3600 / 60);m = m < 10 ? '0' + m : m;// 得到秒 格式化成前缀加零的样式let s = Math.trunc(dec % 3600 % 60);s = s < 10 ? '0' + s : s;finalDateObj.h = h;finalDateObj.m = m;finalDateObj.s = s;return isPop ? `${h}小时${m}分${s}秒` : finalDateObj;}

第二步、实现“时分秒”的倒计时

实现思路同我的另外一篇博文,相比其代码,只是有两处区别,如下:

countDownComponent.jsx

这是含倒计时的子组件。

1、倒计时初始值

参考项目代码:

// 1、初始化statestate = {canCountDown: true, // 判断是否开启定时器finalDate: '00天00小时00分00秒', // 倒计时初始值}

现项目代码:⭐️

// 1、初始化statestate = {canCountDown: true, // 判断是否开启定时器h1: '0', // 倒计时初始值-时h2: '0', // 倒计时初始值-时m1: '0', // 倒计时初始值-分m2: '0', // 倒计时初始值-分s1: '0', // 倒计时初始值-秒s2: '0', // 倒计时初始值-秒 }

2、获取到倒计时最终值【位于方法updateTime里】

参考项目代码:

/** 调用上面封装的方法timestampFormatter,计算到倒计时的展示结果,并state赋值,方便html里进行展示 */const result = timestampFormatter(countDownTimestamp, true);this.setState({finalDate: result,})

现项目代码:⭐️

/** 调用上面封装的方法timestampFormatter,计算到倒计时的展示结果,并state赋值,方便html里进行展示 */const _h = timestampFormatter(countDownTime).h + '';const _m = timestampFormatter(countDownTime).m + '';const _s = timestampFormatter(countDownTime).s + '';this.setState({h1: _h.slice(0, 1),h2: _h.slice(-1),m1: _m.slice(0, 1),m2: _m.slice(-1),s1: _s.slice(0, 1),s2: _s.slice(-1),})

第三步、HTML渲染 ⭐️

render(){const {h1, h2, m1, m2, s1, s2 } = this.state;return(<div __examplenotes="倒计时" className="countDown"><span __examplenotes="倒计时背景" className="countDownBg"></span><span __examplenotes="时" className="commonTime hour1">{h1}</span><span __examplenotes="时" className="commonTime hour2">{h2}</span><span __examplenotes="分" className="commonTime minute1">{m1}</span><span __examplenotes="分" className="commonTime minute2">{m2}</span><span __examplenotes="秒" className="commonTime second1">{s1}</span><span __examplenotes="秒" className="commonTime second2">{s2}</span></div>)}

三、坑💥

如上结果动图可知:我们实现的是一个可以tab切换的倒计时项目。

实现思路:把倒计时相关的部分封装成一个组件,随之渲染页面。坑💥:当tab1里的第一条数据的倒计时小于0时,tab2里的第一条数据的倒计时 自动归置为0(即使 其并非为0);当tab1里的第二条数据的倒计时小于0时,tab2里的第二条数据的倒计时 自动归置为0(即使 其并非为0);依次类推。。。分析原因:原来是因为我们在更新倒计时时间时定义了如下代码【当倒计时小于0时,定时器被关闭了】,如下图所示:

解决方法:我们需要在切换tab时,判断组件的定时器timer是不是null,如果是nullcountDownTimestamp大于0,调下funTimer(),重新开启定时器,代码如下:【需结合我的另外一篇博文一起看哦~】

countDownComponent.jsx

这是含倒计时的子组件。

componentDidMount() {// 发送事件"startTimer"document.addEventListener("startTimer", this.startTimer, this);}componentWillUnmount() {// 移除事件"startTimer"document.removeEventListener("startTimer", this.startTimer, this);}// 判断组件的定时器timer是不是null,如果是null且countDown大于0,调下funTimer()startTimer = () => {/** seckillTimestamp:秒杀开始时间的时间戳,currentTimestamp:当前时间的时间戳 */const seckillTimestamp = 1664280000000; // 秒杀开始时间【实际情况时 由后台返回,这儿写死只是做个测试】/** 计算倒计时的展示结果,并return值,方便html里进行展示 */const countDownTimestamp = seckillTimestamp * 1 - this.currentTimestamp * 1; // 计算得到 倒计时 的时间戳if (this.timer == null && countDownTimestamp > 0) {this.FunTimer();}}

homePage.jsx

这是调用倒计时子组件的首页

// 标题tab切换按钮 - 0->今日秒抢, 1->明日秒抢switchTitle = _throttle(async (index) => {// 调用组件CountDownComponent里的方法document.dispatchEvent(new CustomEvent("startTimer", {}));}, 1000)

四、完整代码 ⭐️

父页面homePage.jsx

'use strict';import React from 'react';import {observer } from 'mobx-react';import store from '@src/store';import {_throttle } from '@src/utils/utils';import SeckillDetailComponent from './seckillDetailComponent/seckillDetailComponent'; // 引入秒杀详情组件import modalStore from '@src/store/modal';import {Toast } from '@spark/ui';import {CHANNEL_ERROR } from '@src/utils/constants';import './homePage.less';@observerclass HomePage extends ponent {constructor(props) {super(props);}componentDidMount() {store.getHomeData(); // 请求接口-首页}// 点击「奖品」按钮,进入奖品页面toPrizePage = _throttle(() => {const {// 首页接口是否报错isError,// 0:不在区域 1:北京2:上海3:深圳4:杭州orgNo,// 除错误码600004以外的提示信息errorMessage,} = store;(!isError && orgNo != 0) ? store.changePage('myPrizePage') : Toast(errorMessage ? errorMessage : CHANNEL_ERROR);})// 点击「规则」按钮,打开【活动规则弹窗】openRulePop = _throttle(() => {const {// 首页接口是否报错isError,// 0:不在区域 1:北京2:上海3:深圳4:杭州orgNo,// 除错误码600004以外的提示信息errorMessage,} = store;(!isError && orgNo != 0) ? modalStore.pushPop('PublicPopupWindow', {type: 'rule' }) : Toast(errorMessage ? errorMessage : CHANNEL_ERROR);})// 标题切换按钮 - 0->今日秒抢, 1->明日秒抢switchTitle = _throttle(async (index) => {// 设置当前展示日的索引值store.setSeckillIndex(index);// 刷新首页数据await store.getHomeData();// 调用组件SeckillDetailComponent里的方法document.dispatchEvent(new CustomEvent("startTimer", {}));}, 1000)render() {const {// 当前展示的秒杀数据seckillData,// 当前展示的秒杀数据的索引值 0-今日秒抢 1-明日秒抢seckillIndex,} = store;return (<div className="homePage"><span __examplenotes="首页背景" className="homeBackground"></span><div __examplenotes="秒杀活动" className="seckill"><div __examplenotes="第一场秒杀活动" className="seckillFirst"><div __examplenotes="秒杀标题" className={`seckillBg ${seckillIndex == 0 ? 'todaySeckillBg' : 'tomorrowSeckillBg'}`}>{Array(2).fill('').map((item, index) => {return (<div __examplenotes="标题切换按钮-今日秒抢/明日秒抢" className="switchBtn" onClick={() => {this.switchTitle(index) }} key={index}></div>)})}</div><span __examplenotes="第一场秒杀活动背景" className="underFrame"></span><div __examplenotes="第一场秒杀活动详情" className="seckillItem">{seckillData?.length > 0 && seckillData[0] != undefined && <SeckillDetailComponent seckillDetail={seckillData[0]} />}</div></div>{seckillData?.length > 1 && seckillData[1] != undefined && <div __examplenotes="第二场秒杀活动" className="seckillSecond"><span __examplenotes="第二场秒杀活动背景" className="underBorder"></span><div __examplenotes="第二场秒杀活动详情" className="seckillItem2"><SeckillDetailComponent seckillDetail={seckillData[1]} /></div></div>}</div><span __examplenotes="按钮-奖品" className="prize" onClick={this.toPrizePage}></span><span __examplenotes="按钮-规则" className="rule" onClick={this.openRulePop}></span></div>);}}export default HomePage;

子组件seckillDetailComponent.jsx

'use strict';import React from 'react';import {observer } from 'mobx-react';import store from '@src/store';import {_throttle } from '@src/utils/utils';import {Toast } from '@spark/ui';import {timestampFormatter } from '@src/utils/utils'; // 引入上面封装好的方法——`js`将时间戳转换成“时分秒”import './seckillDetailComponent.less';@observerclass SeckillDetailComponent extends ponent {constructor(props) {super(props);// 1、初始化statethis.state = {canCountDown: true, // 判断是否开启定时器h1: '0', // 倒计时初始值-时h2: '0', // 倒计时初始值-时m1: '0', // 倒计时初始值-分m2: '0', // 倒计时初始值-分s1: '0', // 倒计时初始值-秒s2: '0', // 倒计时初始值-秒 }}// 当前时间的时间戳【注意: currentTime一定要记得*1转换成数值型哟】—— 这里是组件,所以可以直接这样写获取到store?.currentTimecurrentTime = store?.currentTime * 1;// 2、初始化定时器timer = null;componentDidMount() {this.handleCountDownLogic();// 发送事件"startTimer"document.addEventListener("startTimer", this.startTimer, this);}componentWillUnmount() {// 清除定时器clearInterval(this.timer);// 移除事件"startTimer"document.removeEventListener("startTimer", this.startTimer, this);}// 判断组件的定时器timer是不是null,如果是null且countDown大于0,调下funTimer()startTimer = () => {/** currentTime:当前时间,startTime:秒杀开始时间 */const {startTime } = this.props?.seckillDetail || {};/** 计算倒计时的展示结果,并return值,方便html里进行展示 */const countDownTime = startTime * 1 - this.currentTime * 1; // 计算得到 倒计时 的时间戳if (this.timer == null && countDownTime > 0) {this.FunTimer();}}// 处理倒计时总逻辑handleCountDownLogic() {// 倒计时if (this.state.canCountDown) {this.setState({canCountDown: false })this.FunTimer(); // 调用函数} else {this.setState({canCountDown: true })clearInterval(this.timer); // 关闭定时器}}// 封装函数-倒计时FunTimer = () => {this.timer = setInterval(() => {// 想实现倒计时的代码就写在这里啦~【我单独封装了一个函数】this.updateTime();}, 1000)}// 更新时间updateTime = () => {/** currentTime:当前时间,startTime:秒杀开始时间 */const {startTime } = this.props?.seckillDetail || {};/** 手动把获取到的当前时间 隔一秒加1s*/this.currentTime += 1000;/** 计算倒计时的展示结果,并return值,方便html里进行展示 */const countDownTime = startTime * 1 - this.currentTime * 1; // 计算得到 倒计时 的时间戳const _h = timestampFormatter(countDownTime).h + '';const _m = timestampFormatter(countDownTime).m + '';const _s = timestampFormatter(countDownTime).s + '';this.setState({h1: _h.slice(0, 1),h2: _h.slice(-1),m1: _m.slice(0, 1),m2: _m.slice(-1),s1: _s.slice(0, 1),s2: _s.slice(-1),})// 倒计时小于0,则刷新首页数据、关闭定时器if (countDownTime < 0) {console.log('倒计时小于0');clearInterval(this.timer); // 关闭定时器this.timer = null; // 将定时器归置为null// 根据自己需求进行操作store.getHomeData(); // 刷新首页数据}}/*** 点击对应按钮* @param {Num} type 1->按钮-立即秒杀 2->按钮-已抢到 3->按钮-已抢完*/clickButton = _throttle((type) => {// orgNo 0:不在区域 1:北京2:上海3:深圳4:杭州const {orgNo } = store;// goodsId: 商品id, name: 奖品名称const {goodsId, name } = this.props?.seckillDetail || {};switch (type) {case 1:store.doSeckill(goodsId, orgNo, name);break;case 2:Toast(`您已抢到${name},请前往【奖品】查看`);break;case 3:Toast(`本场${name}已抢完`);break;}})render() {// 需渲染的秒杀数据const {seckillDetail } = this.props;const {// 首页接口是否报错isError,// 0:不在区域 1:北京2:上海3:深圳4:杭州orgNo,// 活动是否结束 true:活动结束ifEnd,} = store;const {h1, h2, m1, m2, s1, s2 } = this.state;return (<div>{!isError && !ifEnd && orgNo != 0 &&<div __examplenotes="秒杀详情组件" className="seckillDetailComponent"><div __examplenotes="奖品介绍" className="prizeIntroduction"><span __examplenotes="奖品介绍背景" className="prizeBg"></span><div __examplenotes="奖品图片" className="prizeImage"><img src={seckillDetail?.icon} alt="" /></div><span __examplenotes="奖品名称" className="prizeName text-hidden-ellipsis">{seckillDetail?.name}</span><span __examplenotes="奖品剩余量" className="surplus text-hidden-ellipsis">{seckillDetail?.status != 0 && seckillDetail?.status != 3 && seckillDetail?.status != 0 && '正在疯抢 '}剩余:{seckillDetail?.stock}</span><div __examplenotes="滚动条" className="scrollbar"><div __examplenotes="滚动条外壳" className="outerScrollbar"><span __examplenotes="滚动条内里" className="insideScrollbar" style={{'transform': `translateX(${seckillDetail?.process}%)` }}></span></div></div><div __examplenotes="按钮" className="button">{seckillDetail?.status == 0 && <span __examplenotes="按钮-等待开抢" className="commonButton btn-waitToSeckill"></span>}{seckillDetail?.status == 1 && <span __examplenotes="按钮-立即秒杀" className="commonButton btn-immediatelySeckill" onClick={() => {this.clickButton(1) }}></span>}{seckillDetail?.status == 2 && <span __examplenotes="按钮-已抢到" className="commonButton btn-gotten" onClick={() => {this.clickButton(2) }}></span>}{seckillDetail?.status == 3 && <span __examplenotes="按钮-已抢完" className="commonButton btn-robbed" onClick={() => {this.clickButton(3) }}></span>}</div></div><div __examplenotes="倒计时" className="countDown"><span __examplenotes="倒计时背景" className="countDownBg"></span><span __examplenotes="时" className="commonTime hour1">{h1}</span><span __examplenotes="时" className="commonTime hour2">{h2}</span><span __examplenotes="分" className="commonTime minute1">{m1}</span><span __examplenotes="分" className="commonTime minute2">{m2}</span><span __examplenotes="秒" className="commonTime second1">{s1}</span><span __examplenotes="秒" className="commonTime second2">{s2}</span></div><div __examplenotes="场次标题" className="openTitle"><span __examplenotes="场次标题背景" className="titleBg"></span><span __examplenotes="场次标题内容" className="titleName text-hidden-ellipsis">{seckillDetail?.title}</span></div></div>}</div>);}}export default SeckillDetailComponent;

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。