需求背景:移动端全屏视频做背景
先简要说说video在移动端的一些坑(详细的可参考其他大神的文章,这里不赘述)
- ios非静音模式下不能自动播放
- 一些内核浏览器会出现全屏浮层
- 显示浏览器自带控制条
- 等
那么如何完美的解决这一系列问题呢?
思考:video自身的问题我们无法解决,那么就考虑用其他方式代替video。
第一次尝试方案:切割成多个gif图,按顺序播放
此方案的问题在于,我们不能保证每个gif的时长相同且永久不变,也无法监听到gif播放完成的动作,使用顺序轮询的方式加载难以控制gif之间的衔接。
第二次尝试方案:图片序列帧技术
什么是图片序列帧技术?说白了就是把视频通过工具转成一堆连续的图片,把这些图片顺序加载,视觉呈现出来就是一个视频的样子。假如一秒8帧,就每125ms更新一次图片资源。
此处推荐一个视频转图片的在线工具
看到有网友用dom创建和销毁的方式去更新图片资源,而且1秒24帧的频率播放。然而dom的创建和销毁过程太耗cpu了,不可取,我使用的是一个img标签,通过修改src的方式更新资源。加之,如果不是对视频质量有太高要求的,可以适当放缓加载频率。
需要注意的点:
- 图片资源先全部预加载之后再开始操作
- 页面销毁时清除定时器
- 初始时图片一个默认src占位
意外惊喜
图片序列帧技术除了可以解决video在移动端的兼容性问题之外,也可以做到其他稍复杂一点的交互。 比如:当视频播放到某一个时刻,换帧、显示文字、显示一些可点击button等一些列交互,主要就是通过判断图片加载的index。
上代码
先预加载图片
/**
* 图片预加载
*
* @param {[]} imgs 图片数组
* @param {function} callback 图片全部加载完的回调
*
*/
export const preLoad = (imgs = [], callback) => {
const arr = [];
for (let i = 0; i <= imgs.length; i++) {
arr[i] = new Image();
arr[i].src = imgs[i];
arr[i].onload = () => {
if (i === imgs.length - 1) {
console.log('全部加载完成');
typeof callback === 'function' && callback();
}
};
}
};
开始实现
import styles from './video.module.scss';
import React, { useRef, useEffect } from 'react';
import ScrollTip from '@/components/m-scroll-tip';
import { preLoad } from '@/stat/utils';
const BannerVideo = () => {
const imgInstance = useRef(null);
const video_timer = useRef(null);
const imgList = () => {//定义图片数组
const arr = [];
for (let i = 0; i <= 100; i++) {
arr.push(`https://xx_${i}.jpg`);
}
return arr;
};
const play = () => {
let count = 0;
const imgs = imgList();
(function a() {
if (count > 100) { count = 0; }//所有图片播放完后从0开始重播
if (imgInstance?.current) {
imgInstance.current.src = imgs[count];//更新图片src
count++;
}
video_timer.current = setTimeout(a, 125);//每125ms执行一次
})();
};
useEffect(() => {
const imgs = imgList();
preLoad(imgs, play);//图片预加载
return () => {
clearTimeout(video_timer.current);//页面销毁清除定时器
};
}, []);
return (
<div className={styles.box}>
<img className={styles['video-img']} ref={imgInstance} src="https://xx_00.jpg" />
</div>
);
};
export default BannerVideo;