需求:
- 根据关键字在视频中的出现时间,在模拟的视频时间轴中显示对应的位置
- 用户点击,选择不同的关键字,显示对应关键词时间
- 点击进度条中的关键词时间点,视频对应跳转到该时间播放
实现效果:
完整源代码可移步wantingtr’s github (ps:这里只是针对这个需求的代码,后续会将其他功能一起合并)
模拟后台数据:
针对这个需求模拟后台发来的数据,每个关键词分别有value内容以及time数组,包括该关键词在视频中出现的所有时间点,在页面中展示的也应为对应每个关键词的时间点在时间轴上的可视化。
videoAnalysis: {
//关键词
key: [
{
value: "学习",
time: [
'00:00:01',
'00:05:52',
'00:01:12',
'00:02:32'
]
},
{
value: "2018",
time: [
'00:02:02',
'00:03:52',
'00:07:12',
'00:04:52',
'00:04:12',
'00:06:32',
'00:01:32'
]
},
//...
],
实现方法
先上代码:
数据结构
//数据结构
data: {
keyStatus: [],
keyX:[],//关键词时间坐标
videoTotalTime: '00:10:00', //视频总时长
}
-
keyStatus是一个一维对象数组,储存每个关键词的显隐布尔值
{status: false}
。若用户点击,则对应index值为true,其他为false -
keyX是一个二维数组,储存每个关键词对应的所有时间点的坐标,利用
:style
来动态控制每个时间点在时间轴上的横坐标
html
<!--关键词显示-->
<div id="keyContent"
class="keyContent1"
v-for="(item,index) of videoAnalysis.key"
:key="index"
@click= "changeKey(index)">
</div>
<!--时间点可视化-->
<div class="progressbar">
<div v-for="(item,_index) of videoAnalysis.key" :key="_index">
<div v-show="keyStatus[_index].status"
class="keyItemBox"
v-for="(item, __index) of videoAnalysis.key[_index].time"
:key="__index"
:style="getStyle(_index, __index)"
@click="changeVideoTime(item)"
>
</div>
</div>
</div>
这里的重点有三个:
v-for
与v-show
的嵌套循环- 外层的
v-for
循环所有关键词 ->v-for="(item,_index) of videoAnalysis.key"
- 内层的
v-for
循环对应关键词的每个时间点 ->v-for="(item, __index) of videoAnalysis.key[_index].time"
- 外层的
-
用
v-show
来控制每个关键点时间的显示 ->v-show="keyStatus[_index].status"
- 为什么不用
v-if
?v-show
和v-if
在功能上都能控制元素的显隐,但实现原理不一样。- v-if是动态的向DOM树内添加或者删除DOM元素;
- 而v-show是听过设置DOM元素的display属性来控制元素的显示
因此
v-if
切换时有一个个局部编译/卸载的过程,而v-show
只是简单的基于css的切换,所以针对这种需要频繁切换的需求,若v-if
会有更高的切换消耗 keyStatus[_index].status
代表着什么?keyStatus[_index].status
精确到每个关键词,对应的status为用户当前点击的关键词,即为页面上应该显示的关键词时间轴。
- 为什么不用
- 使用
:style="getStyle(_index, __index)"
来对时间轴上的每个时间点进行定位 因为针对每个关键词出现的所有时间点都要在时间轴上进行定位,所以需要针对不同的时间点来动态设定其相对时间轴的X坐标
JS功能实现
初始化
beforeMount() {
//计算视频总时长
var videoTotalTime = this.videoTotalTime.split(":");
var videototaltime = 3600 * parseInt(videoTotalTime[0]) + 60 * parseInt(videoTotalTime[1]) + parseInt(videoTotalTime[2]);
var key = this.videoAnalysis.key
//var obj = { status: false }
for (var i = 0; i < key.length; i++) {
this.keyStatus.push({ status: false}) //给每一个关键词初始化为false
//console.log(this.keyStatus)
var width = 500 //视频进度条像素宽度
var time = key[i].time
var array = []
for (var j = 0; j < time.length; j++) {
var keyTime = time[j].split(":");
var keytime = 3600 * parseInt(keyTime[0]) + 60 * parseInt(keyTime[1]) + parseInt(keyTime[2]);
var bi = keytime / videototaltime //时间占总时间的比例
var x = bi * width //位置
array.push(x.toFixed(2)) //保留两位小数
this.keyX[i] = array //二维数组
}
}
this.keyStatus[0].status = true //默认显示第一个关键词的时间轴
}
具体实现方法都在注释里了,这里有一个问题,即为被注释掉的//var obj = { status: false }
,为何不能在这里通过定义obj再将其push进keyStatus呢。
这是因为obj是在循坏外的,若在循环内将其push进数组,每次push进的为同一个obj,即指针指向的是同一个对象,会使后期代码出现问题。
点击切换关键词
changeKey (index) {
for(var i = 0; i < this.keyStatus.length; i++) {
this.keyStatus[i].status = false
}
this.keyStatus[index].status = true
//console.log(this.keyStatus)
},
切换关键词是应该先将所有值赋为false,再将用户点击的当前关键词赋为true来实现关键词的更新
实现时间点可视化
getStyle(_index, __index) {
var value = this.keyX[_index][__index]
//console.log(value)
return 'transform: translateX(' + value + 'px)';
//利用translateX实现X坐标的移动
}
//改变视频播放
changeVideoTime(time) {
var timeSecond = time.split(":");
var totalTime = 3600 * parseInt(timeSecond[0]) + 60 * parseInt(timeSecond[1]) + parseInt(timeSecond[2]);
var thisVideo = document.getElementById("video");
thisVideo.currentTime = totalTime;
},
遇到的问题
v-show 用布尔类型的数组绑定无法监听
刚开始,因为keyStatus
这个数组只需储存关键词的布尔值,所以将其设为布尔类型的数组,但是发现无法成功渲染。
应该使用对象{satatus:falase}
,通过键值对来进行监听
生命周期造成的对象undefined问题
刚开始将初始化写在函数mounted
里,发现浏览器报错
查询了一下资料,发现这是因为页面渲染刚的时候还没有拿到这个值,所以会报错。那如果写在beforeMount
里呢?试验了一下,发现报错消失
总结
之前没有写过类似的需求,在网上找了一下也没有找到类似的库与方法,所以自己用原生JS和CSS写了一个,性能肯定是不够好的,后续会继续优化,看到这篇博客的前端小伙伴可以多多交流,提意见就更好啦