效果图
代码
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>爱你一万年</title>
<!-- 弯月亮 荣誉出品,[email protected] -->
<style>
#wrapper {
height: 35rem;
width: 100%;
position: relative;
overflow: hidden;
background: url(https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1568263949912&di=5229baf0d8472ab65a023691894896dc&imgtype=0&src=http%3A%2F%2Fbpic.588ku.com%2Felement_origin_min_pic%2F16%2F06%2F25%2F11576df4a2f1f4c.jpg);
color: #ffffff82;
font-size: 14px;
text-shadow: 1px 1px #000;
animation:change 1s infinite;
}
.right {
/*left: 100%;*/
position: absolute;
visibility: hidden;
white-space: nowrap;
/*left: 700px;*/
transform: translateX(700px);
}
.left {
position: absolute;
white-space: nowrap;
user-select: none;
transition: transform 7s linear; /* 时间相同 越长的弹幕滑动距离越长 所以越快~ */
}
input {
position: absolute;
bottom: 10px;
left: 20rem;
width: 300px;
height: 26px;
}
button {
position: absolute;
bottom: 8px;
left: 40rem;
width: 100px;
height: 38px;
border-radius: 10px;
font-size: 16px;
background-color: #FFB6C1;
}
span {
left: 17rem;
font-size: 15px;
/*color:red;*/
}
@keyframes change {
0% {
color: #333;
}
25% {
color: #ff0;
}
50% {
color: #f60;
}
75% {
color: #cf0;
}
100% {
color: #f00;
}
}
</style>
</head>
<body>
<div id="wrapper">
<input type="text" value="爱你一万年">
<button>爱心一击</button>
</div>
<script>
//鼠标点击出现爱心特效
(function(window,document,undefined){
var hearts = [];
window.requestAnimationFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function (callback){
setTimeout(callback,1000/60);
}
})();
init();
function init(){
css(".heart{width: 10px;height: 10px;position: fixed;background: #f00;transform: rotate(45deg);-webkit-transform: rotate(45deg);-moz-transform: rotate(45deg);}.heart:after,.heart:before{content: '';width: inherit;height: inherit;background: inherit;border-radius: 50%;-webkit-border-radius: 50%;-moz-border-radius: 50%;position: absolute;}.heart:after{top: -5px;}.heart:before{left: -5px;}");
attachEvent();
gameloop();
}
function gameloop(){
for(var i=0;i<hearts.length;i++){
if(hearts[i].alpha <=0){
document.body.removeChild(hearts[i].el);
hearts.splice(i,1);
continue;
}
hearts[i].y--;
hearts[i].scale += 0.004;
hearts[i].alpha -= 0.013;
hearts[i].el.style.cssText = "left:"+hearts[i].x+"px;top:"+hearts[i].y+"px;opacity:"+hearts[i].alpha+";transform:scale("+hearts[i].scale+","+hearts[i].scale+") rotate(45deg);background:"+hearts[i].color;
}
requestAnimationFrame(gameloop);
}
function attachEvent(){
var old = typeof window.οnclick==="function" && window.onclick;
window.onclick = function(event){
old && old();
createHeart(event);
}
}
function createHeart(event){
var d = document.createElement("div");
d.className = "heart";
hearts.push({
el : d,
x : event.clientX - 5,
y : event.clientY - 5,
scale : 1,
alpha : 1,
color : randomColor()
});
document.body.appendChild(d);
}
function css(css){
var style = document.createElement("style");
style.type="text/css";
try{
style.appendChild(document.createTextNode(css));
}catch(ex){
style.styleSheet.cssText = css;
}
document.getElementsByTagName('head')[0].appendChild(style);
}
function randomColor(){
return "rgb("+(~~(Math.random()*255))+","+(~~(Math.random()*255))+","+(~~(Math.random()*255))+")";
}
})(window,document);
</script>
<script>
/**
* 设置 弹幕DOM池 每一个通道最多六条弹幕
**/
const MAX_DM_COUNT = 10;
const CHANNEL_COUNT = 10;
let domPool = [];
let danmuPool = [
// '爱你一万年',
];
let hasPosition = [];
/**
* 做一下初始化工作
*/
function init() {
let wrapper = document.getElementById('wrapper')
// 先new一些span 重复利用这些DOM
for (let j = 0; j < CHANNEL_COUNT; j++) {
let doms = [];
for (let i = 0; i < MAX_DM_COUNT; i++) {
// 要全部放进wrapper
let dom = document.createElement('span');
wrapper.appendChild(dom);
// 初始化dom的位置 通过设置className
dom.className = 'right';
// DOM的通道是固定的 所以设置好top就不需要再改变了
dom.style.top = j * 40 + 'px';
// dom.style.left=300+'px'
// dom.style.background='red'
// 放入改通道的DOM池
doms.push(dom);
// 每次到transition结束的时候 就是弹幕划出屏幕了 将DOM位置重置 再放回DOM池
dom.addEventListener('transitionend', () => {
dom.className = 'right';
// dom.style.transition = null;
// dom.style.left = null;
dom.style.transform = null;
domPool[j].push(dom);
});
}
domPool.push(doms);
}
// hasPosition 标记每个通道目前是否有位置
for (let i = 0; i < CHANNEL_COUNT; i++) {
hasPosition[i] = true;
}
}
/**
* 获取一个可以发射弹幕的通道 没有则返回-1
*/
function getChannel() {
for (let i = 0; i < CHANNEL_COUNT; i++) {
if (hasPosition[i] && domPool[i].length) return i;
}
return -1;
}
/**
* 根据DOM和弹幕信息 发射弹幕
*/
function shootDanmu(dom, text, channel) {
console.log('biu~ [' + text + ']');
dom.innerText = text;
// 如果为每个弹幕设置 transition 可以保证每个弹幕的速度相同 这里没有保证速度相同
// dom.style.transition = `transform ${7 + dom.clientWidth / 100}s linear`;
// dom.style.left = '-' + dom.clientWidth + 'px';
// 设置弹幕的位置信息 性能优化 left -> transform
dom.style.transform = `translateX(${-dom.clientWidth}px)`;
dom.className = 'left';
hasPosition[channel] = false;
// 弹幕全部显示之后 才能开始下一条弹幕
// 大概 dom.clientWidth * 10 的时间 该条弹幕就从右边全部划出到可见区域 再加1秒保证弹幕之间距离
setTimeout(() => {
hasPosition[channel] = true;
}, dom.clientWidth * 10 + 1000);
}
window.onload = function() {
init();
// 为input和button添加事件监听
let btn = document.getElementsByTagName('button')[0];
let input = document.getElementsByTagName('input')[0];
btn.addEventListener('click', () => {
input.value = input.value.trim();
if (input.value) danmuPool.push(input.value);input.value=''
})
input.addEventListener('keyup', (e) => {
if (e.key === 'Enter' && (input.value = input.value.trim())) {
danmuPool.push(input.value);input.value=''
}
})
// 每隔1ms从弹幕池里获取弹幕(如果有的话)并发射
setInterval(() => {
let channel;
if (danmuPool.length && (channel = getChannel()) != -1) {
let dom = domPool[channel].shift();
domPool[channel].push(dom)
let danmu = danmuPool.shift();
danmuPool.push(danmu);
shootDanmu(dom, danmu, channel);
}
}, 1);
}
</script>
</body>
</html>