Seeking与Seeked事件同时触发,如何获取视频拖动起止时间?
问题描述
我正在开发一个项目,需要记录用户观看视频时的所有操作,比如停止位置、视频观看时长、拖动(seek)的开始与结束时间。大部分功能已经实现,但遇到了问题:无法正确获取拖动的起止时间,怀疑是seeking和seeked事件的触发逻辑没处理好。不想使用任何插件或库,以下是我的代码,希望有人指出遗漏的地方:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <video controls src="sample.mp4"></video> </body> </html>
const sendRequest = (operation, data = false)=>{ const formData = new FormData(); formData.append('operation', operation); if(data !== false) formData.append('data', JSON.stringify(data)); return fetch('https://www.example.com/operations.php', {method: 'post', body: formData}); }; // Video Analytics Start const video = document.querySelector('video'); let playPauseStatus = {}; let videoWatchTime = 0; let watchingVideo = false; let seekStatus = {}; video.addEventListener('play', ()=>{ playPauseStatus.play = Math.floor(video.currentTime); watchingVideo = true; }); video.addEventListener('pause', ()=>{ playPauseStatus.pause = Math.floor(video.currentTime); watchingVideo = false; sendRequest('4', playPauseStatus).then(()=> playPauseStatus = {}); }); video.addEventListener('seeking', ()=>{ seekStatus.start = Math.floor(video.currentTime); watchingVideo = false; }); video.addEventListener('seeked', ()=>{ seekStatus.end = Math.floor(video.currentTime); sendRequest('5', seekStatus).then(()=> seekStatus = {}); }); setInterval(()=>{ if(watchingVideo === true) videoWatchTime += 0.5; }, 500); // Video Analytics End // Pushing Analytics Start document.addEventListener('visibilitychange', ()=>{ if(document.visibilityState === 'hidden'){ sendRequest('6', {'video_watch_time' : videoWatchTime}); } video.pause(); }); // Pushing Analytics End
问题分析与修复方案
1. 拖动起止时间记录错误的核心问题
你当前的代码每次触发seeking事件都会覆盖seekStatus.start,但用户拖动进度条时,seeking事件会被连续触发多次(比如按住鼠标拖动进度条的过程中),最终seekStatus.start会变成拖动过程中的某个中间值,而非真正的拖动起始点。
2. 观看时长统计逻辑不准确
用setInterval累加时长的方式存在误差(比如浏览器后台时定时器会延迟),更可靠的方式是通过记录时间戳来计算实际观看时长。
3. 拖动后未恢复播放状态的时长统计
用户拖动视频后,如果之前处于播放状态,你的代码没有恢复watchingVideo的状态,会导致后续观看时长无法被统计。
修复后的完整代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <video controls src="sample.mp4"></video> </body> </html>
const sendRequest = (operation, data = false)=>{ const formData = new FormData(); formData.append('operation', operation); if(data !== false) formData.append('data', JSON.stringify(data)); return fetch('https://www.example.com/operations.php', {method: 'post', body: formData}); }; // Video Analytics Start const video = document.querySelector('video'); let playPauseStatus = {}; let videoWatchTime = 0; let watchingVideo = false; let seekStatus = {}; let isSeeking = false; // 标记是否正在拖动 let playStartTime = null; // 记录播放开始的时间戳 // 计算并累加观看时长的工具函数 const calculateWatchTime = () => { if (playStartTime && watchingVideo) { const now = Date.now(); videoWatchTime += (now - playStartTime) / 1000; playStartTime = now; } }; video.addEventListener('play', ()=>{ playPauseStatus.play = Math.floor(video.currentTime); watchingVideo = true; playStartTime = Date.now(); // 记录播放开始时间戳 }); video.addEventListener('pause', ()=>{ calculateWatchTime(); // 暂停时计算并累加时长 playPauseStatus.pause = Math.floor(video.currentTime); watchingVideo = false; playStartTime = null; sendRequest('4', playPauseStatus).then(()=> playPauseStatus = {}); }); video.addEventListener('seeking', ()=>{ if (!isSeeking) { // 仅在第一次进入拖动时记录起始时间 seekStatus.start = Math.floor(video.currentTime); isSeeking = true; } calculateWatchTime(); // 拖动时先计算已观看时长 watchingVideo = false; playStartTime = null; }); video.addEventListener('seeked', ()=>{ seekStatus.end = Math.floor(video.currentTime); sendRequest('5', seekStatus).then(()=> seekStatus = {}); isSeeking = false; // 拖动结束,重置状态 // 如果视频当前是播放状态,恢复观看时长统计 if (!video.paused) { watchingVideo = true; playStartTime = Date.now(); } }); // Video Analytics End // Pushing Analytics Start document.addEventListener('visibilitychange', ()=>{ if(document.visibilityState === 'hidden'){ calculateWatchTime(); // 页面隐藏时计算最终时长 sendRequest('6', {'video_watch_time' : Math.floor(videoWatchTime)}); } video.pause(); }); // Pushing Analytics End
关键修复点说明
- 新增
isSeeking标志位,确保只记录拖动的起始点,而非拖动过程中的中间值。 - 改用时间戳计算观看时长,避免
setInterval带来的误差,统计更精准。 - 拖动结束后,判断视频是否处于播放状态,自动恢复观看时长统计。
- 在
seeking、pause、visibilitychange事件中都调用calculateWatchTime,确保所有场景下的时长都被正确累加。
内容的提问来源于stack exchange,提问作者Abdul Raheem Dumrai
相关产品推荐
相关产品推荐

