记录---如何强制用户看完广告?
<h1 data-id="heading-0">🧑💻 写在开头</h1><p>点赞 + 收藏 === 学会🤣🤣🤣</p>
<h2 data-id="heading-0">场景</h2>
<blockquote>
<p>web端有这样一种场景,广告方投放了一些广告,并要求用户停留在当前页面一定时间(例如看完一段30秒的广告)才能收到活动奖励。</p>
<p>用户可能会打开广告后切到其他tab页或者最小化当前页面,去做自己的事情,如何强制用户停留在当前页面,看完广告?</p>
</blockquote>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202503/2149129-20250314170135687-1634587207.png" alt="" loading="lazy"></p>
<p> </p>
<h2 data-id="heading-1">直接使用定时器</h2>
<p>一开始比较容易想到的方案是在页面组件中设置一个定时器,组件渲染时开始计时,倒计时结束后调用成功的回调,代码如下</p>
<div class="cnblogs_Highlighter">
<pre class="brush:bash;gutter:true;">import { useEffect, useRef, useState } from "react";
const Comp = () => {
const = useState(5)
const timer = useRef<NodeJS.Timeout | null>(null)
useEffect(() => {
timer.current = setInterval(() => {
setSeconds(s => s - 1)
}, 1000);
return () => {
timer.current && clearInterval(timer.current)
}
}, [])
const callback = () => {
console.log('倒计时结束,获得奖励')
}
useEffect(() => {
if(seconds <= 0) {
callback()
timer.current && clearInterval(timer.current)
}
}, )
return (
<div>
观看 {seconds} 秒广告后获得现金奖励!!!
</div>
)
};</pre>
</div>
<div>
<div>
<p>代码运行后也确实起到了倒计时的作用,但是用户切到其他页面时,倒计时依然在继续。</p>
<h2 data-id="heading-2">更好的方案</h2>
<p>有时候用户可能打开广告页之后又切到了其他页面,或者将页面最小化,然后去做自己的事情,如何强制用户停留在当前页面看完广告呢? 问题其实出在<code>setInterval</code>上,简单的定时器即使页面不在前台,也依然会默默执行。</p>
<p>有没有一旦用户离开当前页,倒计时就停止执行的方法呢?</p>
<p>有!答案就是<code>requestAnimationFrame</code>。由于浏览器的性能优化的考虑,当页面不在前台时(被最小化或者隐藏),浏览器会暂停<code>requestAnimationFrame</code>的执行。</p>
<p>所以我们可以用<code>requestAnimationFrame</code>实现<code>setInterval</code>,实现如下</p>
</div>
<div class="cnblogs_Highlighter">
<pre class="brush:bash;gutter:true;"> const mySetInterval = (callback: () => void, delay: number = 0) => {
let start = new Date().getTime();
const obj = { timer: 0 }
const cb = () => {
const current = new Date().getTime();
if (current - start >= delay) {
callback();
start = new Date().getTime();
}
obj.timer = requestAnimationFrame(cb);
};
obj.timer = requestAnimationFrame(cb);
return obj;
};</pre>
</div>
</div>
<div>
<div>
<h3 data-id="heading-3">解释</h3>
<p>入参: <code>callback</code>: 一个无参数的回调函数,当达到延迟时间时将调用这个函数。 <code>delay</code>: 一个表示延迟时间的数字(以毫秒为单位)。</p>
<p>返回值: <code>obj</code> 是一个对象,其中包含一个名为 <code>timer</code> 的属性。这个属性用于存储 <code>requestAnimationFrame</code> 的标识符,以便取消动画。</p>
<p><code>start</code> 变量记录了当前的时间戳(以毫秒为单位),这是用来计算时间差的。</p>
<p><code>requestAnimationFrame</code>会在浏览器渲染一帧的空闲时间执行参数的回调,这里在回调cb的中间又加入了<code>requestAnimationFrame(cb)</code>,即通过递归实现每一帧都会执行cb函数,直到累积的时间大于传入的延迟时间,这里的延迟时间即setInterval的第二个参数,当累积时间达到delay之后执行setInterval的第一个参数,同时更新start时间戳,从而模拟出setInterval的效果。</p>
<p>值得注意的点是,该函数返回的其实是一个对象,而不是<code>requestAnimationFrame</code>的返回值(标识符),这里其实利用了闭包,使得返回的是同一个倒计时的标识,方便清空interval的函数使用。</p>
<h3 data-id="heading-4">在组件中使用</h3>
<p>利用我们实现的setInterval,用来代替原生的setInterval,页面在最小化或者用户切到其他tab页时,倒计时不再继续运行,直到用户打开该页面,实现强制用户停留在当前页面观看广告。</p>
</div>
<div class="cnblogs_Highlighter">
<pre class="brush:bash;gutter:true;">import { useEffect, useRef, useState } from "react";
const Comp = () => {
const = useState(30)
const intervalRef = useRef<{timer: number} | null>(null)
const mySetInterval = (callback: () => void, delay: number = 0) => {
let start = new Date().getTime();
const obj = {
timer: 0
}
const cb = () => {
const current = new Date().getTime();
if (current - start >= delay) {
callback();
start = new Date().getTime();
}
obj.timer = requestAnimationFrame(cb);
};
obj.timer = requestAnimationFrame(cb);
return obj;
};
useEffect(() => {
intervalRef.current = mySetInterval(() => {
setSeconds(s => s - 1)
}, 1000);
return () => {
intervalRef.current && cancelAnimationFrame(intervalRef.current.timer)
}
}, [])
const callback = () => {
console.log('倒计时结束,获得奖励')
}
useEffect(() => {
if (seconds <= 0) {
callback()
intervalRef.current && cancelAnimationFrame(intervalRef.current.timer)
}
}, )
return (
<div>
观看 {seconds} 秒广告后获得现金奖励!!!
</div>
)
};</pre>
</div>
<h2 data-id="heading-5">参考</h2>
<blockquote>
<p>can i use 查看浏览器支持版本</p>
<p>MDN关于requestAnimationFrame的说明</p>
<p>ahooks中useRefInterval源码</p>
</blockquote>
<div>
<h2>本文转载于:https://juejin.cn/post/7461206048140361754</h2>
</div>
<h3 id="tid-D8HBxE">如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。</h3>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202501/2149129-20250122165814748-630765389.png" alt="" loading="lazy"></p>
</div><br><br>
来源:https://www.cnblogs.com/smileZAZ/p/18772446
頁:
[1]