网站里看个视频教程,点开新页面查资料再回来,进度条直接归零,这种抓狂时刻谁没经历过?其实这事儿完全有解,而且不用啥黑科技,靠浏览器自带的那点东西就能整得明明白白。
啥是视图过渡
视图过渡这词听着玄乎,说白了就是页面切换时的动画效果。以前想搞这种丝滑效果,基本得靠单页应用框架硬撑。现在原生CSS的视图过渡特性,能让页面跳转时把旧页面的内容“变”成新页面的一部分,视觉上连成一片。但这里有个坑——视频这种有状态的东西,光靠CSS搞不定,得让浏览器记住视频播到哪儿了。
保存视频状态
核心思路其实特简单:跳页前把视频的当前时间存起来,新页面加载时再读出来塞回去。浏览器里有个叫sessionStorage的地儿,专门用来存这种临时数据,页面关了就自动清,正合适。
// 监听页面即将跳转
window.addEventListener('pageswap', (event) => {
const video = document.querySelector('video');
if (video) {
const videoState = {
currentTime: video.currentTime,
paused: video.paused,
volume: video.volume
};
sessionStorage.setItem('videoState', JSON.stringify(videoState));
}
});这里要注意,pageswap事件在页面开始切换时触发,时间点掐得刚刚好。要是用beforeunload也不是不行,但那个触发时机有时候会跟新页面的加载打架,导致存了个寂寞。另外存数据时得用JSON.stringify包一下,不然对象存进去就变成[object Object]这种废柴东西。
恢复视频状态
新页面登场时,得把之前存的宝贝翻出来用。pagereveal事件就是干这活儿的,它在新页面内容刚出来还没完全展示的时候触发,这时候改视频播放位置,用户基本感觉不到跳帧。
window.addEventListener('pagereveal', (event) => {
const savedState = sessionStorage.getItem('videoState');
if (savedState) {
const video = document.querySelector('video');
if (video) {
const state = JSON.parse(savedState);
video.currentTime = state.currentTime;
if (!state.paused) {
video.play();
}
video.volume = state.volume;
}
sessionStorage.removeItem('videoState');
}
});恢复完之后顺手把sessionStorage里的数据清掉,免得后面莫名其妙又被读一次。这招对音频、内嵌的iframe视频同样管用,只要是能控制播放进度的媒体元素,基本都能这么搞。
备胎方案
要是觉得pageswap和pagereveal这套组合拳太新,担心老浏览器不认账,可以换个稳一点的打法。
// 方案二:用pagehide和pageshow兜底
window.addEventListener('pagehide', () => {
const video = document.querySelector('video');
if (video) {
sessionStorage.setItem('videoCurrentTime', video.currentTime);
sessionStorage.setItem('videoPaused', video.paused);
}
});
window.addEventListener('pageshow', () => {
const savedTime = sessionStorage.getItem('videoCurrentTime');
if (savedTime !== null) {
const video = document.querySelector('video');
if (video) {
video.currentTime = parseFloat(savedTime);
const wasPaused = sessionStorage.getItem('videoPaused') === 'true';
if (!wasPaused) {
video.play();
}
}
sessionStorage.removeItem('videoCurrentTime');
sessionStorage.removeItem('videoPaused');
}
});这套方案的缺点在于,pagehide触发时页面还没完全卸载,但新页面加载时可能会有极短的音频中断,视频会闪一下。不过对于大部分普通场景来说,这点瑕疵完全可以接受,毕竟总比进度丢了强。
避坑指南
实际操作时容易踩几个雷。一个是存状态时如果视频还没加载完,currentTime可能是个假值,最好等视频的loadedmetadata事件触发后再读。另一个是多个视频的情况,存的时候得区分开,比如用视频的id或者src做key,不然两个视频会互相覆盖。
还有个细节,有些视频源是分段加载的(m3u8这类),直接改currentTime可能跳不准,得看播放器本身支不支持seek操作。这时候就得结合播放器自己的API来搞,比如用video.js或者plyr这类库,它们都有现成的状态保存方法。
