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

news2024/11/18 10:28:26

目录

介绍

准备

目标

效果

规定

思路

解答参考

扩展功能


介绍

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

准备

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

├── 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.coloradmin.cn/o/1283387.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

大数据 - MapReduce:从原理到实战的全面指南

本文深入探讨了MapReduce的各个方面&#xff0c;从基础概念和工作原理到编程模型和实际应用场景&#xff0c;最后专注于性能优化的最佳实践。 一、引言 1.1 数据的价值与挑战 在信息爆炸的时代&#xff0c;数据被视为新的石油。每天都有数以百万计的数据被生成、存储和处理&…

GUI库pyqt5的中线程通信问题

背景 在使用pyqt5编写GUI程序时&#xff0c;我们可能有时会需要使用多线程来处理问题&#xff0c;这就可能会涉及到线程之间相互通信的问题。比如接下来要解决的进度条问题。 解决思路 我们可以通过pyqt5的信号与槽机制来完成线程之间的通信。 代码编写 # Copyright (C) 2…

如何写好亚马逊listing页面?助你提高转化率!(下)

一、产品长描述 产品长描述位于listing页面的中部&#xff0c;是产品信息的重要组成部分。卖家可以描述产品的特点、功能和使用方法&#xff0c;有助于解决买家可能存在的疑虑&#xff0c;从而增加销售、提高转化率和品牌形象。 在亚马逊上&#xff0c;有两种长描述方式——有…

Canvas鼠标画线

鼠标按下开始画线,鼠标移动根据鼠标的轨迹去画,鼠标抬起停止画线 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0">…

[SHCTF 2023]——week1-week3 Web方向详细Writeup

Week1 babyRCE 源码如下 <?php$rce $_GET[rce]; if (isset($rce)) {if (!preg_match("/cat|more|less|head|tac|tail|nl|od|vi|vim|sort|flag| |\;|[0-9]|\*|\|\%|\>|\<|\|\"/i", $rce)) {system($rce);}else {echo "hhhhhhacker!!!".&…

antv x6填坑指南: 部分节点拖拽和操作撤销重做不生效问题、使用Stencil过滤时过滤后分组的显示高度无法根据过滤节点自适应问题

问题1. 部分分组中节点拖拽添加或操作后撤销重做操作不生效。 前提&#xff1a;使用Stencil插件&#xff0c;创建画布侧边栏的 UI 组件&#xff0c;同时使用其分组、折叠能力。分组包含固定分组、后台接口获取的动态分组和组件。 //固定分组初始化 initStencil (graph, stenc…

C语言——深入理解指针(5)

目录 1. sizeof和strlen的对比 1.1 sizeof 1.2 strlen 1.3 sizeof和strlen 的对比 2. 数据和指针题解析 2.1 一维数组 2.2 字符数组 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2,6 2.3 二维数组 3. 指针运算题解析 3.1 例1 3.2 例2 3.3 例3 3.4 例4 3.5 例5 3.6 例…

DSShop移动商城网店系统 反序列化RCE漏洞复现

0x01 产品简介 DSShop是长沙德尚网络科技有限公司推出的一款单店铺移动商城网店系统,能够帮助企业和个人快速构建手机移动商城,并减少二次开发带来的成本。 以其丰富的营销功能,精细化的用户运营,解决电商引流、推广难题,帮助企业打造生态级B2C盈利模式商业平台。完备的电商…

EasyRecovery2024免费永久版手机数据恢复软件

EasyRecovery2024是一款操作安全、用户可自主操作的数据恢复方案&#xff0c;它支持从各种各样的存储介质恢复删除或者丢失的文件&#xff0c;其支持的媒体介质包括&#xff1a;硬盘驱动器、光驱、闪存、硬盘、光盘、U盘/移动硬盘、数码相机、手机以及其它多媒体移动设备。能恢…

将.tiff格式图片转换为可视化的png,jpg,bmp等格式(附代码)

目前常用的.tiff格式图片转png格式图片&#xff0c;搜了一下&#xff0c;多数都是第三方平台直接转换&#xff0c;需要米&#xff0c;其实大可不必&#xff0c;自己撸代码就可以直接转换。 TIFF&#xff08;Tagged Image File Format&#xff09;是一种灵活的位图格式&#xf…

探索人工智能领域——每日20个名词详解【day7】

目录 前言 正文 总结 &#x1f308;嗨&#xff01;我是Filotimo__&#x1f308;。很高兴与大家相识&#xff0c;希望我的博客能对你有所帮助。 &#x1f4a1;本文由Filotimo__✍️原创&#xff0c;首发于CSDN&#x1f4da;。 &#x1f4e3;如需转载&#xff0c;请事先与我联系以…

YITH Product Countdown Premium电商商城产品倒计时高级版插件

点击阅读YITH Product Countdown Premium电商商城产品倒计时高级版插件原文 YITH Product Countdown Premium电商商城产品倒计时高级版插件的作用通过显示优惠的剩余时间或特定产品的可用数量的倒计时来促进销售。 您如何从中受益&#xff1a; 您可以利用稀缺性原则来增加转…

Open Inventor 2023.2.1 Crack

Fixed Bugs List 2023.2 2023.2.1 Open Inventor 2023.2.1 MeshViz #OIV-4824 Crash in MeshViz PbNonLinearDataMapping::computeColor Cache #OIV-4867 SoText3 : Texture read access violation – CAS-44904 Core #OIV-4725 Invalid displayed PoCircle color…

虹科案例 | OPC UA SDK快速扩展VIMANA智能制造软件连接性

导读&#xff1a; 虹科Matrikon在为OPC兼容性构建软件方面享有盛誉&#xff0c;VIMANA也因此选择使用我们的FLEX OPC UA SDK来快速扩展其智能制造软件的连接性&#xff0c;从而使用户能够“收听”其所有兼容OPC UA的设备&#xff0c;本篇案例文章将带你了解OPC UA SDK的现实应用…

《python每天一小段》--(2)使用Paramiko库,批量操作linux服务器

使用Paramiko库&#xff0c;批量操作多Linux服务器 使用Paramiko库建立SSH连接并执行命令获取多个Linux服务器的磁盘信息。通过这个例子&#xff0c;你可以学习到如何使用Python自动化操作远程服务器。 操作如图&#xff1a; 查看了两台服务器的磁盘容量&#xff1a; 安装模…

【电路笔记】-电阻器额定功率

电阻器额定功率 文章目录 电阻器额定功率1、概述2、电阻功率&#xff08;P&#xff09;3、功率电阻器4、电阻器额定功率示例15、电阻器额定功率示例2 电能被电阻吸收&#xff0c;因为它是电压和电流的乘积&#xff0c;一些电阻将这种电能转化为热量。 1、概述 当电流由于电阻器…

正则表达式(3):入门

正则表达式&#xff08;3&#xff09;&#xff1a;入门 小结 本博文转载自 从这篇文章开始&#xff0c;我们将介绍怎样在Linux中使用”正则表达式”&#xff0c;如果你想要学习怎样在Linux中使用正则表达式&#xff0c;这些文章就是你所需要的。 在认识”正则表达式”之前&am…

【unity3D】unity中如何查找和获取游戏物体

&#x1f497; 未来的游戏开发程序媛&#xff0c;现在的努力学习菜鸡 &#x1f4a6;本专栏是我关于游戏开发的学习笔记 &#x1f236;本篇是unity中游戏物体的查找与获取 这里写自定义目录标题 获取当前物体的基本属性查找其它物体- 通过名称查找其它物体- 通过标签查找- 通过类…

微信开发者工具里面模拟操作返回、录屏、网络速度、截屏等操作

有时候可能因为自己开发的页面使用了自定义的样式&#xff0c;所以没有了左上角的导航返回&#xff0c;所以进入我自定义的页面后&#xff0c;想要返回的话&#xff0c;就可以用到这里面你的操作&#xff0c;或者想截屏了也可以直接使用里面的截屏&#xff0c;或者返回主页面等…

如何有效进行测试执行进度计划

测试执行通常都是处于软件测试生命周期的关键路径上&#xff0c;它不仅在测试过程中占有重要的地位&#xff0c;并且也会花费大量的测试时间。针对测试执行而进行的计划&#xff0c;即测试执行进度计划&#xff0c;是进行测试执行进度控制的基础。在进行测试执行进度计划制订的…