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

解答参考

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/216952.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

206 反转链表

解题思路可以有两种方法&#xff1a;递归 or 迭代。 \qquad 迭代&#xff1a;通过使用for循环遍历&#xff0c;完成目标。方法直观&#xff0c;容易理解。 \qquad 递归&#xff1a;通过函数调用其自身&#xff0c;完成目标。递归最复杂、最重要的部分就是递归函数的构建&#…

tex中的边框

文章目录 利用tcolorbox宏包给公式加框 利用tcolorbox宏包 tcolorbox可以创建一个盒子的环境&#xff0c;例如&#xff1a; \documentclass{article} \usepackage{tcolorbox} \begin{document}\begin{tcolorbox}[left1cm, right1cm, top0.5cm, bottom0.5cm,colbackblue!10!wh…

Win Server 2019远程桌面服务部署

一、添加远程桌面授权服务 服务器管理 - 添加角色和功能打开“添加角色和功能向导”窗口&#xff0c;选择基于角色或给予功能安装&#xff1a; 打开服务器管理&#xff0c;打开角色和功能&#xff0c;添加远程回话主机和远程桌面授权 image.png 以上配置完成后使用期限为120…

JVM之垃圾回收与算法(四)

垃圾回收与算法 1.如何确定垃圾 1.1. 引用计数法 在 Java 中&#xff0c;引用和对象是有关联的。如果要操作对象则必须用引用进行。因此&#xff0c;很显然一个简单的办法是通过引用计数来判断一个对象是否可以回收。简单说&#xff0c;即一个对象如果没有任何与之关联的引用…

希宝猫罐头怎么样?专业人士告诉你质量好又便宜的猫罐头推荐

作为从业6年的宠物护理师来说&#xff0c;只买合适的&#xff0c;贵的不如好的&#xff0c;只要配方不出错营养跟得上&#xff0c;观察自家猫咪体质真的基本不怎么出错。希望大家看完这篇文章&#xff0c;各位铲屎官都能买到满意的猫罐头。那么希宝猫罐头在各方面表现怎么样呢&…

Linux系统下Nginx的安装步骤

目录 Nginx简介Nginx的作用Nginx的安装方法方法一方法二方法三 本文主要介绍在Linux系统下&#xff0c;三种常见Nginx安装方法。 Nginx简介 Nginx是一个高性能的HTTP和反向代理服务器&#xff0c;也可以作为邮件代理服务器和通用的TCP/UDP代理服务器。它最初由Igor Sysoev创建…

值班日历实现不同人显示不同的颜色区别

前端UI用的移动端的vantUI。这里只是我的思路总结&#xff0c;和用什么UI框架关系不大。 先看效果图&#xff1a; <van-calendarref"calendar":poppable"false":show-confirm"false":style"{ height: 580px }":min-date"minD…

01 高等数学.武忠祥.0基础

第一章 函数与极限 01映射与函数 02 函数概念 对应法则 定义域 常见函数 函数的几种特性 周期函数不一定有最小周期。 涉及额外与复习 存在与任意的关系

专业爬虫框架 _scrapy进阶使用详解

⑴ 中间件 中间件基本介绍 在Scrapy中&#xff0c;中间件是一种插件机制 它允许你在发送请求和处理响应的过程中对Scrapy引擎的行为进行干预和定制。 Scrapy中间件的用途&#xff1a; 修改请求、处理响应、处理异常、设置代理、添加自定义的HTTP头部等等。 Scrapy中间件主要分…

P1025 [NOIP2001 提高组] 数的划分

暴搜 剪枝 枚举固定的位置 #include<bits/stdc.h> using namespace std; using ll long long; const int N 1e310; int n,k; int res; void dfs(int last,int sum,int cur){if(curk){if(sumn)res;return;}for(int ilast;isum<n;i)dfs(i,sumi,cur1); } int main() {c…

【C++】树型结构关联式容器:map/multimap/set/multisetの使用指南(27)

前言 大家好吖&#xff0c;欢迎来到 YY 滴C系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; 目录 一.键值对二.关联式容器&#xff06;序列…

Pygame游戏实战六:飞机大战

介绍模块 本游戏使用的是由Pycharm中的pygame模块来实现的&#xff0c;也可以在python中运行。通过Pygame制作一个飞机大战&#xff0c;通过控制自己的飞机来攻击敌机&#xff0c;敌机的速度也忽快忽慢&#xff0c;看看这个是你小时候玩的游戏吗&#xff1f; 最小开发框架 详…

JDBC常见的几种连接池使用(C3P0、Druid、HikariCP 、DBCP)(附上代码详细讲解)

Hi i,m JinXiang ⭐ 前言 ⭐ 本篇文章主要介绍JDBC常见的几种连接池使用&#xff08;C3P0、Druid、HikariCP 、DBCP&#xff09;以及部分理论知识 &#x1f349;欢迎点赞 &#x1f44d; 收藏 ⭐留言评论 &#x1f4dd;私信必回哟&#x1f601; &#x1f349;博主收将持续更新学…

kubectl获取ConfigMap导出YAML时如何忽略某些字段

前言&#xff1a; 当我们在使用Kubernetes时&#xff0c;常常需要通过kubectl命令行工具来管理资源。有时我们也想将某个资源的配置导出为YAML文件&#xff0c;这样做有助于版本控制和资源的迁移。然而&#xff0c;默认情况下&#xff0c;使用kubectl get命令导出资源配置会包…

【c】有序数列插入一个整数

#include<stdio.h> int main() {int n;scanf("%d",&n);int arr[n1];for(int i0;i<n;i){scanf("%d",&arr[i]);}int a;scanf("%d",&a);arr[n]a;for(int j0;j<n;j){if(arr[j]>arr[n])//交换元素位置{int temparr[j];arr…

JFrog----常见的开源协议以及应用注意点

文章目录 1. MIT 许可证2. GPL&#xff08;通用公共许可证&#xff09;3. LGPL&#xff08;较宽松的通用公共许可证&#xff09;4. Apache 许可证 2.05. BSD 许可证开源协议的选择和注意点结论 开源软件近年来在软件开发中变得越来越流行。使用开源软件可以节省时间和资源&…

拼多多电商平台API接口,获取拼多多实时准确数据,获取产品销量、价格,sku图片及sku库存数据演示

拼多多商品详情API接口的作用是让开发者可以获取拼多多平台上特定商品的详细信息&#xff0c;包括商品的标题、价格、图片、规格、参数以及店铺信息等。通过这个接口&#xff0c;开发者可以轻松地获取商品的原始数据&#xff0c;便于进行数据分析、价格比较、爬取等操作。这为电…

算法基础六

搜索插入位置 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 示例 1: 输入: nums [1,3,5,6], target 5 输出: 2 示例 2: 输入: nums [1,3,5,6], target 2 输…

ITSM变更管理,有效降低IT管理风险!

在当今数字化时代&#xff0c;信息技术的快速发展使企业面临着不断变化的需求和挑战。为了适应和应对这些变化&#xff0c;企业需要进行各种IT系统和应用的变更。然而&#xff0c;不加控制的变更可能带来潜在的风险和不良影响。因此&#xff0c;ITSM变更管理成为了一项必不可少…
最新文章