弹幕

首先是弹幕的位置,是要从最右滑到最左,为了防止随机高度弹幕会覆盖的问题,设置了通道。

每一个通道是从左到右的一条,高度固定,这样不同通道的弹幕不会相互覆盖。

弹幕滑动就是简单设置CSS属性  transition 实现。开始使用 left 改变弹幕的位置,后来改为 transform ,性能确实提高很多。

设置10条弹幕通道,每个通道有一个DOM池,每一次发射弹幕就从DOM池中拿出一个DOM从右滑到左边直到消失,然后再放回DOM池,当DOM池为空时就不能再通过该通道发射弹幕了,通过这种方式来限制最大同屏弹幕数。

因为通过 transition 设置了弹幕滑动的时间,而这个时间固定的,距离弹幕最左露头到最右消失,也就是“屏幕宽度+弹幕长度”,所以: 弹幕越长,速度越快 。这样的话,后面特别长的弹幕就有可能超过前面比较短的弹幕,本来根据弹幕长度设置了滑动时间,但是跑去看了下B站弹幕也有这个属性,所以就又改回去了>_<

最后设置一个弹幕池,设置一个定时器不停的去弹幕池拿弹幕,当DOM空闲且有未发射弹幕时就发射弹幕。

点击发送按钮就是把弹幕放到弹幕池就好了。

效果图:

   

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>原生JS实现弹幕效果</title>
<style>
#wrapper {
height: 400px;
width: 700px;
position: relative;
overflow: hidden;
background: url(./game_manwall_213255_m.jpg);    //自己换个背景图
color: #ffffff82;
font-size: 14px;
text-shadow: 1px 1px #000;
margin: 0 auto;
}
.right {
position: absolute;
visibility: hidden;
white-space: nowrap;
/*left: 700px;*/
transform: translateX(700px);
}
.left {
position: absolute;
white-space: nowrap;
user-select: none;
transition: transform 7s linear; /* 时间相同 越长的弹幕滑动距离越长 所以越快~ */
}
input {
position: absolute;
bottom: 10px;
left: 150px;
width: 300px;
height: 26px;
}
button {
position: absolute;
bottom: 8px;
left: 476px;
width: 100px;
height: 38px;
border-radius: 10px;
font-size: 16px;
}
</style>
</head>
<body>
<div id="wrapper">
<input type="text">
<button id="bth">发&nbsp;&nbsp;送</button>
</div>
<script>
/**
* 设置 弹幕DOM池 每一个通道最多六条弹幕
**/
const MAX_DM_COUNT = 6;
const CHANNEL_COUNT = 10;
let domPool = [];
let danmuPool = [
'2333333', '233333366666666666666', '2333333', '2333333', '2333333',
'2333333', '2333333', '233333366666', '2333333', '2333333', '2333333',
'66666666','888888888','ahahahaha','2333333', '233333366666666666666', '2333333', '2333333', '2333333',
'2333333', '2333333', '233333366666', '2333333', '2333333', '2333333',
'66666666','888888888','ahahahaha','2333333', '233333366666666666666', '2333333', '2333333', '2333333',
'2333333', '2333333', '233333366666', '2333333', '2333333', '2333333',
'66666666','888888888','ahahahaha'
 
];
let hasPosition = [];
/**
* 做一下初始化工作
*/
function init() {
let wrapper = document.getElementById('wrapper')
// 先new一些span 重复利用这些DOM
for (let j = 0; j < CHANNEL_COUNT; j++) {
let doms = [];
for (let i = 0; i < MAX_DM_COUNT; i++) {
// 要全部放进wrapper
let dom = document.createElement('span');
wrapper.appendChild(dom);
// 初始化dom的位置 通过设置className
dom.className = 'right';
// DOM的通道是固定的 所以设置好top就不需要再改变了
dom.style.top = j * 20 + 'px';
// 放入改通道的DOM池
doms.push(dom);
// 每次到transition结束的时候 就是弹幕划出屏幕了 将DOM位置重置 再放回DOM池
dom.addEventListener('transitionend', () => {
dom.className = 'right';
// dom.style.transition = null;
// dom.style.left = null;
dom.style.transform = null;
domPool[j].push(dom);
});
}
domPool.push(doms);
}
// hasPosition 标记每个通道目前是否有位置
for (let i = 0; i < CHANNEL_COUNT; i++) {
hasPosition[i] = true;
}
}
/**
* 获取一个可以发射弹幕的通道 没有则返回-1
*/
function getChannel() {
for (let i = 0; i < CHANNEL_COUNT; i++) {
if (hasPosition[i] && domPool[i].length) return i;
}
return -1;
}
/**
* 根据DOM和弹幕信息 发射弹幕
*/
function shootDanmu(dom, text, channel) {
console.log('biu~ [' + text + ']');
dom.innerText = text;
// 如果为每个弹幕设置 transition 可以保证每个弹幕的速度相同 这里没有保证速度相同
// dom.style.transition = `transform ${7 + dom.clientWidth / 100}s linear`;
// dom.style.left = '-' + dom.clientWidth + 'px';
// 设置弹幕的位置信息 性能优化 left -> transform
dom.style.transform = `translateX(${-dom.clientWidth}px)`;
dom.className = 'left';
 
hasPosition[channel] = false;
var bth=document.getElementById("bth");
bth.onclick=function(){
dom.innerHTML=text.value;
};
// 弹幕全部显示之后 才能开始下一条弹幕
// 大概 dom.clientWidth * 10 的时间 该条弹幕就从右边全部划出到可见区域 再加1秒保证弹幕之间距离
setTimeout(() => {
hasPosition[channel] = true;
}, dom.clientWidth * 10 + 1000);
}
window.onload = function() {
init();
// 为input和button添加事件监听
let btn = document.getElementsByTagName('button')[0];
let input = document.getElementsByTagName('input')[0];
btn.addEventListener('click', () => {
input.value = input.value.trim();
if (input.value) danmuPool.push(input.value);
})
input.addEventListener('keyup', (e) => {
if (e.key === 'Enter' && (input.value = input.value.trim())) {
danmuPool.push(input.value);
}
})
// 每隔1ms从弹幕池里获取弹幕(如果有的话)并发射
setInterval(() => {
let channel;
if (danmuPool.length && (channel = getChannel()) != -1) {
let dom = domPool[channel].shift();
let danmu = danmuPool.shift();
shootDanmu(dom, danmu, channel);
}
}, 1);
}
 
</script>
</body>
</html>

JavaScript简单的弹幕的更多相关文章

  1. JavaScript实现简单的弹幕效果实例分析

    不知大家有没有感受到,弹幕又是另一出好戏!! 不过我个人还是比较排斥看电视的时候被出来的弹幕打扰.今天我们来写一个简单的弹幕.简单到什么程度呢?看下效果: 由图可以看出,我们的呆毛html结构确实是非 ...

  2. JavaScript简单入门(补充篇)

    本文是对上一篇 JavaScript简单入门 的一些细节补充. 一.全局变量和局部变量 在<script>标签内定义的变量是当前页面中的全局变量.即 <script>标签可以直 ...

  3. 《javascript高级程序设计》读书笔记(一)javascript简单介绍

    第一章:javascript简单介绍 Netscape Navigator 开发的javascript   Javascript的实现有三部分: 1.核心(ECMAScript):提供核心语言功能. ...

  4. Javascript 简单实现鼠标拖动DIV

    http://zhangbo-peipei-163-com.iteye.com/blog/1740078 比较精简的Javascript拖动效果函数代码 http://www.jb51.net/art ...

  5. 用react的ReactCSSTransitionGroup插件实现简单的弹幕动画

    1,开始的思路 公司想做直播方面的项目,并想加入弹幕的功能,直播的页面已经作为一个组件放在了用react+redux写好的一个网站项目上.所以技术老大让我研究下如何用react实现弹幕的功能.下面我就 ...

  6. [Java Web] 4、JavaScript 简单例子(高手略过)

    内容概览: JavaScript简介 JavaScript的基本语法 JavaScript的基本应用 JavaScript的事件处理 window对象的使用 JavaScript简介: JavaScr ...

  7. javascript - 简单实现一个图片延迟加载的jQuery插件

    最近在看一本书<Third-Party Javascript>很不错,推荐给大家,下载地址各位自己搜索了. 步骤: 1.打开google,鉴于google基本打不开,那么就打开这个网址吧. ...

  8. javascript 简单的计算器

    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm1.aspx. ...

  9. 基于CommentCoreLibrary简单的弹幕实现

    本文地址:http://www.cnblogs.com/liaoyu/p/ccl-demo.html 实现基于开源的 CommentCoreLibrary 最近有需求要实现一个简单的评论弹幕实现,通过 ...

随机推荐

  1. .NET----错误和异常处理机制

    前言 错误的出现并不总是编写程序的人的原因,有时应用程序会因为应用程序的最终用户引发的动作或运行代码的环境发生错误.无论如何,我们都应预测应用程序中出现的错误,并相应的进行编码. .Net改进了处理错 ...

  2. 为什么双重检查锁模式需要 volatile ?

    双重检查锁定(Double check locked)模式经常会出现在一些框架源码中,目的是为了延迟初始化变量.这个模式还可以用来创建单例.下面来看一个 Spring 中双重检查锁定的例子. 这个例子 ...

  3. Mybatis案例超详解(上)

    Mybatis案例超详解(上) 前言: 本来是想像之前一样继续跟新Mybatis,但由于种种原因,迟迟没有更新,快开学了,学了一个暑假,博客也更新了不少,我觉得我得缓缓,先整合一些案例练练,等我再成熟 ...

  4. Unity进阶之ET网络游戏开发框架 02-ET的客户端启动流程分析

    版权申明: 本文原创首发于以下网站: 博客园『优梦创客』的空间:https://www.cnblogs.com/raymondking123 优梦创客的官方博客:https://91make.top ...

  5. OpenGL入门第一天:环境

    本文是个人学习记录,学习建议看教程 https://learnopengl-cn.github.io/ 非常感谢原作者JoeyDeVries和各位翻译提供的优质教程 近况(牢骚 这几天教母校初中的OI ...

  6. 消息中间件——RabbitMQ(四)命令行与管控台的基本操作!

    前言 在前面的文章中我们介绍过RabbitMQ的搭建:RabbitMQ的安装过以及各大主流消息中间件的对比:,本章就主要来介绍下我们之前安装的管控台是如何使用以及如何通过命令行进行操作. 1. 命令行 ...

  7. git 导出新修改的文件

    git archive -o update.zip HEAD $(git diff --name-only HEAD)

  8. odoo t标签用法

    在odoo中,通过QWeb来对模板进行渲染后加载到浏览器中,而模板中有许多的标签来定制各种需求变化,在这里记录学习过程中碰到的标签定义,以方便查询. 模板中的标签统一都是以"t-" ...

  9. eShopOnContainers部署在docker的坑

    把eShopOnContainers(.net core 的版本是2.1)下载之后,部署到docker上,查看容器eShopOnContainers的项目都部署上去了. 用http://localho ...

  10. Vmware启动ubuntu 出现错误。

    Vmware启动ubuntu 出现错误“以独占方式锁定此配置文件失败. 可能其它正在运行VMware进程在使用此配置文件”. 在网上查找了很多方法,法(1)试过在启动任务管理器中“结束与VMware有 ...