【已解决】浏览器小化或者切换标签,倒计时不准确解决方案


在这里插入图片描述

问题出现原因

一般屏幕刷新率为60HZ,也就是1秒刷新60次,大概17毫秒刷新一次。确保每17毫秒都执行代码则不会出现任何问题,由于定时器会收到JS事件队列的宏任务、微任务的影响,所以并不可能保证每17毫秒都执行一次,可能会有延迟,出现丢帧、卡顿的现象。
并且当浏览器标签切换到后台时,浏览器为了提高性能、节省电池和系统资源,以及减少不必要的计算开销,会对特定的任务进行节流后者延迟执行。

    let leftTime = 600 * 1000;
    function cutdownTime() {
    
    
    	let interval = setInterval(() => {
    
    
    		const currentTime = new Date();
    		if (leftTime == 0) {
    
    
    			console.log('结束倒计时')
    			clearInterval(interval)
    		} else {
    
    
    			leftTime -= 1;
    			console.log('剩余时间:', leftTime,'当前时间秒数', currentTime.getSeconds())
    		}
    	}, 1000)
    }
    window.addEventListener("visibilitychange", () => {
    
    
    	if (document.hidden) {
    
    
    		console.log('页面隐藏')
    	}
    })
    cutdownTime()

解决方案

提供两种解决方案,requestAnimationFrame以及Web Workers

1、使用requestAnimationFrame

使用requestAnimationFramerequestAnimationFrame可以按照显示器的刷新率来调度动画帧,能够十分精准严格的卡住显示器刷新的时间,比如60HZ显示器会自动大概17ms执行一次,对于120HZ显示器大概9ms执行一次。 requestAnimationFrame 的 MDN地址

		//设置结束时间为当前时间的10s后结束
		const endTime = Date.now() + 10 * 1000;
		function updateCountdown() {
    
    
		    //当前时间
			const currentTime = Date.now();
			// 时间差
			const tiemDifference = endTime - currentTime;
			if (tiemDifference == 0) {
    
    
				console.log('倒计时结束')
			} else {
    
    
				//计算剩余的时间,时:分:秒
				const seconds = Math.floor((tiemDifference / 1000) % 60)
				const minutes = Math.floor((tiemDifference / 1000 / 60) % 60)
				const hours = Math.floor((tiemDifference / 1000 / 60 / 60) % 24)
				console.log(`剩余时间:${
      
      hours}:${
      
      minutes}:${
      
      seconds}`)
				requestAnimationFrame(updateCountdown)
			}
		}
		requestAnimationFrame(updateCountdown)

2、使用Web Workers

Web Workers可以开辟一个新的线程执行JS代码,不会受到主线程阻塞的影响,所以可以使用它来解决倒计时不准确的问题。

//index.html
	<script type="module">
		// import * as Worker from "./webworks.js"
		let endTime = Date.now() + 10 * 1000;
		// 在 Web Worker 中执行倒计时逻辑
		const worker = new Worker('./webworks.js');
		worker.postMessage(endTime);
		// 主线程监听 Web Worker 的消息
		worker.onmessage = (event) => {
    
    
			const remainingTime = event.data;
			console.log(remainingTime, 'remainingTime')
			//如果剩余时间结束则停止线程
			//Worker线程被终止,就无法重新启动或恢复其执行。
			//因此终止Worker线程 之后,如果需要重新执行相同的任务,需要重新创建一个新的 Worker 实例。
			if (remainingTime) worker.terminate();
		}
	</script>
//webworks.js  监听主线程传递的目标时间
self.onmessage = function(event) {
    
    
	const targetDate = event.data;
  
	// 开始执行倒计时逻辑
	setInterval(function() {
    
    
	  const currentTime = Date.now();
	  const timeDifference = targetDate - currentTime;

	  // 将剩余时间发送回主线程
	  self.postMessage(timeDifference > 0 ? timeDifference : 0);
	}, 1000); // 每隔一秒钟更新一次时间
  };

可能会出现下面的报错信息

3.html:1 Access to script at 'file:///Users/zbs/study/%E6%B5%8F%E8%A7%88%E5%99%A8%E4%BC%98%E5%8C%96%E7%AD%96%E7%95%A5%E5%AF%B9%E5%AE%9A%E6%97%B6%E5%99%A8%E7%9A%84%E5%BD%B1%E5%93%8D/webworks.js' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, isolated-app, chrome-extension, chrome, https, chrome-untrusted.

这个错误说明浏览器拦截了对本地文件系统(file://)的跨域请求。通常 Web Worker 是不能直接从本地文件系统中加载的,这是浏览器的安全策略所限制的

解决方案:

本地服务器启动代码,我用的是Five Server,这样就可以通过 http:// 或 https:// 协议访问你的网页,而不是通过 file:// 协议。
在这里插入图片描述

浏览器地址就变成了以http开头

在这里插入图片描述

好书推荐

TypeScript + Vue.js 前端开发从入门到精通
本书以一个一线前端架构师的视角,深入浅出地介绍TypeScript与Vue.js整合开发大型前端应用的全部技术细节。

全书共17个章节,主要内容包括TypeScript基础、面向对象编程、Vue中的模板、组件属性和方法、用户交互处理、组件基础与进阶、Vue响应性编程、动画技术、脚手架Vue CLI和Vite工具的使用、Element Plus UI组件库以及基于Vue的网络框架vue-axios的应用等。此外,本书还涵盖Vue路由管理和状态管理的内容,并通过实战编程技术论坛系统项目的开发,让读者巩固所学的知识,全面提升自己的前端开发技能。

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_38951259/article/details/134910410