全站通知:
Widget:Danmu
刷
历
编
跳到导航
跳到搜索
<!DOCTYPE html> <html>
<head>
<meta charset="UTF-8"> <title>弹幕效果</title> <style> .danmu-container { width: 100%; position: relative; overflow: hidden; }
.danmu { position: absolute; color: #383030; white-space: nowrap; font-size: 16px; top: 0; left: 100%; }
@keyframes moveLeft { from { transform: translateX(0); }
to { transform: translateX(-100vw); } } </style>
</head>
<body>
<script> const config = { duration: 8, minInterval: 300, maxInterval: 800, maxDanmu: 20, containerHeight: '200px', trackHeight: 20, trackPadding: 2, fontSize: 20, cleanupInterval: 2000, defaultDanmu: [ '欢迎来到弹幕区~', '下次赶晚的猎人小姐:我再也不早早下池了[跪了][跪了][跪了][跪了]', '猎人小姐:老姚烧烤再送20[开学季][开学季][开学季]', "猎人小姐:真的送啦!!!!", "恋与深空:感谢支持,20连福利大派送!", "猎人小姐:可以啊啊啊我爱你", "!!:???好家伙", "啊啊啊好好好稳稳稳", "爽!哈哈哈哈哈", "在【首页:他】的评论区留言即可发送弹幕~" ] };
class TrackManager { constructor(containerHeight, trackHeight, trackPadding) { this.trackHeight = trackHeight; this.trackPadding = trackPadding; this.numTracks = Math.floor(containerHeight / (trackHeight)); console.log(`Container height: ${containerHeight}, Tracks: ${this.numTracks}`); this.tracks = new Array(this.numTracks).fill(false); }
getAvailableTrack() { const availableTracks = this.tracks .map((occupied, index) => ({ occupied, index })) .filter(track => !track.occupied) .map(track => track.index);
if (availableTracks.length === 0) return -1; const randomIndex = Math.floor(Math.random() * availableTracks.length); return availableTracks[randomIndex]; }
occupyTrack(trackIndex) { if (trackIndex >= 0 && trackIndex < this.tracks.length) { this.tracks[trackIndex] = true; } }
releaseTrack(trackIndex) { if (trackIndex >= 0 && trackIndex < this.tracks.length) { this.tracks[trackIndex] = false; } } }
const trackManager = new TrackManager( parseInt(config.containerHeight), config.trackHeight, config.trackPadding );
let danmuArr = [...config.defaultDanmu];
function getDanmuFromComments() { const comments = document.querySelectorAll('.comment-thread'); comments.forEach(comment => { try { const username = comment.querySelector('.comment-user-name a')?.textContent; const content = comment.querySelector('.comment-text')?.textContent; if (username && content) { const danmuText = `${username}:${content}`; danmuArr.push(danmuText); } } catch (error) { console.error('Error processing comment:', error); } }); }
const container = document.getElementById('danmuContainer'); container.style.height = config.containerHeight;
let activeDanmu = 0; const danmuPool = [];
function createDanmuPool(size) { for (let i = 0; i < size; i++) { const danmu = document.createElement('div'); danmu.className = 'danmu'; danmu.style.display = 'none'; container.appendChild(danmu); danmuPool.push(danmu); } }
function getDanmuFromPool() { return danmuPool.find(danmu => danmu.style.display === 'none') || null; }
function createDanmu(text) { if (activeDanmu >= config.maxDanmu) return;
const trackIndex = trackManager.getAvailableTrack(); if (trackIndex === -1) return;
const danmu = getDanmuFromPool(); if (!danmu) return;
activeDanmu++; trackManager.occupyTrack(trackIndex);
danmu.innerText = text; danmu.style.display = 'block'; danmu.style.top = (trackIndex * (config.trackHeight + config.trackPadding)) + 'px'; danmu.style.animation = `moveLeft ${config.duration}s linear`; danmu.dataset.trackIndex = trackIndex;
danmu.addEventListener('animationend', () => { danmu.style.display = 'none'; danmu.style.animation = ; activeDanmu--; trackManager.releaseTrack(parseInt(danmu.dataset.trackIndex)); }, { once: true }); }
let lastTime = 0; let nextInterval = config.minInterval;
function animate(currentTime) { if (!lastTime) lastTime = currentTime;
const elapsed = currentTime - lastTime;
if (elapsed >= nextInterval) { const randomDelay = Math.random() * 500; setTimeout(() => { const randomIndex = Math.floor(Math.random() * danmuArr.length); createDanmu(danmuArr[randomIndex]); }, randomDelay);
lastTime = currentTime; nextInterval = Math.random() * (config.maxInterval - config.minInterval) + config.minInterval; }
requestAnimationFrame(animate); }
async function initialize() { try { createDanmuPool(config.maxDanmu); requestAnimationFrame(animate);
const hasComments = await waitForComments(); if (hasComments) { getDanmuFromComments(); } else { console.log('Comments not found, using default danmu only'); danmuArr = [...config.defaultDanmu]; } } catch (error) { console.error('Initialization error:', error); danmuArr = [...config.defaultDanmu]; createDanmuPool(config.maxDanmu); requestAnimationFrame(animate); } }
function waitForComments() { return new Promise((resolve) => { let attempts = 0; const maxAttempts = 50; const checkComments = () => { attempts++; const comments = document.querySelectorAll('.comment-thread'); if (comments.length > 0) { resolve(true); } else if (attempts >= maxAttempts) { resolve(false); } else { setTimeout(checkComments, 100); } }; checkComments(); }); }
initialize();
setInterval(() => { danmuPool.forEach(danmu => { if (danmu.style.display === 'none') { danmu.style.animation = ; } }); }, config.cleanupInterval); </script>
</body>
</html>