目录
介绍
准备
目标
效果
规定
思路
解答参考
扩展功能
介绍
弹幕指直接显现在视频上的评论,可以以滚动、停留甚至更多动作特效方式出现在视频上,是观看视频的人发送的简短评论。通过发送弹幕可以给观众一种“实时互动”的错觉,弹幕的出现让观看过程充满乐趣。本题需要在已提供的基础项目中,完成视频弹幕的功能。
准备
开始答题前,需要先打开本题的项目代码文件夹,目录结构如下:
├── 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
是存放视频的文件夹。
注意:打开环境后发现缺少项目代码,请手动输入下述命令进行下载:
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
元素节点内。 -
生成的
span
元素节点相对于#video
元素绝对定位 ,初始位的left
是#video
元素的宽,top
是#video
元素的高内的随机数。注意:需求中所需样式可直接通过已提供的getEleStyle
方法获取。 -
弹幕每隔
bulletConfig.time
(弹幕配置对象) 时间,向左移动距离为bulletConfig.speed
(弹幕配置对象)。 - 当弹幕最右端完全移出
#video
元素时,移除span
元素。
2.补全 #sendBulletBtn
元素的绑定事件,点击发送按钮,输入框中的文字出现在弹幕中,样式不同于普通弹幕(样式红色字体红色框已设置,类名为 create-bullet
)。通过调用 renderBullet
方法和正确的传参实现功能。
效果
最终效果可参考文件夹下面的 gif 图,图片名称为 effect.gif(提示:可以通过 VS Code 或者浏览器预览 gif 图片)。
规定
- 请勿修改
js/index.js
文件外的任何内容。 -
请严格按照考试步骤操作,切勿修改考试默认提供项目中的文件名称、文件夹路径、class 名、id 名、图片名等,以免造成无法判题通过。
-
先自己动手做一下吧!传送门
思路
如果想要解决这道题目首先你需要对JS的原生操作能够比较熟悉,这道题目涉及到了子节点的插入以及移除的设置,元素位置的设置。同时需要会利用题目中已经给出的方法,对其进行调用获取我们需要的东西。除此之外还需要你会使用定时器。
解答参考
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
videoEle.appendChild(spanEle);//将弹幕插入到视频元素中
const videoStyle=getEleStyle(videoEle);//计算视频的宽高
spanEle.style.left=videoStyle.width+'px';//设置弹幕的位置left
spanEle.style.top=getRandomNum(videoStyle.height)+'px';//设置弹幕的位置top
spanEle.style.color=`rgb(${getRandomNum(255)},${getRandomNum(255)},${getRandomNum(255)})`;//设置弹幕的颜色
spanEle.innerHTML=bulletConfig.value;//设置弹幕的文字
//设置定时器,每个bulletConfig.time就移动bulletConfig.speed距离
let time=setInterval(()=>{
spanEle.style.left=parseInt(spanEle.style.left)-bulletConfig.speed+'px';
//弹幕离开视线时的条件
if(parseInt(spanEle.style.left)<=-getEleStyle(spanEle).width){
videoEle.removeChild(spanEle);//移除弹幕
clearInterval(time);//清楚定时器
}
},bulletConfig.time)
}
首先将题目给已经给我们编写好的span标签插入到视频元素中,这里使用到了appendChild()方法,用于给父元素插入子节点。插入之后,我们设置span的left以及top对应的值,在设置之前我们先使用getEleStyle方法来获取视频的宽高等信息。然后设置span的left为视频的宽度,它的top为视频高度的随机值,那此时就需要再调用题目已经写好了的getRandomNum()方法来获取到随机值。然后记得后面需要加上px。同时设置span的颜色以及文本。
截止我们需要给它增加一个定时器用于弹幕每隔 bulletConfig.time
(弹幕配置对象) 时间,向左移动距离为 bulletConfig.speed
(弹幕配置对象)。这里用到了setInterval。然后span的left值不断地减少,使用parseInt方法是将后面的px去掉,才能够进行运算。计算之后记得加上px。重要的是需要判断弹幕离开视线的时刻。当弹幕距离左边的距离是它宽度的负数值时刚好弹幕从视频完全移出。此时使用removeChild方法来对其进行移除。同时记得清除定时器。
document.querySelector("#sendBulletBtn").addEventListener('click', () => {
// TODO:点击发送按钮,输入框中的文字出现在弹幕中
//获取输入框中为文字
bulletConfig.value=document.querySelector("#bulletContent").value;
//当有输入文字时在进行调用renderBullet方法
if( bulletConfig.value){
renderBullet(bulletConfig,videoEle,true);
}
})
同时,当在输入框输入文字后发送弹幕时,点击发送按钮,调用renderBullet方法。在调用之前,我们先获取输入框中输入的文本,当判断有输入时才进行调用对应的方法,同时第三个参数出传入true,表示新增弹幕。
完整文件代码:(有什么更好地解决方式欢迎评论区交流学习!)
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
videoEle.appendChild(spanEle);//将弹幕插入到视频元素中
const videoStyle=getEleStyle(videoEle);//计算视频的宽高
spanEle.style.left=videoStyle.width+'px';//设置弹幕的位置left
spanEle.style.top=getRandomNum(videoStyle.height)+'px';//设置弹幕的位置top
spanEle.style.color=`rgb(${getRandomNum(255)},${getRandomNum(255)},${getRandomNum(255)})`;//设置弹幕的颜色
spanEle.innerHTML=bulletConfig.value;//设置弹幕的文字
//设置定时器,每个bulletConfig.time就移动bulletConfig.speed距离
let time=setInterval(()=>{
spanEle.style.left=parseInt(spanEle.style.left)-bulletConfig.speed+'px';
//弹幕离开视线时的条件
if(parseInt(spanEle.style.left)<=-getEleStyle(spanEle).width){
videoEle.removeChild(spanEle);//移除弹幕
clearInterval(time);//清楚定时器
}
},bulletConfig.time)
}
document.querySelector("#sendBulletBtn").addEventListener('click', () => {
// TODO:点击发送按钮,输入框中的文字出现在弹幕中
//获取输入框中为文字
bulletConfig.value=document.querySelector("#bulletContent").value;
//当有输入文字时在进行调用renderBullet方法
if( bulletConfig.value){
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;
}
})
扩展功能
虽然已经实现了题目要求的功能,且提交已经成功通过了。但是我还想要在这个基础上,实现当点击视频播放时是弹幕可以移动,当点击暂停时,弹幕停止移动,继续播放时弹幕能够继续移动。并且当点击切换按钮时,可以控制视频中弹幕的显示以及隐藏。(这部分若感兴趣可以看看)
思路是先声明一个用于接受视频中出现的弹幕的数组。然后再声明两个函数,一个stop,用于当监听到视频的播放为暂停时清除弹幕的定时器,一个为recover,用于当监听当视频点击继续播放时,让弹幕中的定时器都恢复。同时点击切换按钮,为每一个弹幕设置visibility属性来实现实现或者隐藏。修改后的完整代码如下:
const bullets = [
"前方高能",
"原来如此",
"这么简单",
"学到了",
"学费了",
"666666",
"111111",
"workerman",
"学习了",
"别走,奋斗到天明"];
let bulletsArray = []; // 存储所有已生成的弹幕
/**
* @description 根据 bulletConfig 配置在 videoEle 元素最右边生成弹幕,并移动到最左边,弹幕最后消失
* @param {Object} bulletConfig 弹幕配置
* @param {Element} videoEle 视频元素
* @param {boolean} isCreate 是否为新增发送的弹幕,为 true 表示为新增的弹幕
*
*/
// 暂停弹幕移动的函数
function stop() {
for (let bullet of bulletsArray) {
clearInterval(bullet.timer);
}
}
// 恢复弹幕移动的函数
function recover() {
for (let bullet of bulletsArray) {
bullet.timer = setInterval(() => {
bullet.spanEle.style.left = parseInt(bullet.spanEle.style.left) - bullet.bulletConfig.speed + 'px';
if (parseInt(bullet.spanEle.style.left) <= -getEleStyle(bullet.spanEle).width) {
videoEle.removeChild(bullet.spanEle);
clearInterval(bullet.timer);
}
}, bullet.bulletConfig.time);
}
}
function renderBullet(bulletConfig, videoEle, isCreate = false) {
const spanEle = document.createElement("SPAN");
spanEle.classList.add(`bullet${index}`);
if (isCreate) {
spanEle.classList.add("create-bullet")
}
videoEle.appendChild(spanEle);
const videoStyle = getEleStyle(videoEle);
spanEle.style.left = videoStyle.width + 'px';
spanEle.style.top = getRandomNum(videoStyle.height) + 'px';
spanEle.style.color = `rgb(${getRandomNum(255)},${getRandomNum(255)},${getRandomNum(255)})`;
spanEle.innerHTML = bulletConfig.value;
const bullet = {
spanEle: spanEle,
bulletConfig: bulletConfig,
timer: null
};
bulletsArray.push(bullet);
bullet.timer = setInterval(() => {
if (!bulletConfig.isHide) {
spanEle.style.left = parseInt(spanEle.style.left) - bulletConfig.speed + 'px';
if (parseInt(spanEle.style.left) <= -getEleStyle(spanEle).width) {
videoEle.removeChild(spanEle);
clearInterval(bullet.timer);
// 移除弹幕后,从数组中删除对应的弹幕对象
const indexToRemove = bulletsArray.indexOf(bullet);
if (indexToRemove !== -1) {
bulletsArray.splice(indexToRemove, 1);
}
}
}
}, bulletConfig.time);
}
document.querySelector("#sendBulletBtn").addEventListener('click', () => {
// TODO:点击发送按钮,输入框中的文字出现在弹幕中
//获取输入框中为文字
bulletConfig.value=document.querySelector("#bulletContent").value;
//当有输入文字时在进行调用renderBullet方法
if( bulletConfig.value){
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("pause", () => {
isPlay = false;
stop(); // 停止弹幕移动
});
document.querySelector("#vd").addEventListener('play', () => {
console.log('播放')
if (!bulletConfig.isHide) {
recover();
}
// 监听视频播放事件,当视频播放时,每隔 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;
for (let bullet of bulletsArray) {
bullet.spanEle.style.visibility = 'visible';
}
} else {
bulletConfig.isHide = true;
for (let bullet of bulletsArray) {
bullet.spanEle.style.visibility = 'hidden';
}
}
})
好啦,本文就先到这里了。我去吃饭了,拜拜~~~