前端视频预览功能的实现

CSDN话题挑战赛第2期
参赛话题:前端技术分享


✨✨✨前言

这是我模仿B站所写项目遇到的第二个很有挑战性的部分,网上的资料比较缺乏,还是大佬室友换了个关键词才搜到比较有用的资料hh。

之前没有写过视频相关的项目,所以视频相关的API都十分陌生。从最开始的一无所知,一步步查资料,理解代码,进行修改,解决代码,最后实现的时候感觉非常有成就感。

网上能找到的相关插件是用angular.js实现(现在基本被vue,react替代),跟我使用技术栈冲突,无法直接引用插件,而且很多技术细节没有文字解释,阅读比较困难。所以写下这篇博客,方便自己重看代码,同时也希望能帮到在实现这一功能受阻的程序猿们o( ̄▽ ̄)ブ



✨✨✨前置准备

  • 对VUE,vite框架,hls.js插件有基本的掌握
  • 对同步异步,promise有基本的了解
  • 熟悉vue3-video-play源代码 以及video相关API
  • 了解canvas画布的使用


✨✨✨概述


整个过程大致可分成5步:


1.预处理,创建一个video标签,视频源和当前视频源一致,并监听canplay事件

2.将整个视频划成若干段,对于每一段,canplay事件触发时,创建一个canvas画布,并修改video的currentTime

3.利用监听函数和promise函数,保证视频截图成功绘制到canvas画布上

4.将canvas转为blob,并用一个数组将其存下来,预处理完成

5.当鼠标悬浮在进度条上时,根据其位置展示对应缩略图


✨✨✨实现过程及代码

一、

本来是想在函数里创造video标签的,但我将hls.js插件初始化设计在Mounted时期,所以就直接在HTML中添加了。

<video id="Vvideo" ref="Vvideo" src="https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8" controls="true"
      preload="auto" crossOrigin="anonymous" width="400" height="200" v-show="false">
      您的浏览器不支持 video 标签。
</video>

ngx-thumbnail-video插件监听的是loadeddata,但我尝试的时候发现这样canvas只绘制出黑屏,所以我监听canplay事件,但canplay事件在视频准备好后会一直触发,这好解决,只需要在触发后立刻移除监听函数就可。

const preloadVideoThumbs = () => {
  state.Vvideo.addEventListener('canplay', setFrontEndPreload);
}
const setFrontEndPreload = async () => {
  state.Vvideo.removeEventListener('canplay', setFrontEndPreload);
  // 接下来代码见后文二~四
}

二、

根据自己的需求设置间隔,不过太长太短都不好,太长会导致用户点击后与预览图很可能不一致,太短会导致图片过多从而占用内存较大,我这里设置的3

for (let i = 0; i <= state.Vvideo.duration; i += 3) {
  let canvas = document.createElement("canvas");
  const context = canvas.getContext('2d');
  state.Vvideo.currentTime = i;
  //接下来代码见后文三
}


三、

这里需要等到 currentTime 对应帧 canplay 后再进行绘制,否则会导致黑屏。但addEventListener是异步操作,等到事件触发时currentTime 就变为最后一个循环的 currentTime ,这显然不是我们想要的。
namo,闭包能解决问题吗?答案是不能,闭包就无法避免currentTime 设置后立刻绘制。

namo 我们能否强制让 addEventListener变成同步呢,promise能解决这个问题.
new promise本身是同步的,只有当遇到resolve / reject 时才会结束,否则将一直阻塞,我们可以将resolve 函数设置在被监听事件的末尾,这样就能达到目的了


await new Promise(function (rsv) {
  const event = function () {
    context?.drawImage(state.Vvideo, 0, 0, 300, 150);
    //代码见后文四
    state.Vvideo.removeEventListener('canplay', event);
    rsv(null);
  };
  state.Vvideo.addEventListener('canplay', event);

});

四、

将绘制出来的canvas转为img图片有两种,toDataURL和toBlob两种,网上实现视频预览这一部分大多都是前者,但我查到的资料了解到toBlob更优,详见博客链接:
https://qa.1r1g.com/sf/ask/3855198931/


canvas.toBlob(function (blob) {
  state.Thumbnails.push(URL.createObjectURL(blob as Blob));
}, "image/jpeg");

五、

若当前帧已经加载出来,则显示当前帧,否则显示loading。namo如何判断当前帧是否加载完成呢?
我们可以将当前帧应该在数组中位置与数组大小进行比较,若小于,则加载完成。


<img class="d-slider__img"  ref="refImg"   :style="{ left: state.hoverImgLeft }"
:src="state.imgIndex<props.Thumbnails.length? props.Thumbnails[state.imgIndex]:state.loadingImg" 
alt="图片加载失败">

细节处理,防止缩略图显示在边框之外

const mousemoveHandle = (ev: MouseEvent) => {
  if (!props.hover) return;
  let val = getPosition(ev);
  emits('onMousemove', ev, val);
  state.hoverPosition = val;
  if (props.vertical) return;
  state.imgIndex=(val*props.totalTimeNumber);
  state.imgIndex/=3;
  state.imgIndex=~~state.imgIndex;
  //获取dom
  let refSliderEl = (refSlider.value as HTMLButtonElement);
  // 提示宽的一半宽度
  let refImgWidth = (refImg.value as HTMLButtonElement).clientWidth / 2;
  let movePositon = ev.clientX - refSliderEl.getBoundingClientRect().left;
  // 如果当前往左的偏移量大于提示框宽度
  if (movePositon < refImgWidth) {
    state.hoverImgLeft = (refImgWidth - movePositon) + 'px'
  } else if ((refSliderEl.clientWidth - movePositon) < refImgWidth) {
    // 如果当前往右的偏移量大于提示框宽度  (总宽度-当前移动位置)< Img一半的宽度
    state.hoverImgLeft = (refSliderEl.clientWidth - movePositon) - refImgWidth + 'px'
  } else {
    state.hoverImgLeft = '50%'
  }

}



✨✨✨效果展示

截图

在这里插入图片描述


在这里插入图片描述


视频(加载有些小满,请稍等一下~)




✨✨✨资料源

vue3-video-play:https://blog.csdn.net/xdlumia/article/details/11986500


ngx-thumbnail-videohttps://levelup.gitconnected.com/build-youtube-like-stylish-video-player-with-thumbnail-preview-on-progress-bar-hovered-53b9074acd75



✨✨✨github链接

想要看完整版代码以及页面效果,请移步github哦
https://github.com/Ki-Wi-Berry/bilibili-videos


⛄码字不易,如果觉得对您有帮助的话,麻烦点个免费的赞~⛄

  • 5
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: HTML5提供了多种实现预览图片和视频的方式。 预览图片可以使用HTML5的`<img>`标签,通过设置`src`属性指定图片的路径来展示图片。可以通过添加`width`和`height`属性来控制图片的显示大小。另外,可以使用CSS属性来设置图片的样式,比如调整边框、圆角等。如果需要预览多张图片,可以使用JavaScript来动态地修改`src`属性。 预览视频可以使用HTML5的`<video>`标签,通过设置`src`属性指定视频文件的路径来播放视频。可以使用`controls`属性来显示视频的播放控制栏,包括播放/暂停、音量和进度条等。同时,可以设置`width`和`height`属性来控制视频的显示大小。另外,还可以使用JavaScript来控制视频的播放、暂停、音量、全屏等功能,通过对`<video>`标签的属性和方法进行操作。 为了兼容不同浏览器和不同文件格式,可以在`<video>`标签内添加多个`<source>`标签,每个`<source>`标签指定一个不同格式的视频文件。浏览器会选择支持的第一个视频格式进行播放。此外,可以使用`poster`属性设置视频封面,在视频加载前显示一张图片。 总结起来,HTML5可以通过`<img>`标签和`<video>`标签来实现图片和视频预览功能,通过设置相关属性和样式,以及使用JavaScript来控制播放和操作。这些功能实现可以根据需求来灵活选择,并且需要考虑浏览器的兼容性和文件格式的支持。 ### 回答2: 在HTML5中,我们可以使用HTML标签和JavaScript来实现预览图片和视频功能。 对于图片预览,可以使用HTML的<input type="file">标签来创建一个文件上传表单,用户可以选择本地的图片文件进行上传。然后,借助JavaScript的FileReader对象,可以读取这个文件,并以DataURL的形式将其显示在页面中。通过设置<img>标签的src属性为DataURL,可实现预览图片的效果。 对于视频预览,HTML5提供了<video>标签。通过设置<video>标签的src属性为视频文件的URL,即可将视频加载到页面中进行预览。此外,<video>标签还提供了一些属性和方法用于控制视频的播放、暂停和调整播放进度等功能,可以根据需求进行配置。 在实际使用中,可以使用JavaScript添加事件监听器,以便根据用户的操作来控制图片和视频预览效果,比如播放、暂停、拖动进度条等。另外,可以根据需要设置一些样式或使用其他的第三方库来美化预览效果,增强用户体验。 总而言之,通过使用HTML5中提供的相关标签和JavaScript的操作,我们可以方便地实现图片和视频预览功能,为用户提供更好的浏览体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值