效果

gif有些糊,可以 在线预览

实现关键点

requestAnimationFrame 循环帧;

绘制单条弹幕,画框子 -> 画头像 -> 写黑色的字 -> 写红色的字, measureText获取文字宽度;

防止弹幕重叠,分行且记录当前行是否可插入,弹幕随机行插入;

弹幕滚出屏幕外时,移除此条弹幕;

循环发射弹幕的实现。

代码

弹幕(头像,文字)

// 圆角矩形

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)

android 带头像的弹幕,原生Canvas实现带头像的弹幕相关推荐

  1. php网页自定义头像系统,怎样用canvas实现自定义头像功能

    这次给大家带来怎样用canvas实现自定义头像功能,用canvas实现自定义头像功能的注意事项有哪些,下面就是实战案例,一起来看一下. 写在最前: 前两天老大跟我说老虎官网上那个自定义头像的功能是fl ...

  2. Android Canvas绘制带箭头的直线

    先看下效果图: 下面我们直接看代码 我自定义了一个View,代码如下: package com.davis.drawtrangle;import android.content.Context; im ...

  3. 微信小程序踩坑记录 ------- canvas 生成带小程序码的微信朋友圈分享图

    最近做了一个问卷类的小程序,其中的结果页想让用户进行朋友圈分享转发,网上搜索资料,得出解决思路,用 canvas 将页面绘制生成图片,然后保存到手机相册,最终效果如下: 在这里我只写页面里关于 can ...

  4. GitHub上受欢迎的Android UI Library-项目开发实战篇:带各类框架链接地址详细解说及使用方法

    这是我列举的下列所有框架github地址:https : //github.com/opendigg/awesome-github-android-ui 抽屉菜单类的框架 MaterialDrawer ...

  5. 国庆头像小程序源码,带独立版后台同时可添加小程序跳转+流量主,所有改动均可后台添加+带搭建教程

    微信小程序实现国旗头像,国庆个性化头像 国庆头像小程序源码,带独立版后台同时可添加小程序跳转+流量主,所有改动均可后台添加+带搭建教程 快去挑选一个自己喜欢的国庆头像吧,只需简单两步即可制作自己专属国 ...

  6. 原生canvas游戏性能优化

    微信小游戏在 17 年末推出,再次带火了 H5 小游戏的开发,本公众号准备一些 H5 游戏开发的文章,奉献给读者,想做小游戏的可以练练手,没时间做的可以学习下游戏常用的一些方法和概念. 本文来自 li ...

  7. java 后端 使用 Graphics2D 制作海报,画echarts图,带工具类,各种细节:如头像切割成圆形,文字换行算法(完美实验success),解决画上文字、图片后不清晰问题

    文章目录 先看成品 前言 一.项目目录结构 一.海报制作PosterUtil.java工具类 1. 描述 2. 代码 二.测试生成海报 1. 描述 2. 直接上代码 四.其他测试 1. Test1_C ...

  8. Python之tkinter:动态演示调用python库的tkinter带你进入GUI世界(Canvas)

    Python之tkinter:动态演示调用python库的tkinter带你进入GUI世界(Canvas) 导读 动态演示调用python库的tkinter带你进入GUI世界(Canvas) 目录 t ...

  9. 带你体验云原生场景下 Serverless 应用编程模型

    简介: 阿里云 Knative 基于 ASK 之上,在完全兼容社区 Knaitve 的同时对 FC.ECI 工作负载进行统一应用编排,支持事件驱动.自动弹性,为您提供统一的 Serverless 应用 ...

最新文章

  1. 如何用php实现分页效果
  2. 单页面与多页面的区别及优缺点
  3. FPGA 开平方方法
  4. mysql编译参数查看_查看 apache,nginx,mysql 安装时的编译参数
  5. PHP编译安装时常见错误解决办法,php编译常见错误
  6. 真是一分钱一分货 NVme SSD都有哪些优势?
  7. 什么是java常量?
  8. java cookie 加密_java cookie encodeBase64加密
  9. 深入理解javascript原型和闭包(12)——简介【作用域】
  10. oracle游标遍历的三种方式
  11. 点乘叉乘坐标公式_点积与叉乘的运算与物理意义
  12. 蓝桥杯 左baby右兄弟
  13. 默认conf指向位置
  14. UE4 打包之后Mesh没有材质问题
  15. java sqlite sqlite_busy_sqlite3出现SQLITE_BUSY错误码的原因以及解决方法
  16. 【转】给大家分享一下目前mlc颗粒的内存卡资料
  17. 微信动态表情保存到手机相册
  18. 【飞凌嵌入式 OK3399-C+开发板试用体验】开箱上电
  19. 人间清醒,内容为王 - 技术er究竟该如何写博客?1024上海嘉年华之敖丙演讲观后感。
  20. POJ 1205 Water Treatment Plants(递推)

热门文章

  1. JedisConnectionException: Could not get a resource from the pool
  2. 超级计算机国产cpu,为何国产超级计算机已经领先全世界了,而国产cpu却依然落后?...
  3. html 支持ssi,shtml网页SSI使用详解
  4. 天龙八部TLBB补丁Update目录说明
  5. 人机大战硝烟再起:阿尔法狗升级了 柯洁拼了
  6. 破解JS加密:url unicode加密而已
  7. 看来不止一次的电影(电影经典给你好看准备下载下来,免得以后收费了)
  8. 转:程序员常用不常见很难得的地址大全,博主很辛苦
  9. 计算机组成原理第四章例4.1,计算机组成原理第四章.ppt
  10. Android 定制自己的launcher