基于原生js实现主流弹幕的所有功能
原生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代码写出来显得杂乱无章,比如我这个。但是用熟练了以后,你就会发现它越用越上瘾。
 "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实现主流弹幕的所有功能相关推荐
- 基于原生js的图片延迟加载
当页面图片比较多的时候,我们通常会做一个延迟加载,避免页面打开时一下子的请求数太多,加载过慢影响用户体验. 如果项目用了jquery框架,则可以直接用 jquery.lazyload.可在jquery ...
- 基于原生JS项目使用Vue3 + Surely Vue Table组件
Js & Surely Vue Table 本文主要说明,基于原生JS项目如何使用Surely Vue Table组件. Surely Vue Surely Vue Table 是 Ant D ...
- 原生html中modal,基于原生JS封装的Modal对话框插件
基于原生JS封装Modal对话框插件 原生JS封装Modal对话框插件,个人用来学习原理与思想,只有简单的基本框架的实现,可在此基础上添加更多配置项 API配置 //基本语法 let modal = ...
- 基于原生js雷霆战机
本篇文章是基于原生js使用函数制作 游戏预览 html部分 <!DOCTYPE html> <html lang="en"><head>< ...
- linux脚本石英钟,原生JS实现的简单小钟表功能示例
本文实例讲述了原生JS实现的简单小钟表功能.分享给大家供大家参考,具体如下: 先来看看运行效果: 完整代码: www.jb51.net 钟表 body { background-color:#00A2 ...
- 原生JS实现文件自定义位置盖章功能并导出PDF
原生JS实现文件自定义位置盖章功能并导出PDF 实现原理 在需要签章的文件上面创建一个div,可以通过移动这个div来确定签章位置,然后在通过获取这个位置把章子替换到这,并导出PDF,可以多次盖章! ...
- 基于原生JS写的异形轮播图--效果如网易云、QQ音乐播放器中轮播图
css部分 <style>#box{height:500px;width:1000px;position: relative;margin:100px auto;overflow: hid ...
- 使用原生js实现按钮的全选功能,简单清晰
1.相信有很多朋友在学习前端原生JS的时候,都有遇到过实现全选按钮的需求吧.废话不多说直接上代码吧! <!DOCTYPE html> <html lang="en" ...
- 原生js完成一个简单的抽奖功能
文章目录 前言 实现过程 1.前期准备 2.CSS美化 3.使用 js 完成抽奖功能 3.1 随机数模块 3.2 随机奖品 3.3 开始抽奖 3.4 点击开始抽奖 完整代码 HTML部分 CSS部分 ...
最新文章
- 【组队学习】【28期】基于transformers的自然语言处理(NLP)入门
- 腾讯 JDK 11 正式开源,高性能、太牛逼啦!
- 2011年软考网络工程师全面复习资料汇总
- 小程序input框letter-spacing失效,处理方法
- POI实现读写内容到word中
- [转] 初识Firebug(HTML查看和编辑、Javascript控制台、网络状况监视器)
- H5与企业微信jssdk集成
- Java SpringMvc+hibernate架构中,调用Oracle中的sp,传递数组参数
- 在mysql中删除表中字段_MySQL中的表中增加删除字段
- win7怎么把计算机放到桌面6,win7系统如何设置更改桌面图标?
- java对象比较 hashcode_java基础----比较对象 hashcode 与 equals 与 ==
- linux正则表达式_Linux 中几个正则表达式的用法
- C# 匿名对象的写法
- 理解GetHashCode()的缺陷
- django oscar_赢得奥斯卡奖之后会发生什么
- 【企业开源】小米开源:站在巨人肩膀上的创新
- win7设置文件夹共享 win7共享文件夹
- 手机卫星定位系统_为什么手机支持北斗导航,却不知道如何开启?
- comsume(comsumer怎么读)
- 【Matlab】牛顿迭代法实现