看B站时,对弹幕的实现产生了兴趣,一开始想到用css3动画去实现,后来感受这样性能不是很好,查了下资料,发现能够用canvas实现,因而就摸索着写了一个简单的弹幕。css
弹幕功能前端
效果图vue
demo css3
源码地址git
前端框架选了比较熟悉的vuejsgithub
弹幕滚动的基本思路就是经过定时器不断地改变弹幕的位置,时时重绘画布。canvas
实现步骤数组
先加入一个canvas标签,这里有个注意点,关于设备像素比对canvas的影响,会出现绘图模糊。bash
canvas 绘图模糊问题前端框架
<canvas width="600" height="600"></canvas> // 若是单纯这样写,canvas会出现模糊
<canvas width="600" height="600" style="width: 300px;height: 300px"></canvas>
//为了避免出现模糊,须要设置canvas的css宽高为上下文宽高的1/devicePixelRatio,
本文是对于devicePixelRatio:2的设备设置的,该值可从window.devicePixelRatio取得。
<canvas ref="hiddenCanvas" width="0" height="0" style="display: none"></canvas>
// 后面会用到复制代码
咱们先定义一个数组来存放弹幕数据,一条弹幕信息,包括文本内容,x,y坐标位置,颜色,速度(能够是随机或者固定,为了计算简单,咱们这里采用了固定的速度)
var dmArr = [];
var gap = 80; // 弹幕的上下间距
var hiddenCanvas = this.$refs.hiddenCanvas;
// 增长弹幕的方法
function pushDm(text, color) {
let y = getY(); // 先肯定跑道
let x = 600; // 初始x坐标为canvas的右边界
let delayWidth = 0; // 同跑道
for (let i = 0, len = dmArr.length; i < len; i++) {
let dm = dmArr[i];
if (y === dm.y) { // 若是是同跑道,则日后排,设置必定的间隔,保证弹幕不会重叠;
delayWidth += Math.floor(hiddenCanvas.getContext('2d').measureText(dm.text).width * 4 + 50);
} }
dmArr.push({
text: text,
x: x + delayWidth,
y: y,
speed: 8,
color: color || getColor()
});
}
// 随机得到y坐标
function getY() {
let range = Math.floor(600 / gap); // 跑道数量
return Math.floor(Math.random() * range + 1) * gap;
}
// 随机得到颜色
function getColor() {
return `${Math.floor(Math.random() * 16777215).toString(16)}`;
}
// 写一个for循环,初始化30条弹幕
for (let i = 0; i < 30; i++) {
pushDm(`It's barrage ${i}`); } 复制代码
接下来设置一个20ms的定时器,实现弹幕滚动效果
var timer = null;
var ctx = this.$refs.canvas.getContext('2d');
function start(){
timer = setInterval(() => {
ctx.clearRect(0, 0, 600, 600); // 每次须要清空画布
ctx.save();
ctx.font = '30px Microsoft YaHei'; // 这里须要把字体大小设为须要显示的css大小的2倍(devicePixelRatio为2时)
if (!dmArr.length) stop(); // 若是没有新弹幕了,就中止计时器
for (let i = 0, len = this.dmArr.length; i < len; i++) {
let dm = dmArr[i];
let overRange = -ctx.measureText(dm.text).width * 2;
dm.x -= dm.speed;
if (dm.x < overRange) {
dmArr.splice(i, 1); // 弹幕在画布中不可见时,从数组中移除该项
continue;
}
ctx.fillStyle = `#${dm.color}`;
ctx.fillText(dm.text, dm.x, dm.y);
}
ctx.restore();
}, 20);
}
function stop() {
clearInterval(timer);
ctx.clearRect(0, 0, 600, 600);
}复制代码
咱们还须要一个输入框,来实现手动添加弹幕功能
<input type="text" @keyup.enter="sent" v-model="dmInput" maxlength="20">
<button type="button" @click="sent">发表</button>
var dmInput = '';
var color = ''; // 可自定义弹幕的颜色
function sent() {
if (!dmInput) return;
stop();
pushDm(dmInput, color);
start();
dmInput = '';
}复制代码
有待改进的地方和疑问?