CSDN话题挑战赛第2期
参赛话题:前端技术分享
目录:
这是我模仿B站所写项目遇到的第二个很有挑战性的部分,网上的资料比较缺乏,还是大佬室友换了个关键词才搜到比较有用的资料hh。
之前没有写过视频相关的项目,所以视频相关的API都十分陌生。从最开始的一无所知,一步步查资料,理解代码,进行修改,解决代码,最后实现的时候感觉非常有成就感。
网上能找到的相关插件是用angular.js实现(现在基本被vue,react替代),跟我使用技术栈冲突,无法直接引用插件,而且很多技术细节没有文字解释,阅读比较困难。所以写下这篇博客,方便自己重看代码,同时也希望能帮到在实现这一功能受阻的程序猿们o( ̄▽ ̄)ブ
整个过程大致可分成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-video:https://levelup.gitconnected.com/build-youtube-like-stylish-video-player-with-thumbnail-preview-on-progress-bar-hovered-53b9074acd75
想要看完整版代码以及页面效果,请移步github哦
https://github.com/Ki-Wi-Berry/bilibili-videos
⛄码字不易,如果觉得对您有帮助的话,麻烦点个免费的赞~⛄
我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden
只是想确保我理解了事情。据我目前收集到的信息,Cucumber只是一个“包装器”,或者是一种通过将事物分类为功能和步骤来组织测试的好方法,其中实际的单元测试处于步骤阶段。它允许您根据事物的工作方式组织您的测试。对吗? 最佳答案 有点。它是一种组织测试的方式,但不仅如此。它的行为就像最初的Rails集成测试一样,但更易于使用。这里最大的好处是您的session在整个Scenario中保持透明。关于Cucumber的另一件事是您(应该)从使用您的代码的浏览器或客户端的角度进行测试。如果您愿意,您可以使用步骤来构建对象和设置状态,但通常您
华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o
C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.
MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO
动漫制作技巧是很多新人想了解的问题,今天小编就来解答与大家分享一下动漫制作流程,为了帮助有兴趣的同学理解,大多数人会选择动漫培训机构,那么今天小编就带大家来看看动漫制作要掌握哪些技巧?一、动漫作品首先完成草图设计和原型制作。设计草图要有目的、有对象、有步骤、要形象、要简单、符合实际。设计图要一致性,以保证制作的顺利进行。二、原型制作是根据设计图纸和制作材料,可以是手绘也可以是3d软件创建。在此步骤中,要注意的问题是色彩和平面布局。三、动漫制作制作完成后,加工成型。完成不同的表现形式后,就要对设计稿进行加工处理,使加工的难易度降低,并得到一些基本准确的概念,以便于后续的大样、准确的尺寸制定。四、
2022/8/4更新支持加入水印水印必须包含透明图像,并且水印图像大小要等于原图像的大小pythonconvert_image_to_video.py-f30-mwatermark.pngim_dirout.mkv2022/6/21更新让命令行参数更加易用新的命令行使用方法pythonconvert_image_to_video.py-f30im_dirout.mkvFFMPEG命令行转换一组JPG图像到视频时,是将这组图像视为MJPG流。我需要转换一组PNG图像到视频,FFMPEG就不认了。pyav内置了ffmpeg库,不需要系统带有ffmpeg工具因此我使用ffmpeg的python包装p
Transformers开始在视频识别领域的“猪突猛进”,各种改进和魔改层出不穷。由此作者将开启VideoTransformer系列的讲解,本篇主要介绍了FBAI团队的TimeSformer,这也是第一篇使用纯Transformer结构在视频识别上的文章。如果觉得有用,就请点赞、收藏、关注!paper:https://arxiv.org/abs/2102.05095code(offical):https://github.com/facebookresearch/TimeSformeraccept:ICML2021author:FacebookAI一、前言Transformers(VIT)在图
遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg
通常,数组被实现为内存块,集合被实现为HashMap,有序集合被实现为跳跃列表。在Ruby中也是如此吗?我正在尝试从性能和内存占用方面评估Ruby中不同容器的使用情况 最佳答案 数组是Ruby核心库的一部分。每个Ruby实现都有自己的数组实现。Ruby语言规范只规定了Ruby数组的行为,并没有规定任何特定的实现策略。它甚至没有指定任何会强制或至少建议特定实现策略的性能约束。然而,大多数Rubyist对数组的性能特征有一些期望,这会迫使不符合它们的实现变得默默无闻,因为实际上没有人会使用它:插入、前置或追加以及删除元素的最坏情况步骤复