视频弹幕

介绍

弹幕指直接显现在视频上的评论,可以以滚动、停留甚至更多动作特效方式出现在视频上,是观看视频的人发送的简短评论。通过发送弹幕可以给观众一种“实时互动”的错觉,弹幕的出现让观看过程充满乐趣。本题需要在已提供的基础项目中,完成视频弹幕的功能。

准备

开始答题前,需要先打开本题的项目代码文件夹,目录结构如下:

1
2
3
4
5
6
7
8
├── effect.gif 
├── css
│ └── index.css
├── video
│ └── video1.webm
├── index.html
└── js
└── index.js

其中:

  • index.html 是主页面。
  • js/index.js 是需要补充代码的 js 文件。
  • css/index.css 是样式文件。
  • effect.gif 是完成的效果图。
  • video 是存放视频的文件夹。

注意:打开环境后发现缺少项目代码,请手动输入下述命令进行下载:

1
2
3
cd /home/project
wget -q https://labfile.oss.aliyuncs.com/courses/18213/test6.zip
unzip test6.zip && rm test6.zip

在浏览器中预览 index.html 页面,显示如下所示:

初始效果

目标

请在 js/index.js 文件中补全代码。具体需求如下:

  1. 补全 renderBullet 函数中的代码,控制弹幕的显示颜色和移动。功能说明如下:
  • 每个弹幕内容包裹在 span 标签中,作为子节点插入到 #video 元素节点内。

  • 生成的

1
span

元素节点相对于

1
#video

元素绝对定位 ,初始位置的

1
left

1
#video

元素的宽,

1
top

1
#video

元素的高内的随机数。

注意:需求中所需样式可直接通过已提供的 getEleStyle 方法获取。

  • 弹幕每隔 bulletConfig.time(弹幕配置对象) 时间,向左移动距离为 bulletConfig.speed(弹幕配置对象)。

  • 当弹幕最右端完全移出 #video 元素时,移除 span 元素。

  1. 补全 #sendBulletBtn 元素的绑定事件,点击发送按钮,输入框中的文字出现在弹幕中,样式不同于普通弹幕(样式红色字体红色框已设置,类名为 create-bullet )。通过调用 renderBullet 方法和正确的传参实现功能。

最终效果可参考文件夹下面的 gif 图,图片名称为 effect.gif(提示:可以通过 VS Code 或者浏览器预览 gif 图片)。

最终效果

规定

  • 请勿修改 js/index.js 文件外的任何内容。
  • 请严格按照考试步骤操作,切勿修改考试默认提供项目中的文件名称、文件夹路径、class 名、id 名、图片名等,以免造成无法判题通过。

判分标准

  • 本题完全实现题目目标得满分,否则得 0 分。

总通过次数: 663 | 总提交次数: 729 | 通过率: 90.9%

难度: 中等 标签: 蓝桥杯, 2023, 省赛, Web 前端, JavaScript, HTML5

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
const bullets = [
"前方高能",
"原来如此",
"这么简单",
"学到了",
"学费了",
"666666",
"111111",
"workerman",
"学习了",
"别走,奋斗到天明"];


/**
* @description 根据 bulletConfig 配置在 videoEle 元素最右边生成弹幕,并移动到最左边,弹幕最后消失
* @param {Object} bulletConfig 弹幕配置
* @param {Element} videoEle 视频元素
* @param {boolean} isCreate 是否为新增发送的弹幕,为 true 表示为新增的弹幕
*
*/
function renderBullet(bulletConfig, videoEle, isCreate = false) {
const spanEle = document.createElement("SPAN");
spanEle.classList.add(`bullet${index}`);
if (isCreate) {
spanEle.classList.add("create-bullet")
}
// TODO:控制弹幕的显示颜色和移动,每隔 bulletConfig.time 时间,弹幕移动的距离 bulletConfig.speed
// initialize
spanEle.innerText = bulletConfig.value
let {width: videoEleWidth, height: videoEleHeight} = getEleStyle(videoEle)
let timer = null

spanEle.style.left = `${videoEleWidth}px`
spanEle.style.top = `${getRandomNum(videoEleHeight)}px`
// append
videoEle.appendChild(spanEle)
let {width: spanEleWidth, left: spanEleLeft} = getEleStyle(spanEle)
console.log(spanEleWidth);
// move
timer = setInterval(() => {
// move
videoEleWidth -= bulletConfig.speed
spanEle.style.left = `${videoEleWidth}px`
// checkout
if (videoEleWidth + spanEleWidth <= 0) {
console.log(spanEleLeft, videoEleWidth);
videoEle.removeChild(spanEle)
clearInterval(timer)
}
}, bulletConfig.time)
// color
spanEle.style.color = `RGB(${getRandomNum(255)}, ${getRandomNum(255)}, ${getRandomNum(255)})`
}

document.querySelector("#sendBulletBtn").addEventListener('click', () => {
// TODO:点击发送按钮,输入框中的文字出现在弹幕中
const sendInput = document.getElementById('bulletContent')
const bulletConfig = {
isHide: false, // 是否隐藏
speed: 5, // 弹幕的移动距离
time: 50, // 弹幕每隔多少ms移动一次
value: sendInput.value // 弹幕的内容
}
const videoEle = document.querySelector("#video");
renderBullet(bulletConfig, videoEle, true)
})

function getEleStyle(ele) {
// 获得元素的width,height,left,right,top,bottom
return ele.getBoundingClientRect();
}

function getRandomNum(end, start = 0) {
// 获得随机数,范围是 从start到 end
return Math.floor(start + Math.random() * (end - start + 1));
}

// 设置 index 是为了弹幕数组循环滚动
let index = 0;
const videoEle = document.querySelector("#video");
// 弹幕配置
const bulletConfig = {
isHide: false, // 是否隐藏
speed: 5, // 弹幕的移动距离
time: 50, // 弹幕每隔多少ms移动一次
value:"" // 弹幕的内容
}
let isPlay = false;
let timer; // 保存定时器
document.querySelector("#vd").addEventListener('play', () => {
// 监听视频播放事件,当视频播放时,每隔 1000s 加载一条弹幕
isPlay = true;
bulletConfig.value = bullets[index++];
renderBullet(bulletConfig, videoEle);
timer = setInterval(() => {
bulletConfig.value = bullets[index++];
renderBullet(bulletConfig, videoEle);
if (index >= bullets.length) {
index = 0;
}
}, 1000);
})

document.querySelector("#vd").addEventListener("pause", () => {
isPlay = false;
clearInterval(timer);
})

document.querySelector("#switchButton").addEventListener("change", (e) => {
if (e.target.checked) {
bulletConfig.isHide = false;
} else {
bulletConfig.isHide = true;
}
})