目前代码还是有一些bug,主要是调整浏览器大小的时候会出现文字运动停顿等问题,后续有时间在解决,不同文字长度速度不同而产生的文字堆叠问题已经解决,原理就是追击原理,代码如下,看效果可以直接拷贝到vscode打开浏览器运行即可
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<style>
.container {
width: 600px;
height: 400px;
border: 1px solid #ccc;
margin: 0 auto;
position: relative;
}
.scroll {
white-space: nowrap;
transition: all 3s;
}
.input {
position: absolute;
right: -220px;
bottom: 0;
}
.anima {
animation: fade 2s ease-in-out linear;
}
</style>
<div class="container">
<div class="input">
<input type="text" id="text" />
<button id="btn">发送</button>
</div>
</div>
<script>
let container = document.querySelector(".container");
let text = document.getElementById("text");
let {
width: containerWidth,
left: containerLeft,
right: containerRight,
} = container.getBoundingClientRect();
let bullet = {
left: 0,
duration: 0,
startTime: 0,
top: 0,
width: 0,
height: 0,
};
let cnStr = "这还是一个非常狗血的故事";
let abc = "abcdefghigklmnopqrstuvwsyg";
let upperABC = abc.toUpperCase();
let channel = [];
function randomNum(max, min) {
return parseInt(Math.random() * (max - min + 1) + min, 10);
}
function productData() {
let arr = [];
let preWidth = 600;
for (let i = 0; i < 1000; i++) {
let str =
i +
cnStr.substr(0, randomNum(-1, cnStr.length)) +
upperABC.substr(0, randomNum(-1, upperABC.length)) +
abc.substr(0, randomNum(-1, abc.length));
arr.push(str);
}
return arr;
}
function initBullet(str, top, type = "") {
if (str) {
let div = document.createElement("div");
div.innerHTML = str;
div.style.position = "absolute";
div.style.left = containerWidth + "px";
div.style.top = top + "px";
div.style.whiteSpace = "nowrap";
if (type === "current") {
div.style.border = "1px solid red";
div.className = "anima";
}
container.appendChild(div);
return div;
}
return null;
}
class Barrage {
constructor(container, data, top) {
this.data = data;
this.channel = [];
this.top = top;
this.currentArr = [];
this.init();
}
init() {
this.loopWatcherLaunch = this.wrapper.call(this);
this.loopWatcherLaunch.call(this);
}
wrapper() {
let div = initBullet(this.data[0], this.top);
let timer = null;
return function (newDom = null) {
if (!div) div = newDom;
clearInterval(timer);
let firstChnnel = this.channel[0];
if (firstChnnel) {
var firstChnnelRect = firstChnnel.getBoundingClientRect();
if (
parseInt(firstChnnelRect.right) - parseInt(containerLeft) <=
0
) {
container.removeChild(firstChnnel);
this.channel.shift();
}
}
// 子弹初始化
let check = this.checkIsLauncch(div);
if (check && div) {
if (this.currentArr.length === 0) this.data.shift();
launchBullet.call(this, div);
if (this.currentArr.length > 0) {
div = initBullet(this.currentArr.shift(), this.top, "current");
} else {
div = initBullet(this.data[0], this.top);
}
}
if (this.channel.length > 0) {
timer = setTimeout(() => {
window.requestAnimationFrame(this.loopWatcherLaunch.bind(this));
}, 0);
}
};
function launchBullet(div) {
let startTime = +new Date();
div.startTime = startTime;
div.duration = 5;
let { width, right } = div.getBoundingClientRect();
let leftDistance = right - containerLeft;
div.moveV = (containerWidth + width) / div.duration;
div.leftDruation = leftDistance / div.moveV;
this.channel.push(div);
div.style.transition = `all ${div.leftDruation}s linear 0s`;
div.style.transform = `translateX(-${right - containerLeft}px)`;
}
}
checkIsLauncch(dom) {
let lastDom = this.channel[this.channel.length - 1];
if (lastDom && dom) {
let duration = 5;
let lastRect = lastDom.getBoundingClientRect();
let newsRect = dom.getBoundingClientRect();
if (lastRect.right > containerRight - 20) return false;
let lastS = lastRect.left - containerLeft + lastRect.width;
let lastV = lastDom.moveV;
let lastT = lastS / lastV;
let newsS = containerWidth;
let newsV = (containerWidth + newsRect.width) / duration;
let newsT = newsS / newsV;
if (newsT < lastT) {
return false;
}
}
return true;
}
addLaunch(value) {
if (!value) {
return;
}
if (this.data.length > 0) {
this.currentArr.push(value);
} else {
this.data.unshift(value);
this.loopWatcherLaunch.call(
this,
initBullet(this.data[0], this.top)
);
}
}
getCurrentDataLen() {
return this.data.length;
}
}
let barrageArr = [];
let data = productData();
// let barrage = new Barrage(container, ["222", "333"]);
for (let i = 0; i < 5; i++) {
let barrage = new Barrage(
container,
data.slice(i * 200, (i + 1) * 200),
i * 80
);
barrageArr[i] = {
instance: barrage,
length: barrage.getCurrentDataLen(),
};
}
btn.onclick = function () {
let minItem = barrageArr[0].instance.getCurrentDataLen();
let barrage = barrageArr[0].instance;
barrageArr.forEach((item) => {
if (minItem < item.instance.getCurrentDataLen()) {
barrage = item.instance;
}
});
barrage.addLaunch(text.value);
};
</script>
</body>
</html>