原生js实现弹幕效果原理 二

距离写实现原理一有很长一段时间了,因为好像没什么人看,所以我就不太想写原理二,为了不食言,我还是坚持做完这个系列吧

下面介绍的就是目前主流直播弹幕网站的弹幕实现方式,包括弹幕指定暂停,点赞,举报功能.

首先来看一张实现动图

原理简介

  • 动画效果使用css3中的 transition 属性实现
  • 只有transition是远远不够的,因为transition需要主动触发,但是一般网上常见的简单的触发方式,就是 定义css样式的:hover或者:focus属性,指定用户的某一个活动主动触发transition,这显然是无法实现弹幕自动化效果的。因此,可以想到使用js触发。css触发说到底也就是当满足某种条件时,替换dom元素的class样式
  • 知道原理以后我们要做的就是,在适当的时机给弹幕dom添加上class或者移除class,在js里这是非常简单的,dom.classList.add(“xxx”)实现添加class,dom.classList.remove(“xxx”)移除class.
  • 上面强调的 适当 这个词大有文章,众所周知,js是一个单线程编程语言,可能有些人被setInterval和setTimeOut函数误导,以为是js引擎新开了一个线程去处理定时功能,但实际上,js引擎所做仅仅只是,1.将计时任务放到一个带有优先级的事件队列中 2.然后一个一个的执行事件
  • 上面说了js引擎是单线程按队列顺序执行事件,但每个事件之间不可避免的会有间隔时间,在这个间隔时间里js引擎是空闲的,当js引擎空闲时,就是GUI绘制引擎工作的时间了,GUI引擎做的工作就是渲染dom。即是说,并不是说js刚改变class,dom元素的样式就会刷新,中间有一个间隔时间,这个间隔时间就是GUI引擎在等待js引擎空闲,具体的细节你们可以去https://blog.csdn.net/qq_36995542/article/details/80007381看看。

源代码解析

首先是html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="utf-8"/><meta http-equiv="X-UA-Compatible" content=="IE=edge"/><meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests"><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"><title>-------------</title><link type="text/css" href="script/jquery-easyui-1.7.5/themes/icon.css" rel="stylesheet"><link type="text/css" href="script/jquery-easyui-1.7.5/themes/default/easyui.css" rel="stylesheet"><script type="text/javascript" src="script/jquery.min.js"></script><script type="text/javascript" src="script/jquery-easyui-1.7.5/jquery.easyui.min.js"></script><script type="text/javascript" src="script/jquery-easyui-1.7.5/locale/easyui-lang-zh_CN.js"></script><link type="text/css" href="http://netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css"rel="stylesheet"><link rel="stylesheet" type="text/css" href="css/style.css?t=1.1.24">
</head>
<body>
<nav class="side-menu"><div class="settings"><img class="head-icon" src="pic/head-icon-1.jpg" /></div><div class="menu-ul"><ul><li><a href="#"><i class="fa fa-home fa-lg"></i><span class="nav-text">Home</span></a></li><li><a href="panSearch.html"><i class="fa fa-cloud fa-lg"></i><span class="nav-text">网盘资源搜索</span></a></li><li><a href="#"><i class="fa fa-book fa-lg"></i><span class="nav-text">Book</span></a></li><li><a href="#"><i class="fa fa-envelope fa-lg"></i><span class="nav-text">Envelope</span></a></li><li><a href="#"><i class="fa fa-rocket fa-lg"></i><span class="nav-text">Rocket</span></a></li><li><a href="#"><i class="fa fa-code fa-lg"></i><span class="nav-text">Code</span></a></li><li><a href="#"><i class="fa fa-coffee fa-lg"></i><span class="nav-text">Coffee</span></a></li><li><a href="#"><i class="fa fa-compass fa-lg"></i><span class="nav-text">Compass</span></a></li><li><a href="#"><i class="fa fa-download fa-lg"></i><span class="nav-text">Download</span></a></li></ul></div>
</nav>
<div class="barrage-box"><div class="barrage-content"><!--<canvas id="canvas-1"></canvas>--><div class="barrage-road"><span class="barrage">测试弹幕.....<!--<input class="starButton" type="button" value="">--><img src="pic/star-active.png" class="star-img" alt=""><span class="star-num-text">0</span></span></div><div class="barrage-road"></div><div class="barrage-road"></div><div class="barrage-road"></div><div class="barrage-road"></div><div class="barrage-road"></div><div class="barrage-road"></div></div><div class="barrage-settings"><!--<input class="text-size" type="range" max="100" min="0">--><input class="text-preview" type="button" value="Hello World"><div class="range-slider"><div class="range-slider-fill"></div><div class="range-slider-handle"></div></div><div style="margin-left: 5px"><input type="radio"  name="colorSelected" value="black"><span style="color: black">黑色</span></div><div style="margin-left: 5px"><input type="radio"  name="colorSelected" value="red"><span style="color: red">红色</span></div><div style="margin-left: 5px"><input type="radio"  name="colorSelected" value="green"><span style="color: green">绿色</span></div><div style="margin-left: 5px"><input type="radio"  name="colorSelected" value="white" checked="checked"><span style="color: white">白色</span></div><div style="margin-left: 10px"><select class="speed-selector"><option value="12" name="speedNum">请选择车速</option><option value="12" name="speedNum">8</option><option value="11" name="speedNum">7</option><option value="10" name="speedNum">6</option><option value="9" name="speedNum">5</option><option value="8" name="speedNum">4</option><option value="7" name="speedNum">3</option><option value="6" name="speedNum">2</option><option value="5" name="speedNum">1</option></select></div></div><div class="comment-box"><input class="comment-input" type="text" placeholder="请输入待发送内容"></div><div class="button-area"><button class="send-button" onclick="addBarrageData()">发送弹幕</button></div></div>
<script type="text/javascript" src="script/index-2-pro.js?t=1.0.23"></script>
<ul class="right-menu-ul" style="display: none"><li class="menu-item">举报当前弹幕</li>
</ul>
</body>
</html>

html代码没什么好说的,连这个都看不懂的话,下面的基本也没戏了。

重头戏js源码

  • js这门语言的特点就是: 它的语义宽松,这也就导致了很多的js代码写出来显得杂乱无章,比如我这个。但是用熟练了以后,你就会发现它越用越上瘾。
&ensp;"use strict";var barrageContent = document.getElementsByClassName("barrage-content")[0];// var rect = barrageContent.getBoundingClientRect();/*** 弹道* @type {HTMLCollectionOf<Element>}*/var barrageRoad = document.getElementsByClassName("barrage-road");var rangeValue = 20;//字体大小 预设值var sliderHandle = document.getElementsByClassName("range-slider-handle")[0];//字体调节滑块var slider = document.getElementsByClassName("range-slider")[0];var sliderRect = slider.getBoundingClientRect();var sliderFill = document.getElementsByClassName("range-slider-fill")[0];//滑过区域var textPreview = document.getElementsByClassName("text-preview")[0];//预览字体区var content = document.getElementsByClassName("comment-input")[0];//弹幕输入框var speedNum = document.getElementsByClassName("speed-selector")[0];//弹幕速度var color = document.getElementsByName("colorSelected");//弹幕颜色选择器var colorSelected = "white";//被选择颜色 预设值var transitionEvent = whichTransitionEvent();//判断浏览器内核类型var index = 1;//数据库弹幕表分页页码var deviceType = goPAGE();var I = 0;//右键弹幕的idsliderHandle.style.left = "0px";/*** 测试用数据 数据格式,我下面的源码获取数据是用的我自己建的后台,如果你只是想要一个单机版的话,直接添加下面数组里的初始化数据就行,每个字段都要。* @type {*[]}*/var barrageData_2 = [{"barrageId": 1,"content": "第二条弹幕.............","color": "red","speed": 16,"textSize": 20,"road": 0,"starNum":0}];barrageData_2 = barrageData_2.concat();/*** 初始化操作自执行函数*/(function () {for (var i = 0; i < barrageData_2.length; i++) {barrageData_2[i]["road"] = i % barrageRoad.length;}console.log("screen-size", screen.width, screen.height);var that = this;var timer = setInterval(function (args) {if (barrageData_2.length <= 0) {console.log("barrageData:", barrageData_2);regularGetBarrage();return;}sendBarrage(barrageData_2[0]["barrageId"], barrageData_2[0]["content"], barrageData_2[0]["color"], barrageData_2[0]["speed"], barrageData_2[0]["textSize"], barrageData_2[0]["road"]);barrageData_2.splice(0, 1);}, 1000);})();window.onload = function (ev) {document.getElementsByClassName("menu-item")[0].onclick = report;//TODO}/*** 鼠标指定弹幕事件函数* @param event*/function onMouseIn(event) {var barrage = event.valueOf();var computeStyle = window.getComputedStyle(barrage), left = computeStyle.getPropertyValue("left");barrage.style.left = left;barrage.classList.remove("barrage-active");}/*** 鼠标离开弹幕事件函数* @param event*/function onMouseLeave(event) {var barrage = event.valueOf();barrage.classList.add("barrage-active");}/*** 弹幕点击事件* @param event*/function mouseClicked(event) {var barrage = event.valueOf();barrage.classList.add("barrage-clicked");barrage.childNodes[1].setAttribute("src", "/pic/star-active.png");barrage.childNodes[3].innerHTML = parseInt(barrage.childNodes[3].innerHTML) + 1;star(barrage);}/*** 弹幕发送处理函数* @param index* @param content 弹幕内容* @param colorSelected 选择的颜色* @param speedNum 弹幕速度* @param rangeValue 字体大小* @param roadNum 弹道*/function sendBarrage(barrageId, content, colorSelected, speedNum, rangeValue, roadNum) {var newBarrage = document.createElement("span");if (content === "") {return;}newBarrage.classList.add("barrage");newBarrage.style.setProperty("color", colorSelected);newBarrage.style.transitionDuration = speedNum + "s";newBarrage.style.webkitTransitionDuration = speedNum + "s";newBarrage.style.setProperty("font-size", rangeValue + "px");newBarrage.innerHTML = content + "<img src='pic/star.png' class='star-img' alt=''>" +"                    <span class='star-num-text'>"+barrageData_2[0]["starNum"]+"</span><input type='text' hidden value="+barrageId+">";newBarrage.onmouseenter = function (ev) {onMouseIn(this);};newBarrage.onmouseleave = function (ev) {onMouseLeave(this);};if (deviceType === "pc"){newBarrage.onclick = function (ev) {var ul = document.getElementsByClassName("right-menu-ul")[0];ul.style.display = "none";mouseClicked(this);};} else {newBarrage.ontouchend = function (ev) {var ul = document.getElementsByClassName("right-menu-ul")[0];ul.style.display = "none";mouseClicked(this);}}newBarrage.oncontextmenu = function (ev) {var ul = document.getElementsByClassName("right-menu-ul")[0];// console.log("contextmenu", ev.currentTarget.valueOf().childNodes);event.preventDefault();//TODO 奇迹的用法I = barrageId;ul.style.display = "block";ul.style.left = event.clientX + 10 + "px";ul.style.top = event.clientY + 10 + "px";};//TODO 备注,dom初始化的时机barrageRoad[roadNum].appendChild(newBarrage);setTimeout(function () {newBarrage.classList.add("barrage-active");},50);newBarrage.addEventListener(transitionEvent, function (evt) {// console.log(this);this.parentNode.removeChild(this);// barrageData_2.splice(0, 1);console.log("----------------------transitionEnd事件触发一次---------------");});}/*** 滑块鼠标按下事件函数* @param event*/if (deviceType === "pc"){sliderHandle.onmousedown = function (event) {var that = this;var oldX = event.clientX;var left = parseInt(that.style.left);document.onmousemove = function (ev) {var x = ev.clientX - oldX;that.style.left = left + x + "px";rangeValue = Math.ceil((parseInt(that.style.left) / sliderRect.width) * 40) + 1;if (parseInt(that.style.left) < 0) {that.style.left = "0";rangeValue = 10;}if (parseInt(that.style.left) > sliderRect.width) {that.style.left = sliderRect.width - 10 + "px";rangeValue = 50;}sliderFill.style.width = that.style.left;textPreview.style.fontSize = rangeValue + "px";console.log("rangeValue: ", rangeValue);};document.onmouseup = function (ev) {document.onmouseup = null;document.onmousemove = null;}};} else {sliderHandle.ontouchstart = function (event) {var that = this;var oldX = event.clientX;var left = parseInt(that.style.left);document.ontouchmove = function (ev) {var x = ev.clientX - oldX;that.style.left = left + x + "px";console.log("left", that.style.left);rangeValue = Math.ceil((parseInt(that.style.left) / sliderRect.width) * 40) + 1;if (parseInt(that.style.left) < 0) {that.style.left = "0";rangeValue = 10;}if (parseInt(that.style.left) > sliderRect.width) {that.style.left = sliderRect.width - 10 + "px";rangeValue = 50;}sliderFill.style.width = that.style.left;textPreview.style.fontSize = rangeValue + "px";console.log("rangeValue: ", rangeValue);};document.ontouchend = function (ev) {document.ontouchend = null;document.ontouchmove = null;}};}/*** 发送弹幕按钮点击事件响应函数*/function addBarrageData() {var item = {"content": "","color": "red","speed": 16,"textSize": 20,"road": 0,"starNum": 0};var content_1 = content.value;item["speed"] = speedNum.value;item["textSize"] = rangeValue;item["road"] = Math.floor(Math.random() * barrageRoad.length);content_1 = contentFilter();item["content"] = content_1;console.log("content: ", content_1);for (var i = 0;i<color.length;i++) {if (color[i].checked) {item["color"] = color[i].value;break;}}console.log("添加事件触发");barrageData_2.push(item);console.log("barrageData :", barrageData_2.length);// content.value = "";saveBarrage();}/*** 右键菜单处理函数*/// document.oncontextmenu = function (event) {////     var ul = document.getElementsByClassName("right-menu-ul")[0];////     event.preventDefault();////     ul.style.display = "block";//     ul.style.left = event.clientX +"px";//     ul.style.top = event.clientY + "px";////     // return false;// }/*** 全局点击事件重写* @param e*/document.onclick = function (e) {var ul = document.getElementsByClassName("right-menu-ul")[0];ul.style.display = "none";};/*** 举报弹幕响应函数* @param id*/function report() {// $.ajax({//     url: "/barrage/save",//     type: "post",//     dataType: "json",//     data: {//         "barrageSenderId": 1,//         "content": contentFilter(),//         "speed": speedNum.value,//         "color": function () {//             for (var i = 0; i < color.length; i++) {//                 if (color[i].checked) {//                     return color[i].value;//                 }//             }//         },//         "textSize": rangeValue,//         "road": Math.floor(Math.random() * barrageRoad.length)//     },//     complete: function (ev) {////     }// });$.messager.show({title: "提示消息",msg: "举报成功:"+ I,timeout: 5000});}/*** 发送的弹幕发送至服务器*/function saveBarrage() {$.ajax({url: "/barrage/save",type: "post",dataType: "json",data: {"barrageSenderId": 1,"content": contentFilter(),"speed":speedNum.value,"color":function () {for (var i = 0;i<color.length;i++){if (color[i].checked){return color[i].value;}}},"textSize": rangeValue,"road": Math.floor(Math.random() * barrageRoad.length)},complete:function (ev) {// console.log(ev);content.value = "";if (ev.status === 200){console.log(ev.responseText);}else{console.log("error", ev.responseText);}}});}/*** 后台弹幕数据拉取函数*/function regularGetBarrage() {$.ajax({url: "/barrage/getData?index=" + index,type: "get",dataType: "json",complete: function (ev) {if (ev.status == 200) {// console.log("regular", ev.responseText);var data = JSON.parse(ev.responseText);barrageData_2 = barrageData_2.concat(data["resultData"]["barrageInfoPage"]["content"]);console.log("regular", barrageData_2);index++;// console.log(data["resultData"]["barrageInfoPage"]["content"]);} else {console.log("error", ev.responseText);}}});}/*** 弹幕点赞处理函数* @param option*/function star(option) {var barrageId = option.childNodes[4].value;// console.log("star", option.childNodes);$.ajax({type: "get",dataType: "json",url: "/barrage/star?barrageId=" + barrageId,complete: function (ev) {if (ev.status == 200) {$.messager.show({title: "系统提示",msg: "点赞成功",timeout: 3000});console.log(ev.responseText);}}});}/*** 浏览器内核判断函数* @returns {*}*/function whichTransitionEvent() {var t;var el = document.createElement('fakeelement');var transitions = {'transition': 'transitionend','OTransition': 'oTransitionEnd','MozTransition': 'transitionend','WebkitTransition': 'webkitTransitionEnd'}for (t in transitions) {if (el.style[t] !== undefined) {return transitions[t];}}}/*** 输入内容过滤*/function contentFilter() {var content_1 = content.value;content_1 = content_1.replace(/<.*?>.*?<\/.*?>/g, '[非法字段]');content_1 = content_1.replace(/<img/g, '[非法字段]');content_1 = content_1.replace(/<script/g, '[非法字段]');return content_1;}/***判断是否为移动端*/function goPAGE() {if ((navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i))) {/*window.location.href="你的手机版地址";*/// deviceType = "mobile";return "mobile";}else {/*window.location.href="你的电脑版地址";    */// deviceType = "pc";return "pc";}}

css代码

这个我就放github上算了,不然篇幅实在是太长了。
css代码地址

下一篇文章预告

实现一个基于springboot框架的注解方法跟踪小框架

基于原生js实现主流弹幕的所有功能相关推荐

  1. 基于原生js的图片延迟加载

    当页面图片比较多的时候,我们通常会做一个延迟加载,避免页面打开时一下子的请求数太多,加载过慢影响用户体验. 如果项目用了jquery框架,则可以直接用 jquery.lazyload.可在jquery ...

  2. 基于原生JS项目使用Vue3 + Surely Vue Table组件

    Js & Surely Vue Table 本文主要说明,基于原生JS项目如何使用Surely Vue Table组件. Surely Vue Surely Vue Table 是 Ant D ...

  3. 原生html中modal,基于原生JS封装的Modal对话框插件

    基于原生JS封装Modal对话框插件 原生JS封装Modal对话框插件,个人用来学习原理与思想,只有简单的基本框架的实现,可在此基础上添加更多配置项 API配置 //基本语法 let modal = ...

  4. 基于原生js雷霆战机

    本篇文章是基于原生js使用函数制作 游戏预览 html部分 <!DOCTYPE html> <html lang="en"><head>< ...

  5. linux脚本石英钟,原生JS实现的简单小钟表功能示例

    本文实例讲述了原生JS实现的简单小钟表功能.分享给大家供大家参考,具体如下: 先来看看运行效果: 完整代码: www.jb51.net 钟表 body { background-color:#00A2 ...

  6. 原生JS实现文件自定义位置盖章功能并导出PDF

    原生JS实现文件自定义位置盖章功能并导出PDF 实现原理 在需要签章的文件上面创建一个div,可以通过移动这个div来确定签章位置,然后在通过获取这个位置把章子替换到这,并导出PDF,可以多次盖章! ...

  7. 基于原生JS写的异形轮播图--效果如网易云、QQ音乐播放器中轮播图

    css部分 <style>#box{height:500px;width:1000px;position: relative;margin:100px auto;overflow: hid ...

  8. 使用原生js实现按钮的全选功能,简单清晰

    1.相信有很多朋友在学习前端原生JS的时候,都有遇到过实现全选按钮的需求吧.废话不多说直接上代码吧! <!DOCTYPE html> <html lang="en" ...

  9. 原生js完成一个简单的抽奖功能

    文章目录 前言 实现过程 1.前期准备 2.CSS美化 3.使用 js 完成抽奖功能 3.1 随机数模块 3.2 随机奖品 3.3 开始抽奖 3.4 点击开始抽奖 完整代码 HTML部分 CSS部分 ...

最新文章

  1. 【组队学习】【28期】基于transformers的自然语言处理(NLP)入门
  2. 腾讯 JDK 11 正式开源,高性能、太牛逼啦!
  3. 2011年软考网络工程师全面复习资料汇总
  4. 小程序input框letter-spacing失效,处理方法
  5. POI实现读写内容到word中
  6. [转] 初识Firebug(HTML查看和编辑、Javascript控制台、网络状况监视器)
  7. H5与企业微信jssdk集成
  8. Java SpringMvc+hibernate架构中,调用Oracle中的sp,传递数组参数
  9. 在mysql中删除表中字段_MySQL中的表中增加删除字段
  10. win7怎么把计算机放到桌面6,win7系统如何设置更改桌面图标?
  11. java对象比较 hashcode_java基础----比较对象 hashcode 与 equals 与 ==
  12. linux正则表达式_Linux 中几个正则表达式的用法
  13. C# 匿名对象的写法
  14. 理解GetHashCode()的缺陷
  15. django oscar_赢得奥斯卡奖之后会发生什么
  16. 【企业开源】小米开源:站在巨人肩膀上的创新
  17. win7设置文件夹共享 win7共享文件夹
  18. 手机卫星定位系统_为什么手机支持北斗导航,却不知道如何开启?
  19. comsume(comsumer怎么读)
  20. 【Matlab】牛顿迭代法实现

热门文章

  1. 7-5合数(20分)备忘录和计数简化查找
  2. Color Coherence Vector
  3. Jquery中的parent()与parents()取父元素的区别
  4. disabled=disabled注意事项
  5. 魔兽最多人的服务器,现在魔兽世界前三名人最多最平衡的服务器是哪3个?
  6. openshift mysql_openshift 部署主备mysql
  7. 小型企业网组网 同时实现高可靠性
  8. 网络布线 水晶头线序
  9. 电力运维云平台在配电室的研究及应用
  10. 手表什么牌子好_什么手表品牌性价比高