上次项目我们分的是图片网站,原本想的是加一个视频播放的功能嘞,但是奈何我们组就一个后端,三个前端,视频播放并不是我们网站的必要功能,所以就没去实现,但是项目结束了,闲着没事就看了一下弹幕的实现方式,在b站搜到了一个使用canvas实现弹幕的方法,看到人家的代码才知道自己写的代码有多low,人家写的代码是真的将功能分离,后期的可维护性和拓展性大大加强了。这里就来分享一下代码。
html并没有写过多的样式,大概是这样的,video用到的是原生的东西

html:

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>canvas实现弹幕</title><style>.content {width: 900px;margin: 60px auto;border: 1px solid red;position: relative;}#canvas {position: absolute;top: 0;right: 0;/* z-index: 10; */}.control {text-align: center;}input {vertical-align: middle;}</style>
</head><body><div class=""><div class="content"><canvas id="canvas"></canvas><video id="video" src="视频地址" height="auto" controls width="900"></video></div><div class="control">//  弹幕内容<input id="ipt" placeholder="请输入弹幕" />//弹幕的颜色选择<input id="colorIpt" value="#cccccc" type="color" />//弹幕的字体大小选择<input id="fontIpt" min="20" max="60" value="26" type="range" /><button id="submit">点击发送</button></div></div></body></html>

js(这里我和那个视频上的一样,把实现弹幕的主要功能和调用以及生成假数据的函数分开)

let data = getDate(20)
// 获取数据的函数
function getDate(len) {let data = []/* 一个弹幕数据中需要有弹幕的具体值,弹幕的字体大小,弹幕的颜色,它出现在视频中的时间,以及他的速度*/for (let i = 0; i < len; i++) {data.push({value: `第${i}条弹幕`,fontSize: 26,color: 'red',time: i * 2,speed: 1})}return data
}
const canvas = document.getElementById('canvas')
const video = document.getElementById('video')
/* Barrages是实现的具体方法,需要传canvas(绘制弹幕的canvas),video(播放视频),数据
*/
barage = new Barrages({canvas,video,data
})
// 播放视频,弹幕播放
video.addEventListener('play', () => {barage.isPlay = truebarage.render()})
// 视频暂停,弹幕暂停
video.addEventListener('pause', () => {barage.pause()
})
/* 声名一个对象,里边包含着弹幕的字体和颜色(这里给其一个默认值),之后修改颜色和字体大小的时候会改变这个对象的值
*/
let option = {color: '#cccccc',fontSize: 26
}
// 点击发送,插入数据
submit.addEventListener('click', () => {option.time = video.currentTimebarage.setBarrage(option)ipt.value = ''
})
//弹幕内容
ipt.addEventListener('change', (event) => {option.value = event.target.value;
})
//弹幕颜色
colorIpt.addEventListener('change', (event) => {option.color = event.target.value;
})
//弹幕字体大小
fontIpt.addEventListener('change', (event) => {option.fontSize = +event.target.value;
})

具体实现函数

// 渲染弹幕数据的具体函数
class Barrage {constructor(options, ctx) {this.ctx = ctx// 设置是否初始化this.isInit = false// 判断是否还需要绘制this.flag = true// 归并到this上去Object.assign(this, options)}// 初始化init() {this.color = this.color || this.ctx.colorthis.fontSize = this.fontSize || this.ctx.fontSizethis.speed = this.speed || this.ctx.speed// 获取宽度this.width = this.getWidth()// 获取宽度this.x = this.ctx.canvas.width// 获取高度this.y = Math.random() * this.ctx.canvas.height// 判断是否大于canvas的高度减去字体大小(如果大于就等于这个值)if (this.y > this.ctx.canvas.height - this.fontSize) {this.y = this.ctx.canvas.height - this.fontSize}if (this.y < this.fontSize) {this.y = this.fontSize}/* 上述的两个判断是为了避免弹幕出现在canvas外边(别忘了把字体大小算进去)*/}// 获取宽度(通过把弹幕内容放到html元素中来获弹幕的具体宽度)getWidth() {// 创建一个标签let span = document.createElement('span')span.innerText = this.valuespan.style.font = `${this.fontSize}px "Miscrosoft YaHei"`span.style.position = 'absolute'span.style.zIndex = -1//插入到body里边(方便获取宽度)document.body.appendChild(span)let width = span.clientWidth// 删除document.body.removeChild(span)return width}//将弹幕渲染到canvas上render() {//指定字体大小this.ctx.context.font = `${this.fontSize}px "Miscrosoft YaHei"`// 指定字体颜色this.ctx.context.fillStyle = this.color// 指定绘制内容和绘制位置this.ctx.context.fillText(this.value, this.x, this.y)}
}
// 整体的流程控制
class Barrages {constructor(options) {//判断是否有canvas和videoif (!options.canvas || !options.video) {return}// 声明一个默认值(避免没有传值时的错误渲染)const defaultOptions = {fontSize: 26,color: 'green',data: [],speed: 1}// 把这些东西都归并到this上Object.assign(this, defaultOptions, options)// 初始化canvasthis.canvas.width = this.video.clientWidththis.canvas.height = this.video.clientHeight// 取出canvas上下文(绘制canvas需要获取它的上下文)this.context = this.canvas.getContext('2d')// 将每个数据实例化(箭头函数的this)this.barrages = this.data.map((item) => new Barrage(item, this))// 是否需要播放弹幕this.isPlay = true}// 清除画布方法clear() {this.context.clearRect(0, 0, this.canvas.width, this.canvas.height)}//将新增的数据添加到数组中setBarrage(data) {this.data.push(data)this.barrages.push(new Barrage(data, this))}// 渲染方法render() {// 清空画布this.clear()// 渲染新的画布this.renderBarrages()//判断是否需要绘制弹幕if (this.isPlay) {// 循环执行渲染函数(需要矫正一下this的值)requestAnimationFrame(this.render.bind(this))}}// 绘制弹幕方法renderBarrages() {// 获取当前视频的时间const time = this.video.currentTime// 遍历数据this.barrages.forEach(item => {// 遍历所有弹幕数据,通过判断时间来确定其是否应该显示if (item.time <= time && item.flag) {// 判断是否应该初始化if (!item.isInit) {item.init()item.isInit = true}/* 超出canvas不需要再绘制了原作者这里写的时this,是不对的,因为这里是箭头函数,this的指向的并不是每条弹幕数据的实例,如果是普通函数this的指向则是window,所以这里改成item(弹幕数据的实例对象)*/if (item.x < -item.width) {item.flag = false}//改变它的横轴位置item.x = item.x - item.speed// 调用其render方法item.render()}})}// 暂停方法pause() {this.isPlay = falseconsole.log('pause')}
}

利用canvas实现的本质就是每次渲染的时候都去初始化canvas,然后根据数据实例化对象的x和y坐标,去绘制到canvas上,虽然我们看着像连续的,但是其背后是重新渲染的。

上边的便是我分享的代码,虽然有点代码搬运工的意思吧,但是看完人家写的代码,还是感觉自己写的太差了,之后要多培养这种将功能分离的思想,和将数据对象化的思想。下周继续努力吧!!!
这段代码的作者b站视频连接

前端实现弹幕代码分享相关推荐

  1. 前端页面与form表单提交:代码分享

    今天分享下"前端页面与form表单提交:代码分享"这篇文章,文中根据实例编码详细介绍,或许对大家的编程之路有着一定的参考空间与使用价值,需要的朋友接下来跟着云南仟龙Mark一起学习 ...

  2. 前端常用得CSS代码分享

    本文首发于公众号:执行上下文,同步更新个人博客:执行上下文,转载请署名.代码不断更新中!! 前提 2019年11月的最后一篇文章来拉,在日常开发中高频使用的CSS代码分享给大家,其中可能有很多大家经常 ...

  3. .net之工作流工程展示及代码分享(二)工作流引擎

    在介绍完表单类的时候,接下来介绍工作流引擎,主要由四个类组成,分别是流程.流程步骤.流程实例.流程步骤实例类. 流程类: 1 [Serializable] 2 public class Flow 3 ...

  4. WordPress程序/functions.php收集整理的各种自定义优化代码分享

    <!--以下代码为主题目录下functions.php文件内代码!-->// 彻底关闭自动更新 add_filter('automatic_updater_disabled', '__re ...

  5. 【纯干货】微信支付接入攻略—JAVA代码分享

    [纯干货]微信支付接入攻略-JAVA代码分享 [声明]本文作者是Gekec网站开发总监Arne Chen.Arne在实际接入微信支付时遇到诸多问题并且逐一解决的过程中总结了不少实战经验.文中引用代码是 ...

  6. arima模型matlab代码_PSTR面板平滑转换模型简介(附Matlab代码分享)

    写论文的时候用到的~相关的资料太少了,做一些简单内容和资料的分享.(PSTR模型的Matlab代码分享在最后)本文主要为简单理论和粗暴实操~ 有用的话可以点个赞哟(知乎小白卑微求赞) 嘻嘻下面进入正题 ...

  7. python登录代码思路_用python登录Dr.com思路以及代码分享

    用python登录Dr.com思路以及代码分享 发布于 2014-08-28 22:31:52 | 192 次阅读 | 评论: 0 | 来源: 网友投递 Python编程语言Python 是一种面向对 ...

  8. python turtle画画 30排以内_Python竟能画这么漂亮的花,帅呆了(代码分享)

    阅读本文大概需要3分钟 关于函数和模块讲了这么久,我一直想用一个好玩有趣的小例子来总结一下,同时也作为实战练习一下. 趣味编程其实是最好的学习途径,回想十几年前我刚毕业的时候,第一份工作就给手机上写a ...

  9. Airlaunch 快捷设置代码分享

    Airlaunch 快捷设置代码分享 设置:prefs:root=SETTING 蜂窝网络:prefs:root=MOBILE_DATA_SETTINGS_ID WIFI:prefs:root=WIF ...

最新文章

  1. 关于学习Python的一点学习总结(2->列表)
  2. 026-请问你怎么测试网络协议
  3. flutter_web 实战之文章列表与详情
  4. Tableau实战系列数据连接及数据准备
  5. 微软放弃IE浏览器 应尽快完成国产化替代
  6. CF1063B Labyrinth
  7. JavaScript 监听手机端的touch滑动事件(滑动手势)
  8. git 相同commit_Git 合并多次 commit 、 删除某次 commit
  9. java静态引用_Java开发中静态方法引用和实例方法引用案例详细讲解
  10. 浏览器处理 前台传递的+时出现问题
  11. amd显卡风扇调节_显卡风扇转速调节
  12. java垃圾回收文件分析
  13. darkest dark theme 插件对应eclipse各版本安装方法
  14. Boom 3D环绕音效软件免费安装使用教程
  15. Lua程序设计随笔(5)
  16. QT状态栏(statusbar)用法
  17. 爬虫python代码网易云_爬虫实战(二) 用Python爬取网易云歌单
  18. 2022年舞台造雪机市场前景分析及研究报告
  19. java文件损坏_用java下载文件 - 文件损坏
  20. vue的v-model双向数据绑定原理

热门文章

  1. 千言实体链指赛事登顶,冠军团队经验独家分享
  2. 常见glb格式文件模型gltf格式文件模型问题下载制作,以及解决处理办法
  3. 测试用例设计方法2——边界值
  4. php_imagick.dll for php 5.2.8 win ext
  5. 在cmd下批量实现图片后缀重命名
  6. vivo手机便签里的东西都没有了
  7. NPM安装asar,打包,解压,查看asar文件
  8. 【手游】有杀气童话 美术资源加密分析
  9. uni.showModal用法
  10. php 计算当前网速,php 测试网速 - qiuzhizhe的个人空间 - OSCHINA - 中文开源技术交流社区...