需求:
点击按钮,请求成功返回后,置灰60秒倒计时,不允许点击
碰到问题:
1、页面有个loading,在loading为true时,加载超过1秒后,计数不连贯
2、代码写到一个文件中,耦合严重
3、浏览器切换后定时器停止执行,浏览器切换tab页面后,切换回去,仍有计数,并停止
解决方法
1、在loading结束后,再开始计数
2、抽离代码,模块化
3、浏览器监听事件 document.addEventListener(‘visibilitychange’,()=>{})
浏览器标签页被隐藏或显示的时候会触发visibilitychange事件。
页面代码实现
vue文件
<u-button
:disabled="store.state.timer.remainTime > 0"
:loading="triggerLoading"
type="primary"
@click="handleTriggerTaskUploadSyncReportJob"
>
<template v-if="store.state.timer.remainTime > 0">
<u-icon type="time" />
{{ store.state.timer.remainTime + ' ' }}
</template>
同步记录
</u-button>
import { useStore } from 'vuex';
import { useRouter, onBeforeRouteLeave } from 'vue-router';
import {
triggerTaskUploadSyncReportJobApi
} from '@/api/statistics';
import visibility from '@/mixins/visibility';
import { createTimer, removeTimer } from './hooks/createTimer';
mixins: [visibility],
const triggerLoading = ref(false);
onBeforeRouteLeave(() => removeTimer());
if (triggerLoading.value) {
createTimer(true);
triggerLoading.value = false;
}
const handleTriggerTaskUploadSyncReportJob = () => {
triggerLoading.value = true;
triggerTaskUploadSyncReportJobApi({ uploadDate: parseTime(new Date(), 'YYYY-MM-DD') }).then(
res => {
if (res) {
getList();
}
}
);
};
onMounted(() => {
getList();
createTimer(false);
});
return {
triggerLoading,
handleTriggerTaskUploadSyncReportJob,
store
};
createTimer.js-创建定时器
import store from '@/store';
import { setInterValCustom, cancleInterValCustom } from './timer';
export const generateRemainTime = () => {
if (localStorage.getItem('syncRecordsStartTime')) {
return (
60 - Math.floor((Date.now() - (localStorage.getItem('syncRecordsStartTime') || 0)) / 1000)
);
}
return 0;
};
const isCall = () => {
const remainTime = generateRemainTime();
if (remainTime === 0) {
return true;
}
return remainTime > 0 && store.state.timer.remainTime !== remainTime;
};
const loop = timerId => {
const remainTime = generateRemainTime();
store.dispatch('setRemainTime', remainTime);
store.dispatch('setTimerId', timerId);
if (remainTime <= 0) {
removeTimer();
localStorage.removeItem('syncRecordsStartTime');
}
};
export const createTimer = isActive => {
if (isActive) {
localStorage.setItem('syncRecordsStartTime', Date.now());
}
setInterValCustom(loop, isCall);
};
export const removeTimer = () => {
cancleInterValCustom(store.state.timer.timerId);
store.dispatch('setRemainTime', 0);
store.dispatch('setTimerId', null);
};
timer.js-定时器调用
import { getRequestAnimationFrame, cancelRequestAnimationFrame } from './raf';
export const setInterValCustom = (fn, isCall) => {
let timer;
const raf = getRequestAnimationFrame();
const loop = () => {
timer = raf(loop);
if (isCall()) {
fn.call(this, timer);
}
};
timer = raf(loop);
};
export const cancleInterValCustom = timer => {
cancelRequestAnimationFrame(timer);
};
raf.js-定时器分装
function requestAnimationFramePolyfill() {
let lastTime = 0;
return callback => {
const currTime = new Date().getTime();
const timeToCall = Math.max(0, 16 - (currTime - lastTime));
const id = window.setTimeout(() => {
callback(currTime + timeToCall);
}, timeToCall);
lastTime = currTime + timeToCall;
return id;
};
}
export const getRequestAnimationFrame = () => {
if (window.requestAnimationFrame) {
return window.requestAnimationFrame;
}
return requestAnimationFramePolyfill();
};
export const cancelRequestAnimationFrame = id => {
if (window.cancelAnimationFrame) {
return window.cancelAnimationFrame(id);
}
return clearTimeout(id);
};
const raf = getRequestAnimationFrame();
export const cancelAnimationTimeout = frame => cancelRequestAnimationFrame(frame.id);
export const requestAnimationTimeout = (callback, delay) => {
const start = Date.now();
function timeout() {
if (Date.now() - start >= delay) {
callback();
} else {
frame.id = raf(timeout);
}
}
const frame = {
id: raf(timeout)
};
return frame;
};
moudle/timer.js-存储剩余时间和定时器id
const app = {
state: {
remainTime: 0,
timerId: null
},
mutations: {
REMAIN_TIME: (state, remainTime) => {
state.remainTime = remainTime || 0;
},
TIMER_ID: (state, timerId) => {
state.timerId = timerId;
}
},
actions: {
setRemainTime({ commit }, remainTime) {
commit('REMAIN_TIME', remainTime);
},
setTimerId({ commit }, timerId) {
commit('TIMER_ID', timerId);
}
},
getters: {
remainTime: state => state.remainTime || 0,
timerId: state => state.timerId
}
};
export default app;
visibility.js-浏览器切换后定时器停止执行
import { createTimer, removeTimer } from '@/views/statistics/uploadRecord/hooks/createTimer';
export default {
created() {
window.document.addEventListener('visibilitychange', this.visibilityChange);
},
beforeRouteLeave() {
window.document.removeEventListener('visibilitychange', this.visibilityChange);
},
methods: {
visibilityChange() {
if (document.visibilityState === 'hidden') {
removeTimer();
} else if (document.visibilityState === 'visible') {
createTimer(false);
}
}
}
};
|