
效果如上
html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script charset="utf-8" src="//picture.52hrttpic.com/static/webapp/vue/vue.min.js" type="text/javascript">
</script>
<link rel="stylesheet" href="./css/index.css" />
<script src="./js/update.js"></script>
</head>
<body>
<div id="app">
<div class="index" ref="index">
<div class="btn stop" @click="toggelDanmu">{{isStop ? '开始弹幕' : '停止弹幕'}}</div>
<div class="btn add" @click="adddanmu">添加弹幕</div>
</div>
</div>
</body>
<script src="./js/index.js"></script>
</html>
js
function AddDanMuFn(option) {
this.currentDanmuNum = 0; // 移动到实例属性
this.option = option;
this.elWidth = option.el.offsetWidth != 0 ? option.el.offsetWidth : document.querySelector('body').getBoundingClientRect().width;
this.initFn = function(text, isAddDanmu) {
let danmuName = 'danmu-' + this.randomString(6);
let danmuLine = this.currentDanmuNum % (this.option.danmuLine || 3);
let danmuNum = `danmu-${danmuLine}`;
this.currentDanmuNum++;
let danmuNumAllDom = document.querySelectorAll(`.${danmuNum}`);
let newDomLeft = danmuNumAllDom.length > 0 ? this.calculateNewLeft(danmuNumAllDom) : this.elWidth;
let div = document.createElement('div');
// 手动添加的话 会有一个add 的类名 可以在css里 改变特定的样式
div.className = `${danmuName} ${danmuNum} item ${isAddDanmu ? 'add' : ''}`;
div.style.left = newDomLeft + 'px';
div.innerText = text;
(this.option.el || document.querySelector('body')).appendChild(div);
let currentDom = document.querySelector(`.${danmuName}`);
let divWidth = currentDom.offsetWidth;
let divHeight = currentDom.offsetHeight;
div.style.top = danmuLine * (divHeight + (this.option.marginTop || 10)) + 'px';
// Create animation and store its ID
let animationId = requestAnimationFrame(this.animationFn.bind(this, currentDom, divWidth, this.option
.speed || 3));
currentDom.animationId = animationId
};
this.animationFn = function(currentDom, divWidth, speed) {
let rect = currentDom.getBoundingClientRect();
if (rect.left + divWidth < 0) {
cancelAnimationFrame(currentDom.animationId); // Correctly cancel animation
currentDom.remove();
return;
}
currentDom.style.left = rect.left - speed + 'px';
let animationId = requestAnimationFrame(this.animationFn.bind(this, currentDom, divWidth, speed));
currentDom.animationId = animationId;
};
this.stopAll = function() {
let danmus = document.querySelectorAll('.item');
for (let danmu of danmus){
cancelAnimationFrame(danmu.animationId)
danmu.remove();
}
};
this.startAll = function() {
let danmus = document.querySelectorAll('.item');
for (let danmu of danmus) {
if (danmu.animationId) {
let divWidth = danmu.offsetWidth;
let speed = this.option.speed || 3;
let animationId = requestAnimationFrame(this.animationFn.bind(this, danmu, divWidth, speed));
danmu.animationId = animationId;
}
}
};
// 随机数
this.randomInteger = function(a, b) {
if (arguments.length === 1) {
return Math.floor(Math.random() * a);
} else if (arguments.length === 2) {
return Math.floor(Math.random() * (b - a + 1) + a);
}
return 0;
};
// 随机值
this.randomString = function(length) {
length = length || 8; // Reasonable default length
let chars = "ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678";
let result = "";
for (let i = 0; i < length; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length));
}
return result;
};
this.calculateNewLeft = function(danmuNumAllDom) {
let lastDom = danmuNumAllDom[danmuNumAllDom.length - 1];
let lastDomWidth = lastDom.offsetWidth;
let lastDomLeft = lastDom.getBoundingClientRect().left;
let newDomLeft = lastDomWidth + lastDomLeft + (this.option.marginLeft || 10);
return newDomLeft < this.elWidth ? this.elWidth : newDomLeft;
};
}
var app = new Vue({
el: '#app',
data: {
list: [{
text: '山河无恙,国泰民安'
},
{
text: '我爱你,中国!'
},
{
text: '辉煌七十五载,山河锦绣灿烂。'
},
{
text: '生日快乐,我的国!'
},
{
text: '清澈的爱,只为中国!'
},
{
text: '岁月悠悠,山河如画!'
},
{
text: '我爱中华!'
},
{
text: '75年风雨兼程,共筑中国梦!'
},
{
text: '鹭岛金秋红旗展,国庆佳节同欢庆'
},
{
text: '泱泱中华, 千古风华依旧'
},
],
currentIndex: 0, // 当前弹幕列表的下标
addDanmuInterval: null, // 添加弹幕的定时器
isStop: false
},
mounted() {
this.$nextTick(() => {
// 创建实例 生成这个实例 要在页面完全渲染完成的时候 要不然 会获取不到 传入el的宽度
// 这个弹幕的el css要设置成 position relative
// 每一个弹幕 都会有一个共同的class类名item 要设置成 position absolute
// white-space: nowrap; 文字要设置成 不换行
this.danmu = new AddDanMuFn({
danmuLine: 5, // 多少行弹幕
el: document.querySelector('.index'), // 在哪个div里添加弹幕
marginLeft: 30, // 每个弹幕距离左边的距离
speed: 3, // 速度
marginTop: 10 // 每行弹幕上下的距离
});
this.list.forEach((item, index) => {
this.danmu.initFn(this.list[index].text)
})
this.startAddDanmuFn()
})
},
methods: {
toggelDanmu() {
if (this.isStop) {
this.startDanmu()
} else {
this.stopDanmu()
}
this.isStop = !this.isStop
},
stopDanmu() {
// 停止添加弹幕
clearInterval(this.addDanmuInterval)
this.danmu.stopAll()
},
startDanmu() {
this.danmu.startAll()
this.startAddDanmuFn()
},
adddanmu() {
// 自己添加的弹幕 第二个参数 为true 就是代表 是自己添加的弹幕 这个弹幕就会有一个add的类名
this.danmu.initFn('自己添加的弹幕', true)
},
startAddDanmuFn() {
this.addDanmuInterval = setInterval(() => {
// 获取弹幕总数 如果超过20个 不添加弹幕
let danmuAllNum = document.querySelectorAll('.item').length
// if(danmuAllNum > 20) return
this.danmu.initFn(this.list[this.currentIndex].text)
this.currentIndex++
if (this.currentIndex >= this.list.length) {
this.currentIndex = 0
}
}, 1000)
}
},
})
css
*{
margin: 0;
padding: 0;
}
.index{
width: 100vw;
height: 100vh;
background:
position: relative;
overflow: hidden;
.btn{
position: absolute;
bottom: 0;
width: 50vw;
height: 30vw;
font-size: 4vw;
text-align: center;
line-height: 30vw;
&.stop{
left: 0;
}
&.add{
right: 0;
}
}
.item{
height: 10vw;
padding: 0 3vw;
border-radius: 10vw;
background-color: gainsboro;
color:
font-size: 4vw;
display: inline-block;
position: absolute;
white-space: nowrap;
line-height: 10vw;
&.add{
background: red;
}
}
}