[ 蓝桥杯Web真题 ]-视频弹幕

目录

介绍

准备

目标

效果

规定

思路

解答参考

扩展功能


介绍

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

准备

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

├── 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的原生操作能够比较熟悉,这道题目涉及到了子节点的插入以及移除的设置,元素位置的设置。同时需要会利用题目中已经给出的方法,对其进行调用获取我们需要的东西。除此之外还需要你会使用定时器。

解答参考

javascript">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方法来对其进行移除。同时记得清除定时器。

javascript">document.querySelector("#sendBulletBtn").addEventListener('click', () => {
    // TODO:点击发送按钮,输入框中的文字出现在弹幕中
    //获取输入框中为文字
    bulletConfig.value=document.querySelector("#bulletContent").value;
    //当有输入文字时在进行调用renderBullet方法
    if( bulletConfig.value){
        renderBullet(bulletConfig,videoEle,true);
    }
})

同时,当在输入框输入文字后发送弹幕时,点击发送按钮,调用renderBullet方法。在调用之前,我们先获取输入框中输入的文本,当判断有输入时才进行调用对应的方法,同时第三个参数出传入true,表示新增弹幕。

完整文件代码:(有什么更好地解决方式欢迎评论区交流学习!)

javascript">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属性来实现实现或者隐藏。修改后的完整代码如下:

javascript">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';
        }
    }
})

好啦,本文就先到这里了。我去吃饭了,拜拜~~~


http://www.niftyadmin.cn/n/5237566.html

相关文章

基于STM32的智慧农业项目(物联网专业毕设)附送源码和文档材料+学习路线

文章目录 概要整体架构流程硬件选型软件总体框架技术细节实现效果小结 概要 传统农业存在着产量受到环境因素影响较大的问题&#xff0c;现有的农业监测系统数据太过简单、太过理想化。而随着现代科学的持续发展,一个精准化、自动化的现代智能农产品管理系统将在农业生产中起着…

Flask 自定义template和静态文件路径

这边使用flask的时候希望规范下路径&#xff0c;就需要重新移动默认的template的路径&#xff0c;所以就需要自定义路径了&#xff0c;查了下。记录 就直接在app.py里面进行修改&#xff0c;加两个属性&#xff0c;第一个就是放html的第二个就是放css/js那些静态文件的 app F…

vscode配置使用 cpplint

标题安装clang-format和cpplint sudo apt-get install clang-format sudo pip3 install cpplint标题以下settings.json文件放置xxx/Code/User目录 settings.json {"sync.forceDownload": false,"workbench.sideBar.location": "right","…

笔记66:自注意力和位置编码

本地笔记地址&#xff1a;D:\work_file\&#xff08;4&#xff09;DeepLearning_Learning\03_个人笔记\3.循环神经网络\第10章&#xff1a;动手学深度学习~注意力机制 a a a a a a a a a a a a a a a a a a a

原生微信小程序的原理

前言 微信小程序是一种在微信内部运行的轻量级应用&#xff0c;它可以让用户在无需下载安装的情况下&#xff0c;快速地获取和使用各种服务。微信小程序的开发和普通的网页开发有很多相似之处&#xff0c;但也有一些独特的特点和原理。 一. 小程序的技术架构 微信小程序的技术架…

[383] 赎金信 js

题目描述&#xff1a; 给你两个字符串&#xff1a;ransomNote 和 magazine &#xff0c;判断 ransomNote 能不能由 magazine 里面的字符构成。 * * 如果可以&#xff0c;返回 true &#xff1b;否则返回 false 。 * * magazine 中的每个字符只能在 ransomNote 中使用一次。 解题…

3D云参观红色革命纪念馆允许更多人在线交流、体验

生活在和平年代的新一代青少年&#xff0c;可能对革命先烈英勇事迹难以有很深的体会&#xff0c;无法切实感受到中国共产党无畏牺牲、誓死保家卫国的红色精神&#xff0c;因此借助VR虚拟现实制作技术&#xff0c;让参观者们走近革命先烈中&#xff0c;感受老一辈无产阶级革命家…

开发者必备,热门api接口大全

天气预警&#xff1a;支持输入经纬度或者区域编码&#xff0c;获取指定城市当前生效中的各类天气预警&#xff0c;如寒潮蓝色预警信号&#xff0c;或一次性拉取全国所有生效中的天气预警。天气预报查询&#xff1a;支持全国以及全球多个城市的天气查询&#xff0c;包含国内3400…