fly-barrage 前端弹幕库(3):滚动弹幕的设计与实现

项目官网地址:https://fly-barrage.netlify.app/;
👑🐋🎉如果感觉项目还不错的话,还请点下 star 🌟🌟🌟。
Gitee:https://gitee.com/fei_fei27/fly-barrage(Gitee 官方推荐项目);
Github:https://github.com/feiafei27/fly-barrage;

其他系列文章:
fly-barrage 前端弹幕库(1):项目介绍
fly-barrage 前端弹幕库(2):弹幕内容支持混入渲染图片的设计与实现
fly-barrage 前端弹幕库(3):滚动弹幕的设计与实现

今天和大家说说滚动弹幕的设计与实现,为了便于理解,我会由简到难的一步步解析说明,主要包括以下几块内容:

  • 实现一条滚动弹幕的计算与渲染;
  • 实现多条滚动弹幕的计算与渲染;
  • 实现多条滚动弹幕相同字号并且不允许弹幕重叠的计算与渲染;
  • 实现多条滚动弹幕不同字号并且不允许弹幕重叠的计算与渲染;

1:实现一条滚动弹幕的计算与渲染

滚动弹幕具有如下两个特性:

export type BaseBarrageOptions = {// 弹幕的出现时间(毫秒为单位)time: number;
}
export type RenderConfig = {// 弹幕运行速度,仅对滚动弹幕有效(每秒多少像素)speed: number;
}

time 属性表示这个弹幕在视频播放器的时间是 time 的时候,这个滚动弹幕的左侧应该紧贴 Canvas 的右侧,此时弹幕的左侧距离 Canvas 的左侧距离正好是 Canvas 的宽,逻辑图如下所示:
在这里插入图片描述
speed 属性用于描述滚动弹幕的速度,意为每秒会移动多少像素。

每个滚动弹幕都有一个 originalLeft 属性,它标识着当播放进度是 0 的时候,滚动弹幕左侧距离 Canvas 左侧的距离,它的算法如下所示:

// 计算公式是:Canvas 元素的宽 + 弹幕出现时间 * 弹幕速度
this.originalLeft = this.br.canvasSize.width + (this.time / 1000) * this.br.renderConfig.speed;

如果某一个滚动弹幕的 time 属性为 0 的话,它的 originalLeft 正好等于 Canvas 的宽。

除了计算 originalLeft,还需要计算弹幕文本的宽,用于辅助计算某条滚动弹幕应不应该被 Canvas 实际渲染出来,弹幕宽度的计算方式可以看我的上一篇博客。

然后随着视频的播放,我们需要根据当前的播放进度计算出滚动弹幕左移的距离,计算方式如下所示:

// 弹幕整体向左移动的总距离,时间 * 速度
const translateX = (time / 1000) * this.br.renderConfig.speed;

左移的距离计算出来之后,我们就可以计算出这个滚动弹幕此时左侧距离 Canvas 左侧的距离,计算方式如下所示:

barrage.originalLeft - translateX

当滚动弹幕的左侧在 Canvas 右侧的左面并且滚动弹幕的右侧在 Canvas 左侧的右面的话,这个滚动弹幕就应该被渲染出来,借此我们可以计算出滚动弹幕是否会被渲染出来,计算方式如下所示:

const isShouldRender =// 弹幕的 originalRight 属性等于弹幕的 originalLeft 加上弹幕的宽度barrage.originalRight - translateX >= 0 &&barrage.originalLeft - translateX < this.br.canvasSize.width

如果 isShouldRender 变量为 true 的话,对弹幕进行绘制渲染操作即可(由于只有一条弹幕,所以暂不讨论弹幕渲染时的 top,随便设置一个 top 即可)。

2:实现多条滚动弹幕的计算与渲染

弹幕除了 left,还有 top 属性需要关注。在 B 站看视频,可以发现弹幕都是在固定的轨道中前后滚动的,就像现实生活中的高速公路一样,这样处理的好处是可以让播放的弹幕更加工整有层次,如果弹幕太乱的话,也会影响视频的浏览体验。

所以这里需要引入轨道的概念,借助轨道辅助计算弹幕的 top,让弹幕的滚动都是在轨道中。

轨道的高度等于滚动弹幕的高度,计算公式如下所示:

// 弹幕的高度等于弹幕字号乘以行高
const trackHeight = barrage.fontSize * barrage.lineHeight;

然后计算轨道的个数,计算公式如下所示:

// 计算总跑道数(canvas 高度 / 一个弹幕的高度)
const trackNum = Math.floor(canvas.height / trackHeight);

第 n 个轨道的 top 计算公式如下所示:

const n = 10;
const trackTop = (n - 1) * trackHeight;

然后我们遍历滚动弹幕,随机 push 进某一个轨道,将轨道的 top 设置给弹幕的 top 属性即可实现多条弹幕在轨道中滚动的效果。

3:实现多条滚动弹幕相同字号并且不允许弹幕重叠的计算与渲染(轨道算法)

当滚动弹幕的字号都是相同的时候,此时进行的不重叠计算是很简单的,计算步骤如下所示:

  1. 将所有的滚动弹幕按照 time 属性进行一次排序,time 小的排在前面;
  2. 遍历所有的滚动弹幕,判断当前循环的滚动弹幕能不能放到某一个实际轨道中,从上往下判断哪一个实际轨道能放进去;
  3. 如果当前判断的实际轨道中还没有弹幕或者当前实际轨道的最后一个弹幕和当前新增弹幕不重叠的话(是否会发生重叠可以通过两个滚动弹幕的 originalLeft 和 originalRight 计算出来 lastScrollBarrage.originalRight <= barrage.originalLeft),则表明当前循环的弹幕可以放到当前这个实际轨道中并且不会发生重叠的现象;
  4. 如果找到了能放进去的轨道的话,则将这个轨道的 top 设置给弹幕的 top 属性,并将当前这个弹幕的实例 push 到对应轨道的数组中即可;
  5. 如果没有找到能放进去的轨道的话,则不渲染当前这个弹幕,因为没有地方可以不重叠的渲染这个滚动弹幕;
  6. 至此滚动弹幕的不重叠 top 就计算出来了,再结合第一小节的知识进行渲染即可;

相关逻辑图如下所示:
初始阶段,所有轨道都为空:
初始阶段
第一个滚动弹幕能直接放到第一个轨道中:
第一个滚动弹幕
第二个滚动弹幕,和第一条轨道的最后一个弹幕有重叠,第二条轨道没有弹幕,所以放到了第二条轨道中:
第二个滚动弹幕
第三个滚动弹幕和第一个轨道的最后一个弹幕不重叠,所以能放到第一个轨道中:
第三个滚动弹幕
后面的弹幕按此逻辑逐个计算即可。

4:实现多条滚动弹幕不同字号并且不允许弹幕重叠的计算与渲染(虚拟轨道算法)

虚拟轨道算法的相关概念

实际轨道

这里的实际轨道和上面说的轨道是一个东西,只不过他的高度不能简单的视为 字号 x 行高,因为每个弹幕的字号和行高在极限情况下可以都是不同的。

这里规定实际轨道的高度是:所有弹幕字号 x 行高形成数字数组中的众数。

虚拟轨道

虚拟轨道是相邻实际轨道的合并,例如,我们现在有 4 个实际轨道,依次是:rt1、rt2、rt3、rt4,能够组成的虚拟轨道有 10 个分别是:
高度为 1 的:[rt1]、[rt2]、[rt3]、[rt4]
高度为 2 的:[rt1, rt2]、[rt2, rt3]、[rt3, rt4]
高度为 3 的:[rt1, rt2, rt3]、[rt2, rt3, rt4]
高度为 4 的:[rt1, rt2, rt3, rt4]
每个虚拟轨道都有一个对应的数组,用于存放弹幕,弹幕只能存放到虚拟轨道中,不要放到实际轨道中。

计算过程

  1. 计算弹幕高度,判断它适合放在高度为几的虚拟轨道,假设它有两个实际轨道的高度,那么它应该放在高度为2的虚拟轨道;
  2. 遍历高度为 2 的 3 个虚拟轨道,判断有没有虚拟轨道能放下,这里先判断 [rt1, rt2] 能不能放下,能放下的条件是:所有包含 rt1 或者 rt 2 两个实际轨道的虚拟轨道的最后一个弹幕和当前新增弹幕不重叠,如果满足这个条件的话,则 [rt1, rt2] 能放下,否则放不下,继续判断下一个虚拟轨道 [rt2, rt3] 能不能放下,如果高度为 2 的 3 个虚拟轨道都遍历完了,还没有的话,就说明当前新增弹幕无法不重叠的放入;
  3. 如果能够找到不重叠虚拟轨道的话,根据虚拟轨道计算 top,并将当前的弹幕实例 push 到对应虚拟轨道对应的数组中;

相关逻辑图如下所示:
初始阶段,所有轨道都为空:
初始阶段,所有轨道都为空
第一个弹幕的高度是2个实际轨道,能放到 [rt1, rt2]:
第一个弹幕的高度是2个实际轨道,能放下
第二个弹幕的高度是1个实际轨道, [rt1] 和 [rt2] 都放不下,因为第一个弹幕 [rt1, rt2] 占据了这 2 个实际轨道,不过能放到 [rt3]:
放失败的场景
放成功的场景
第三个弹幕的高度是3个实际轨道,它哪个虚拟轨道都放不下:
在这里插入图片描述
后续的新弹幕逻辑以此类推,对应的代码实现如下所示:

/*** 进行不允许重叠的布局* @param scrollBarrages 滚动弹幕实例数组*/
avoidOverlapLayout(scrollBarrages: ScrollBarrage[]) {const startTime = Date.now();// 将所有虚拟轨道中的存储弹幕清空this.virtualTracks.forEach(vt => vt.clearBarrage());// 遍历进行布局计算scrollBarrages.forEach(barrage => {// 遍历 grade 属性为 grade 的虚拟轨道,判断能不能放进去const fittedVirtualTracks = this.gradeToVtsMap.get(barrage.grade) || [];for (let i = 0; i < fittedVirtualTracks.length; i++) {// 当前遍历的 virtualTrackconst virtualTrack = fittedVirtualTracks[i];// 判断当前遍历的 virtualTrack 能不能放进去;// 能放进去的条件是:包含 virtualTrack 内部任一实际轨道的虚拟轨道(grade <= maxGrade)的最后一个弹幕和当前新增弹幕都不重叠const canPush = (this.vtToVtsMap.get(virtualTrack) || []).every(item => {// 获取最后一个滚动弹幕const lastScrollBarrage = item.getLastBarrage();// 如果当前轨道没有滚动弹幕的话,说明和最后一个弹幕不可能重叠,直接 return true 即可if (!lastScrollBarrage) return true;// 有的话,判断是否会重叠return lastScrollBarrage.originalRight + this.minSpace <= barrage.originalLeft;});if (canPush) {barrage.show = true;virtualTrack.push(barrage);// 计算该滚动弹幕的 topbarrage.top = virtualTrack.top;break;} else {barrage.show = false;}}// 重要的弹幕,如果没有虚拟轨道能插进去的话,则随机一个实际轨道if (barrage.prior && !barrage.show) {this.randomTrackBarrage(barrage);}});this.isLogKeyData && console.log(`虚拟轨道算法花费时间:${(Date.now() - startTime)}ms`);
}

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

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

相关文章

显示器开机正常,插入HDMI线却不显示画面,换了HDMI线还是不行?

环境&#xff1a; 惠普/P24VG4 DELL笔记本 问题描述&#xff1a; 显示器开机正常&#xff0c;插入HDMI线却不显示画面&#xff0c;换了HDMI线还是不行&#xff0c;是不是显示器坏了&#xff1f; 解决方案&#xff1a; 1.前往显示器设置菜单里面查看input 2.把输入源默认设…

二百二十五、海豚调度器——用DolphinScheduler调度执行Flume数据采集任务

一、目的 数仓的数据源是Kafka&#xff0c;因此离线数仓需要用Flume采集Kafka中的数据到HDFS中 在实际项目中&#xff0c;不可能一直在Xshell中启动Flume任务&#xff0c;一是项目的Flume任务很多&#xff0c;二是一旦Xshell页面关闭Flume任务就会停止&#xff0c;这样非常不…

案例研究|DataEase助力众陶联应对产业链数据可视化挑战

佛山众陶联供应链服务有限公司&#xff08;以下简称为“众陶联”&#xff09;成立于2016年&#xff0c;是由34家陶瓷企业共同创办的建陶行业工业互联网平台&#xff0c;股东产值占整个行业的22.5%。众陶联以数据赋能为核心&#xff0c;积极探索新的交易和服务模式&#xff0c;构…

帝国cms7.5仿非小号区块链门户资讯网站源码 带手机版

帝国cms7.5仿非小号区块链门户资讯网站源码 带手机版 带自动采集 开发环境&#xff1a;帝国cms 7.5 安装环境&#xff1a;phpmysql 包含火车头采集规则和模块&#xff0c;采集目标站非小号官网。 专业的数字货币大数据平台模板&#xff0c;采用帝国cms7.5内核仿制&#xff0…

Ai-WB2-32S在window下使用vs 和 msys2编译以及烧录

文章目录 前言一、使用前准备第一步 安装vscode第二步 安装msys2 二、使用步骤1.打开MSYS2 MINGW64&#xff08;1&#xff09;在开始栏中找到MSYS2 MINGW64并打开&#xff08;2&#xff09;安装git&#xff08;3&#xff09;安装make&#xff08;4&#xff09;安装好之后的文件…

Redis 之五:Redis 的主从复制

概念 主从复制&#xff0c;是指将一台 Redis 服务器的数据&#xff0c;复制到其他的Redis服务器。前者称为主节点(master)&#xff0c;后者称为从节点(slave)&#xff1b;数据的复制是单向的&#xff0c;只能由主节点到从节点。 默认情况下&#xff0c;每台Redis服务器都是主节…

云天励飞战略投资神州云海,布局机器人市场

日前,AI上市企业云天励飞(688343.SH)完成了对深圳市神州云海智能科技有限公司(以下简称“神州云海”)的B轮战略投资。 公开资料显示,自2015年于深圳创立以来,神州云海始终聚焦人工智能与服务机器人广阔的应用市场,依托自主的核心算法能力,深耕机器人硬件本体研发,整合上下游产…

RabbitMQ-TTL/死信队列/延迟队列高级特性

文章目录 TTL死信队列消息成为死信的三种情况队列如何绑定死信交换机 延迟队列RabbitMQ如何实现延迟队列 总结来源B站黑马程序员 TTL TTLTTL(Time To Live):存活时间/过期时间当信息到达存活时间后&#xff0c;还没有被消费&#xff0c;会被自动清除。RabbitMQ可以对消息设置过…

Win10系統如何重置系统

Win10系統如何重置 大家可以使用Win10內建的重設電腦設定&#xff0c;如以下操作&#xff1a; 首先&#xff0c;可以先到桌面左下角的【開始】 選擇【設定】 在【設定】裡找到【更新與安全性】 在左側欄有一項【復原】 在復原的標題下&#xff0c;副標題有一項【重設此電腦】…

【algorithm】算法基础课---排序算法(附笔记 | 建议收藏)

&#x1f680;write in front&#x1f680; &#x1f4dd;个人主页&#xff1a;认真写博客的夏目浅石. &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd; &#x1f4e3;系列专栏&#xff1a;AcWing算法学习笔记 &#x1f4ac;总结&#xff1a;希望你看完…

​The Sandbox的南极之旅|链接世界:从南极洲到元宇宙

真正的发现之旅不在于寻找新的景观&#xff0c;而在于拥有新的眼光。 - 马塞尔-普鲁斯特 在这个数字世界和物理世界日益交织的时代&#xff0c;The Sandbox 的联合创始人 Arthur Madrid 和 Sebastien Borget 踏上了远离数字空间的旅程&#xff0c;前往地球上未被开发的宝藏地点…

无用工作、UBI与AI

有些隐晦和黑暗的事实无法陈述&#xff0c;因为任何的系统中“无用”的结局都是被无情的抛弃和淘汰&#xff0c;AI监督下的人类结局更是如此。 什么是无用工作&#xff1f; 无用无效工作通常指的是那些看似忙碌但实际上对社会或个人没有实质性贡献的工作。这类工作可能包括以下…

2024环境工程、能源系统与化学材料国际会议(ICEEESCM 2024)

2024环境工程、能源系统与化学材料国际会议&#xff08;ICEEESCM 2024) 一、【会议简介】 2024环境工程、能源系统与化学材料国际会议&#xff08;ICEEESCM 2024)将于2024年在西安举行。会议将围绕环境工程、能源系统与化学材料等议题展开讨论&#xff0c;旨在为从事环境工程…

ABB双语言共享充电宝投资理财源码/共享充电宝系统源码/共享充电宝市场分析/五级分销返利+地图显示模式

ABB双语言共享充电宝投资理财源码/五级分销返利地图显示模式/vue编译后前端 测试环境&#xff1a;Linux系统CentOS7.6、宝塔、PHP7.3、MySQL5.6&#xff0c;根目录public&#xff0c;伪静态laravel5&#xff0c; 源码下载&#xff1a;https://download.csdn.net/download/m0_…

人脸高清算法GFPGAN之TensorRT推理

1. 综述 最近由于做数字人项目&#xff0c;采用的是wav2lip GFPGAN进行人脸面部高清&#xff0c;但GFPGAN模型本身比较大&#xff0c;所以想着使用TensorRT来代替原始的pth推理看看能否提升运行速度&#xff0c;于是便开始了这趟windows1之下进行GFPGAN的trt推理的折腾之旅。…

varFormatter 数据格式化库 以性能优先的 快速的 内存对象格式转换

varFormatter 数据格式化 技术 开源技术栏 对象/变量格式化工具库&#xff0c;其支持将一个对象进行按照 JSON XML HTML 等格式进行转换&#xff0c;并获取到结果字符串&#xff01; 目录 文章目录 varFormatter 数据格式化 技术目录介绍获取方式 使用实例格式化组件的基本使…

图书推荐||Word文稿之美

让你的文档从平凡到出众&#xff01; 本书内容 《Word文稿之美》是一本全面介绍Word排版技巧和应用的实用指南。从初步认识数字排版到高效利用模板、图文配置和表格与图表的排版技巧&#xff0c;再到快速修正错误和保护文件&#xff0c;全面系统地讲解数字排版的技术和能力&…

靶机渗透之My File Server: 1

Name: My File Server: 1Date release: 21 Feb 2020Author: Akanksha Sachin VermaSeries: My File ServerDownload: https://drive.google.com/uc?id1w0grAomPuFaIohBcUwDiI3QIi4fj4kje&exportdownload 对于vulnhub中的靶机&#xff0c;我们都需先下载镜像&#xff0c;然…

Redis 在 Linux 系统下安装部署的两种方式详细说明

小伙伴们好&#xff0c;欢迎关注&#xff0c;一起学习&#xff0c;无限进步 Redis安装和配置 1、首先在官网下载好redis-6.0.9.tar.gzhttp://redis.io/ 或者使用 wget 命令下载&#xff1a;wget http://download.redis.io/releases/redis-6.0.9.tar.gz 2、下载使用上传到阿里…

Entry First Day 入职恩孚第一天

入职第一天&#xff0c;电脑还没配置好就去了工厂。 熟悉了一下设备&#xff0c;切了几个小玩意&#xff0c; hello world 一下。 了解了串行端口的Nodejs的库 https://github.com/serialport/node-serialport&#xff0c;以后要用这个东西和硬件通讯&#xff0c;安装&#…