弹幕

前言

本文的弹幕效果跟B站上的看起来差不多。本文demo利用原生JavaScript搭建。
本文的源码github地址https://github.com/tjlcy/BulletScreen
弹幕获取方法

在Google 浏览器中的扩展程序中下载哔哩哔哩助手,插件开启后在哔哩哔哩的视频页会看见一个粉色的方块写着助手,打开后有一个下载弹幕,可以下载xml格式的弹幕,然后可以用在线工具将弹幕数据转化成json格式的数据,便于操作。
哔哩哔哩助手下载地址:https://chrome.zzzmh.cn/info?token=kpbnombpnpcffllnianjibmpadjolanh

参数设置

视频区域:1120*630
每行弹幕高度:30
最多弹幕行数:16
弹幕字体大小:22
弹幕速度随机范围:100-300
(每秒移动的像素值)

js逻辑

1.初始化弹幕区,绑定弹幕区标签,获取弹幕区宽度,设置弹幕区的最多行数,每行弹幕的高度以及当前行初始设置为0。

2.创建弹幕函数,创建放置弹幕span标签,将它插入到弹幕区之中,然后设置弹幕内容颜色获取(传参获取到数据),之后初始化弹幕的位置,当前位置变动,设置弹幕速度(随机速度需要设置获取随机数的函数),最后存储弹幕的left和width初始的值,以便之后使用。

位置初始化

弹幕都是从右侧滚动出现,所以它的初始位置是在弹幕区的外面,即弹幕区宽度数值的位置;并且弹幕都是先第一行然后才是第二行,垂直位置由当前行数和弹幕的高度确定,例如,当前行数是2,那么垂直位置就是 top = 2*每行弹幕的高度(top是从上往下,数值递增)。

当前位置变动

一开始可能会想到直接写当前行++,但是考虑到弹幕区存在最多行数,所以要设置在当前行等于最大行数情况下回到初始化当前行数。

3.弹幕移动函数,初始化定时器,每隔一段时间执行一次,时间初始化(数字不要直接写在定时器上,不便于维护,建议写在变量上,便于后期维护),每隔一段时间遍历弹幕标签,并让弹幕的left重新赋值。
      特别注意要做边缘检测

4.弹幕暂停,把定时器清除,也就是移动函数的遍历停止,并且记得让定时器回到初始状态,否则会出现累积。

5.建立事件,
视频播放时,弹幕移动函数运行

视频暂停时,弹幕暂停函数运行

视频位置变动时,创建弹幕的函数运行,遍历数据并且按照时间设置所需的弹幕,超出当前时间的弹幕不再创建(需要判断弹幕时间和当前时间的关系)

在视频当前位置改变事件中,

  • 判断弹幕的状态(开启/关闭)
  • 判断此时间点弹幕是否建立过
  • 判断是否拖动
    (后两个主要是判断上一次创建弹幕的时间与当前时间的关系)
    6.按钮,先绑定,当点击时切换弹幕的状态。

源码

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><link rel="stylesheet" href="./index.css">
</head><body><!-- 视频区 --><div id="container"><!-- 视频 --><video src="./MC.mp4" id="video" controls></video></div><!-- 弹幕区 --><div id="text-container"><!-- 弹幕 --><span></span></div><!-- 按钮 --><button id="toggle">显示/隐藏弹幕</button><!-- 数据 --><script src="./danmu.js"></script><!-- 交互操作 --><script src="./index.js"></script>
</body></html>
* {margin: 0;padding: 0;
}/* 视频容器 */#container {position: relative;width: 1120px;height: 630px;/* 居中 */margin: 0 auto;
}/* 视频充满整个容器 */#container video {width: 100%;height: 100%;
}/* 弹幕区域 */#text-container {width: 1120px;margin: 0 auto;position: absolute;/* 居中 */left: 0;right: 0;top: 0;/* 放置按钮被弹幕遮盖,而无法触发 */bottom: 100px;font-size: 22px;color: #fff;/* 超出区域隐藏 */overflow: hidden;/* 让背景浅的地方仍能够看见弹幕文字 */text-shadow: 0 0 2px rgba(0, 0, 0, 0.5);
}/* 弹幕 */#text-container span {position: absolute;/* 不换行 */white-space: nowrap;
}/* 按钮 */#toggle {/* 转换成块盒,独占一行 */display: block;/* 水平居中 */margin: 20px auto;
}
// 初始化
var bulletScreenContainer = document.getElementById("text-container"); //放置弹幕的容器
var bulletScreenWidth = bulletScreenContainer.clientWidth; //获取放弹幕的容器的宽度
var bulletLine = 16; //弹幕行数(Max)
var bulletHeight = 30; //每行弹幕的高度
var CurrentLine = 0; //当前行
/*** @param {*} 创建弹幕* @param {*} content 弹幕的内容* @param {*} color   弹幕的颜色*/
function bulletScreen(content, color) {var span = document.createElement("span"); //创建每个弹幕的容器bulletScreenContainer.appendChild(span); //将每个弹幕放到弹幕区域中span.innerText = content; //弹幕内容根据数据显示在页面中span.style.color = color; //弹幕颜色根据数据设置相应的颜色// 弹幕位置初始化// 水平var left = bulletScreenWidth;span.style.left = left + "px";// 垂直var top = bulletHeight * CurrentLine;span.style.top = top + "px";// 当前行变化// CurrentLine++; 错误 会出现超出行数范围的情况CurrentLine = (CurrentLine + 1) % bulletLine;// 写法2:// if (CurrentLine === bulletLine) {//     CurrentLine = 0;// }// 设置弹幕的速度(随机)span.speed = getRandom(100, 301); //左闭右开// 存储初始值span.left = left;span.width = span.clientWidth;
};
// 获取随机速度
function getRandom(min, max) {return Math.floor(Math.random() * (max - min) + min);
}
var timer = null; //定时器id初始化
// 弹幕移动
function Move() {// 如果timer有值if (timer) {// 已经在移动了return;}var duration = 14; //时间初始化timer = setInterval(function() {// 遍历弹幕标签for (var i = 0; i < bulletScreenContainer.children.length; i++) {var span = bulletScreenContainer.children[i];span.left -= (span.speed * duration) / 1000; //s=vt  1秒 = 1000毫秒span.style.left = span.left + "px"; //把新的位置应用到元素上// 超出边缘移除spanif (span.left <= -span.width) {span.remove();i--;}}}, duration);};
// 弹幕暂停
function Stop() {// 清除定时器clearInterval(timer);// 让定时器回到初始状态timer = null;
};
// 弹幕清空
function Clear() {// 弹幕内容清空bulletScreenContainer.innerHTML = '';// 当前行回到初始值CurrentLine = 0
};
var video = document.getElementById("video"); //绑定视频
// 视频播放时,弹幕移动
video.onplay = function() {Move();
};
// 视频暂停时,弹幕暂停
video.onpause = function() {Stop();
};
var lastCreateTime = -1; //上次创建弹幕的时间点初始化
// 视频当前位置改变时,弹幕出现
video.ontimeupdate = function() {// 判断弹幕状态(开启/关闭)if (isClose) {// 如果弹幕已关闭,不再创建return;};var currentTime = parseInt(video.currentTime); //当前时间初始化// 判断是否拖动if (Math.abs(currentTime - lastCreateTime) > 1) {Clear();};// 判断这个时间点的弹幕是否创建过if (currentTime === lastCreateTime) {// 这个时间点的弹幕已经创建过了,不需要重新创建return;}for (var i = 0; i < danmu.length; i++) {var d = danmu[i];if (d.time === currentTime) {bulletScreen(d.content, d.color);} else if (d.time > currentTime) {break;}}lastCreateTime = currentTime;
};
var btn = document.getElementById("toggle"); //绑定按钮
var isClose = false; //弹幕默认开启状态
// 按钮点击时,切换弹幕的状态(开启/关闭)
btn.onclick = function() {// 判断弹幕是否关闭if (isClose) {isClose = false;} else {isClose = true;Clear(); //关闭弹幕,清空弹幕}}

结语

html和css的逻辑部分比较简单,看代码即可。另外,视频和弹幕任你自由选择。本文主要梳理弹幕的JavaScript逻辑。代码可在我的gitbub https://github.com/tjlcy中查看,也可以在本文中查看。如有不当,还请指出。如果喜欢我的文章,点赞并关注我哦。

前端demo逻辑系列之评分插件之弹幕相关推荐

  1. 前端demo逻辑系列之炫酷字体

    炫酷字体之放大缩小变色 前言 很久没更博客了,一直忙于向后学习,并且也思考了我原来那种博客的模式没什么意思,纯教程笔记类博客过于千篇一律,所以我打算开始更新一些学习中的思路方面想法方面的技术博客.本期 ...

  2. 用YSlow评分插件分析我们页面

    YSlow是yahoo美国开发的一个页面评分插件,非常的棒,从中我们可以看出我们页面上的很多不足,并且可以知道我们改怎么却改进和优化. 仔细研究了下YSlow跌评分规则. 主要有12条: 1. Mak ...

  3. 一个非常棒的jQuery 评分插件--好东西要分享

    现在做网页已经不仅限于实现功能了,更多的是要实现功能的同时追求更加美观的实现.比如页面上让用户评分的功能,你完全可以放5个RdioButton让用户选择分数,也可以用DropDownList来实现,但 ...

  4. 注释那些事儿:前端代码质量系列文章(一)

    摘要: 好的注释可以提高代码的可读性和可维护性,从而提高代码质量.那么什么是好的注释?如何写出好的注释? "Comment or not comment, that is the quest ...

  5. vue2中,使用vite流程之应用前端构建工具vite和vite-plugin-vue2插件

    vue2中,使用vite流程之应用前端构建工具vite和vite-plugin-vue2插件 Vite 官方中文文档--https://cn.vitejs.dev/ vite-plugin-vue2包 ...

  6. 【西瓜】全套打包微社区 西瓜同城分类信息系列打包整套插件(30个插件+小程序)

    [西瓜]微社区 西瓜插件 全套打包微社区 西瓜同城分类信息系列打包整套插件(30个插件+小程序 特惠)西瓜同城系列全套插件 西瓜本地同城分类信息整站 西瓜同城小程序 西瓜同城分类信息系统 ====== ...

  7. 王下邀月熊_Chevalier的前端每周清单系列文章索引

    感谢 王下邀月熊_Chevalier 分享的前端每周清单,为方便大家阅读,特整理一份索引. 王下邀月熊大大也于 2018 年 3 月 31 日整理了自己的前端每周清单系列,并以年/月为单位进行分类,具 ...

  8. vscode 结束_8 个给前端的顶级 VS Code 扩展插件

    翻译:京程一灯 原文:https://1stwebdesigner.com/top-free-extensions-for-vs-code/ 微软的 VS (Visual Studio) Code 是 ...

  9. jQuery Raty星级评分插件使用方法

    转载自  jQuery Raty星级评分插件使用方法 使用jQuery Raty,可以很方便的在页面上嵌入一个评分组件,如下所示:      使用方法很简单,首先从https://github.com ...

最新文章

  1. 干货|深度!“人工智能+制造”产业发展研究报告
  2. 在大型软件中用Word做报表: 书签的应用
  3. 2.11 总结-深度学习第三课《结构化机器学习项目》-Stanford吴恩达教授
  4. MindSpore!这款刚刚开源的深度学习框架我爱了!
  5. LeetCode:棒球比赛【682】
  6. *【CodeForces - 280C】Game on Tree(期望模型,期望的线性性)
  7. 好用的下拉第三方——nicespinner
  8. python读取doc文件_Linux 下Python 读取Word文档内容的方法
  9. 如何踩中下一个技术节拍
  10. python写tcp通信程序_一个简单的基于TCP通信的服务器端与客户端程序
  11. Python:assert基本用法
  12. 236.Lowest Common Ancestor of a BinaryTree
  13. 使用 FME 将勘测定界坐标交换格式文本文件转换成 GIS 格式
  14. 网站服务器诊断分析,网站SEO优化诊断分析报告范文模板(一)
  15. 基于服务号的微信扫码关注公众号登录网站原理分析
  16. 野火指南者开发板移植 lvgl 库
  17. 微软云计算解决方案介绍
  18. 如何管理你的客户?别用会员卡积分打折了,把客户变成粉丝
  19. 单个Java文件打成可执行JAR包
  20. web语义化之SEO和ARIA

热门文章

  1. QY-19 GNSS位移监测站 山体边坡位移变形监测物联网解决方案
  2. 【FPGA零基础学习之旅#4】定时器设计与蜂鸣器驱动
  3. Codeforces Round #827 (Div. 4) A~G
  4. Problem F. 洗衣服
  5. Tkinter 对话框
  6. 阿里云智能客服系统:包括智能导航、客服助手、智能外呼、呼叫中心、在线客服、智能培训等。经阿里内部多年实际使用演变而来,功能齐全,产品化程度高,可本地化部署。有需要的可以联系我。
  7. ChatGPT写作:快速生成优质博客文章的神器
  8. 设计模式Part3——简单工厂模式
  9. Allegro基本元器件库路径
  10. 苏州园林景区网页设计 学生家乡网页设计作品静态HTML网页模板源码 DW大学生家乡主题网站制作 简单家乡介绍网页设计成品