android 带头像的弹幕,原生Canvas实现带头像的弹幕
效果
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实现带头像的弹幕相关推荐
- php网页自定义头像系统,怎样用canvas实现自定义头像功能
这次给大家带来怎样用canvas实现自定义头像功能,用canvas实现自定义头像功能的注意事项有哪些,下面就是实战案例,一起来看一下. 写在最前: 前两天老大跟我说老虎官网上那个自定义头像的功能是fl ...
- Android Canvas绘制带箭头的直线
先看下效果图: 下面我们直接看代码 我自定义了一个View,代码如下: package com.davis.drawtrangle;import android.content.Context; im ...
- 微信小程序踩坑记录 ------- canvas 生成带小程序码的微信朋友圈分享图
最近做了一个问卷类的小程序,其中的结果页想让用户进行朋友圈分享转发,网上搜索资料,得出解决思路,用 canvas 将页面绘制生成图片,然后保存到手机相册,最终效果如下: 在这里我只写页面里关于 can ...
- GitHub上受欢迎的Android UI Library-项目开发实战篇:带各类框架链接地址详细解说及使用方法
这是我列举的下列所有框架github地址:https : //github.com/opendigg/awesome-github-android-ui 抽屉菜单类的框架 MaterialDrawer ...
- 国庆头像小程序源码,带独立版后台同时可添加小程序跳转+流量主,所有改动均可后台添加+带搭建教程
微信小程序实现国旗头像,国庆个性化头像 国庆头像小程序源码,带独立版后台同时可添加小程序跳转+流量主,所有改动均可后台添加+带搭建教程 快去挑选一个自己喜欢的国庆头像吧,只需简单两步即可制作自己专属国 ...
- 原生canvas游戏性能优化
微信小游戏在 17 年末推出,再次带火了 H5 小游戏的开发,本公众号准备一些 H5 游戏开发的文章,奉献给读者,想做小游戏的可以练练手,没时间做的可以学习下游戏常用的一些方法和概念. 本文来自 li ...
- java 后端 使用 Graphics2D 制作海报,画echarts图,带工具类,各种细节:如头像切割成圆形,文字换行算法(完美实验success),解决画上文字、图片后不清晰问题
文章目录 先看成品 前言 一.项目目录结构 一.海报制作PosterUtil.java工具类 1. 描述 2. 代码 二.测试生成海报 1. 描述 2. 直接上代码 四.其他测试 1. Test1_C ...
- Python之tkinter:动态演示调用python库的tkinter带你进入GUI世界(Canvas)
Python之tkinter:动态演示调用python库的tkinter带你进入GUI世界(Canvas) 导读 动态演示调用python库的tkinter带你进入GUI世界(Canvas) 目录 t ...
- 带你体验云原生场景下 Serverless 应用编程模型
简介: 阿里云 Knative 基于 ASK 之上,在完全兼容社区 Knaitve 的同时对 FC.ECI 工作负载进行统一应用编排,支持事件驱动.自动弹性,为您提供统一的 Serverless 应用 ...
最新文章
- 如何用php实现分页效果
- 单页面与多页面的区别及优缺点
- FPGA 开平方方法
- mysql编译参数查看_查看 apache,nginx,mysql 安装时的编译参数
- PHP编译安装时常见错误解决办法,php编译常见错误
- 真是一分钱一分货 NVme SSD都有哪些优势?
- 什么是java常量?
- java cookie 加密_java cookie encodeBase64加密
- 深入理解javascript原型和闭包(12)——简介【作用域】
- oracle游标遍历的三种方式
- 点乘叉乘坐标公式_点积与叉乘的运算与物理意义
- 蓝桥杯 左baby右兄弟
- 默认conf指向位置
- UE4 打包之后Mesh没有材质问题
- java sqlite sqlite_busy_sqlite3出现SQLITE_BUSY错误码的原因以及解决方法
- 【转】给大家分享一下目前mlc颗粒的内存卡资料
- 微信动态表情保存到手机相册
- 【飞凌嵌入式 OK3399-C+开发板试用体验】开箱上电
- 人间清醒,内容为王 - 技术er究竟该如何写博客?1024上海嘉年华之敖丙演讲观后感。
- POJ 1205 Water Treatment Plants(递推)
热门文章
- JedisConnectionException: Could not get a resource from the pool
- 超级计算机国产cpu,为何国产超级计算机已经领先全世界了,而国产cpu却依然落后?...
- html 支持ssi,shtml网页SSI使用详解
- 天龙八部TLBB补丁Update目录说明
- 人机大战硝烟再起:阿尔法狗升级了 柯洁拼了
- 破解JS加密:url unicode加密而已
- 看来不止一次的电影(电影经典给你好看准备下载下来,免得以后收费了)
- 转:程序员常用不常见很难得的地址大全,博主很辛苦
- 计算机组成原理第四章例4.1,计算机组成原理第四章.ppt
- Android 定制自己的launcher