Когда я смотрел станцию Б, то заинтересовался реализацией заграждения. Сначала я думал использовать анимацию CSS3 для ее реализации. Позже я почувствовал, что производительность не очень хорошая. Я проверил информацию и обнаружил, что она может быть реализовано с помощью холста, поэтому я нащупал и написал простой шквал.
Заградительная функция
- Поддержка динамического добавления заграждения
- Заграждение не пересекается
- Настройте цвет заграждения
визуализация
Фронтенд-фреймворк выбрал более привычный vuejs
Основная идея прокрутки экрана пули состоит в том, чтобы непрерывно менять положение экрана пули по таймеру и время от времени перекрашивать холст.
Этапы реализации
Сначала добавьте тег холста.Вот примечание.Что касается влияния соотношения пикселей устройства на холсте, рисунок будет размытым.
<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}`);
}
Далее установите таймер на 20 мс, чтобы добиться эффекта заградительной прокрутки.
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 = '';
}
Области для улучшения и вопросы?
- Как предотвратить перекрытие заграждений, когда скорость не постоянна
- Обнаруживает ли видеочат каждый кадр видео в соответствии со временем отправки? Как добиться?