原生Canvas循环滚动弹幕(现金红包活动带头像弹幕)
效果
gif有些糊,可以 在线预览
实现关键点
requestAnimationFrame循环帧;- 绘制单条弹幕,画框子 -> 画头像 -> 写黑色的字 -> 写红色的字,
measureText获取文字宽度; - 防止弹幕重叠,分行且记录当前行是否可插入,弹幕随机行插入;
- 弹幕滚出屏幕外时,移除此条弹幕;
- 循环发射弹幕的实现。
代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>弹幕(头像,文字)</title>
</head>
<body>
<canvas id="canvas" style="background: #333;"></canvas>
</body>
<script>
// 圆角矩形
CanvasRenderingContext2D.prototype.roundRect = function (left, top, width, height, r) {
const pi = Math.PI;
this.beginPath();
this.arc(left + r, top + r, r, -pi, -pi / 2);
this.arc(left + width - r, top + r, r, -pi / 2, 0);
this.arc(left + width - r, top + height - r, r, 0, pi / 2);
this.arc(left + r, top + height - r, r, pi / 2, pi);
this.closePath();
}
class Barrage {
constructor(id) {
this.scale = 2; // 缩放倍数,1会糊
this.canvas = document.getElementById(id);
this.canvas.width = this.w = document.body.offsetWidth * this.scale;
this.canvas.height = this.h = 220 * this.scale;
this.canvas.style.width = this.w / this.scale + 'px';
this.ctx = this.canvas.getContext('2d');
this.style = { // 弹幕样式
height: 27 * this.scale, // 弹幕高度
fontSize: 14 * this.scale, // 字体大小
marginBottom: 4 * this.scale, // 弹幕 margin-bottom
paddingX: 8 * this.scale, // 弹幕 padding x
avatarWidth: 18 * this.scale, // 头像宽度
}
this.ctx.font = this.style.fontSize + 'px PingFangSC-Regular';
this.barrageList = []; // 弹幕列表
this.rowStatusList = []; // 记录每行是否可插入,防止重叠。 行号为可插入 false为不可插入
let rowLength = Math.floor(this.h / (this.style.height + this.style.marginBottom));
for (var i = 0; i < rowLength; i++) {
this.rowStatusList.push(i)
}
}
shoot(value) {
const { height, avatarWidth, fontSize, marginBottom, paddingX } = this.style;
const { img, t1, t2 } = value;
let row = this.getRow();
let color = this.getColor();
let offset = this.getOffset();
let w_0 = paddingX; // 头像开始位置
let w_1 = w_0 + avatarWidth + 8; // t1文字开始位置
let w_2 = w_1 + Math.ceil(this.ctx.measureText(t1).width) + 8; // t2文字开始位置
let w_3 = w_2 + Math.ceil(this.ctx.measureText(t2).width) + paddingX; // 弹幕总长度
let barrage = {
value,
color,
row,
top: row * (height + marginBottom),
left: this.w,
offset,
width: [w_0, w_1, w_2, w_3],
}
this.barrageList.push(barrage);
}
draw() {
if (!!this.barrageList.length) {
this.ctx.clearRect(0, 0, this.w, this.h);
for (let i = 0, barrage; barrage = this.barrageList[i]; i++) {
// 弹幕滚出屏幕,从数组中移除
if (barrage.left + barrage.width[3] <= 0) {
this.barrageList.splice(i, 1);
i--;
continue;
}
// 弹幕完全滚入屏幕,当前行可插入
if (!barrage.rowFlag) {
if ((barrage.left + barrage.width[3]) < this.w) { //
this.rowStatusList[barrage.row] = barrage.row;
barrage.rowFlag = true;
}
}
barrage.left -= barrage.offset;
this.drawBarrage(barrage);
}
}
requestAnimationFrame(this.draw.bind(this));
}
drawBarrage(barrage) {
const { height, avatarWidth, fontSize, marginBottom, paddingX } = this.style;
const {
value: { img, t1, t2 },
color,
row,
left,
top,
offset,
width,
} = barrage;
// 画框子
this.ctx.roundRect(left, top, width[3], height, height / 2)
this.ctx.fillStyle = 'rgba(255,255,255,0.50)';
this.ctx.fill();
// 画头像
this.ctx.drawImage(img, 0, 0, img.width, img.height, left + width[0], top + (height - avatarWidth) / 2, avatarWidth, avatarWidth);
// 画黑色的字
this.ctx.fillStyle = color;
this.ctx.fillText(t1, left + width[1], top + fontSize + 8);
// 画红色的字
this.ctx.fillStyle = '#F24949';
this.ctx.fillText(t2, left + width[2], top + fontSize + 8);
}
getRow() {
let emptyRowList = this.rowStatusList.filter(d => /\d/.test(d)); // 找出可插入行
let row = emptyRowList[Math.floor(Math.random() * emptyRowList.length)]; // 随机选一行
this.rowStatusList[row] = false;
return row;
}
haveEmptyRow() {
let emptyRowList = this.rowStatusList.filter(d => /\d/.test(d)); // 找出可插入行
return !!emptyRowList.length;
}
getColor() {
return '#000000';
}
getOffset() {
return 1 * this.scale;
}
}
var list = [
{
avatar: 'https://image.duliday.com/living-cost/20200303/2a94df636b91ad15bbbb4408e2f285e4164115?roundPic/radius/66',
t1: '张**三 给 李**四',
t2: '红包',
},
{
avatar: 'https://image.duliday.com/living-cost/20200317/4cd9f827d439f7a1227501f9b09cd1e8622417?roundPic/radius/66',
t1: '王**五 给 赵**六',
t2: '红包',
}
]
// 循环插入发射弹幕
var index = 0;
var shootBarrage = function (list) {
setTimeout(function () {
if (barrage.haveEmptyRow()) {
var data = list[index++] || list[(index = 0) || index++];
var img = new Image();
img.setAttribute("crossOrigin", 'anonymous');
img.onload = function () {
barrage.shoot({
img,
t1: data.t1,
t2: data.t2,
});
}
img.src = data.avatar;
}
shootBarrage(list);
}, 1000)
}
var barrage = new Barrage('canvas');
barrage.draw();
shootBarrage(list)
</script>
</html>
原生Canvas循环滚动弹幕(现金红包活动带头像弹幕)的更多相关文章
- marquee 实现首尾相连循环滚动效果
<marquee></marquee>可以实现多种滚动效果,无需js控制.使用marquee标签不仅可以滚动文字,也可以滚动图片,表格等 marquee标签不是HTML3.2 ...
- Android实现真正的ViewPager【平滑过渡】+【循环滚动】!!!顺带还有【末页跳转】。
实现真正的ViewPager[平滑过渡]+[循环滚动]!!!顺带还有[末页跳转]. 首先呢, 我要对网上常见的3种ViewPager的循环滚动方法做个概述.急需看真正实现方法的同志请选择性忽略下面这一 ...
- 基于html5可拖拽图片循环滚动切换
分享一款基于html5可拖拽图片循环滚动切换.这是一款支持手机端拖拽切换的网站图片循环滚动特效.效果图如下: 在线预览 源码下载 实现的代码. html代码: <div id="s ...
- 【转】Android循环滚动广告条的完美实现,封装方便,平滑过渡,从网络加载图片,点击广告进入对应网址
Android循环滚动广告条的完美实现,封装方便,平滑过渡,从网络加载图片,点击广告进入对应网址 关注finddreams,一起分享,一起进步: http://blog.csdn.net/finddr ...
- jQuery 实现列表自动滚动循环滚动显示新闻通知
需求 页面中一个小区域循环滚动展示通知(公告.新闻.活动.图片等),并且鼠标hover时停止滚动并提示,鼠标离开后,继续滚动. 效果图 https://www.iguopin.com/index.ph ...
- IOS实现自动循环滚动广告--ScrollView的优化和封装
一.问题分析 在许多App中,我们都会见到循环滚动的视图,比如广告,其实想实现这个功能并不难,用ScrollView就可以轻松完成,但是在制作的过程中还存在几个小问题,如果能够正确的处理好这些小问题, ...
- Jquery制作--循环滚动列表
自己模仿JQ插件的写法写了一个循环滚动列表插件,支持自定义上.下.左.右四个方向,支持平滑滚动或者间断滚动两种方式,都是通过参数设置.JQ里面有些重复的地方,暂时没想到更好的方法去精简.不过效果还是可 ...
- Expression Blend4经验分享:文字公告无缝循环滚动效果
这次分享一个类似新闻公告板的无缝循环滚动效果,相信很多项目都会应用到这个效果.之前我也百度了一下,网上的一些Silverlight的文字或图片滚动效果,都是一次性滚动的,如果要做到无缝循环滚动,多数要 ...
- ul 仿 table 循环滚动
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
随机推荐
- spring学习笔记四:AOP
AOP(Aspect Orient Programming),面向切面编程,是对面向对象编程OOP的一种补充 面向对象编程使用静态角度考虑程序的结构,而面向切面编程是从动态角度考虑程序运行过程 AOP ...
- 使用zxing生成和解析二维码
二维码: 是用某种特定的几何图形按一定规律在平面(二维方向上)分布的黑白相间的图形记录数据符号信息的: 在代码编制上巧妙的利用构成计算机内部逻辑基础的0和1比特流的概念,使用若干个与二进制相对应的几何 ...
- MAYA 卸载工具,完美彻底卸载清除干净maya各种残留注册表和文件
是不是遇到MAYA/CAD/3DSMAX/INVENTOR安装失败?AUTODESK系列软件着实令人头疼,MAYA/CAD/3DSMAX/INVENTOR安装失败之后不能完全卸载!!!(比如maya, ...
- Thymeleaf模板笔记
1.常用标签: 使用thymeleaf模板,首要在html中引入: <html xmlns:th="http://www.thymeleaf.org"> 引入css.j ...
- PHP生成word文档的三种实现方式
PHP生成word原理 利用windows下面的 com组件 利用PHP将内容写入doc文件之中 具体实现: 利用windows下面的 com组件 原理:com作为PHP的一个扩展类,安装过offic ...
- unittest(22)- p2p项目实战(8)-test_class_auto_incre
# 8.test_class_auto_incre # 使用ddt import requests import unittest from p2p_project_7.tools.http_requ ...
- Selenium2自动化——初体验
一.Windows下的环境搭建 1.安装Python 访问Python官网:https://www.python.org/ 2.安装setuptools与pip setuptools是Python e ...
- Janet Wu price
上次也是第一次参加百公里是2012的时候,那年的主题是一路有你,和一群同事从深圳湾走到福田,最后累了就回家了,那晚应该是睡得很好吧. 今年是提前报名了,虽然还不确定是否参加.因为说实话,我不喜欢拥堵的 ...
- Opengl-法线贴图(用来细化表面的表现表现的凹凸)
我们通过这张图可以看出来,使用了法线贴图的物体表面更有细节更逼真,其实这就是发现贴图的作用,没什么钻牛角尖的. 其实表面没有凹凸的情况是因为我们把表面一直按照平整来做的,要想突出这个表面的凹凸就要用到 ...
- PS4游戏将登陆PC:一曲属于主机的悲歌
曾经,红白机.PS游戏机等成为一代人难以磨灭的记忆.而随后的索尼PS3.微软Xbox 360.任天堂Wii U等,也称霸了次时代主机时代,成为家庭娱乐的中心.但面对着依托于PC和智能移动终端 ...