Compare commits
1 Commits
main
...
version-2.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
532cbafd8e |
@@ -301,7 +301,8 @@
|
||||
<button id="fab-music" class="fab-item" tabindex="0"><i class="ri-music-2-line"></i><span class="fab-text">Play</span></button>
|
||||
</div>
|
||||
</div>
|
||||
<audio id="site-audio" class="site-audio" src="data/至少做一件离谱的事-Kiri T_compressed.mp3" loop preload="none"></audio>
|
||||
<!-- 隐藏的音频播放iframe -->
|
||||
<iframe id="audio-player-iframe" src="audio-player.html" style="display: none;"></iframe>
|
||||
</main>
|
||||
|
||||
<!-- 微信弹窗 -->
|
||||
@@ -318,7 +319,7 @@
|
||||
</div>
|
||||
|
||||
<footer class="site-footer">
|
||||
<p>© 2018 <span id="currentYear"></span> Honesty. All rights reserved. <a class="icp" href="https://beian.miit.gov.cn/" target="_blank">湘ICP备20014902号</a> Powered By <a href="https://pages.edgeone.ai/" target="_blank"> Tencent EdgeOne </a></p>
|
||||
<p>Copyright © 2018 <span id="currentYear"></span> Honesty. All rights reserved. <a class="icp" href="https://beian.miit.gov.cn/" target="_blank">湘ICP备20014902号</a> Powered By <a href="https://pages.edgeone.ai/" target="_blank"> Tencent EdgeOne </a></p>
|
||||
<script>
|
||||
document.getElementById("currentYear").textContent = ' - ' + new Date().getFullYear();
|
||||
</script>
|
||||
|
||||
208
audio-player.html
Normal file
208
audio-player.html
Normal file
@@ -0,0 +1,208 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Audio Player</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<audio id="audio-player" controls autoplay style="display: none;"></audio>
|
||||
|
||||
<script>
|
||||
const audio = document.getElementById('audio-player');
|
||||
let currentSrc = '';
|
||||
let isStateBroadcasting = false;
|
||||
|
||||
// 监听来自主页面的消息
|
||||
window.addEventListener('message', function(event) {
|
||||
// 允许来自同域的所有窗口的消息
|
||||
if (event.origin !== window.location.origin) return;
|
||||
|
||||
const data = event.data;
|
||||
|
||||
switch (data.action) {
|
||||
case 'play':
|
||||
// 如果当前正在播放,则暂停;否则播放
|
||||
if (audio.paused) {
|
||||
audio.play().catch(e => console.error('播放失败:', e));
|
||||
} else {
|
||||
audio.pause();
|
||||
}
|
||||
// 发送状态更新回所有可能的窗口
|
||||
broadcastState();
|
||||
break;
|
||||
|
||||
case 'pause':
|
||||
if (!audio.paused) {
|
||||
audio.pause();
|
||||
}
|
||||
broadcastState();
|
||||
break;
|
||||
|
||||
case 'setTrack':
|
||||
if (currentSrc !== data.src) {
|
||||
currentSrc = data.src;
|
||||
audio.src = data.src;
|
||||
audio.load();
|
||||
// 自动播放新曲目
|
||||
setTimeout(() => {
|
||||
audio.play().catch(e => console.error('播放失败:', e));
|
||||
broadcastState();
|
||||
}, 100);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'setVolume':
|
||||
audio.volume = data.volume;
|
||||
broadcastState();
|
||||
break;
|
||||
|
||||
case 'getCurrentState':
|
||||
// 向请求方发送当前状态
|
||||
event.source.postMessage({
|
||||
action: 'currentState',
|
||||
playing: !audio.paused,
|
||||
src: audio.src,
|
||||
volume: audio.volume,
|
||||
currentTime: audio.currentTime,
|
||||
duration: audio.duration || 0
|
||||
}, event.origin);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
// 广播状态到所有可能监听的窗口
|
||||
function broadcastState() {
|
||||
if (isStateBroadcasting) return; // 防止状态广播循环
|
||||
|
||||
isStateBroadcasting = true;
|
||||
window.parent.postMessage({
|
||||
action: 'stateChange',
|
||||
playing: !audio.paused,
|
||||
src: audio.src,
|
||||
volume: audio.volume,
|
||||
currentTime: audio.currentTime,
|
||||
duration: audio.duration || 0
|
||||
}, '*');
|
||||
|
||||
// 保存播放状态
|
||||
savePlaybackState();
|
||||
|
||||
setTimeout(() => {
|
||||
isStateBroadcasting = false;
|
||||
}, 10);
|
||||
}
|
||||
|
||||
// 监听音频事件并通知主页面
|
||||
audio.addEventListener('play', function() {
|
||||
broadcastState();
|
||||
});
|
||||
|
||||
audio.addEventListener('pause', function() {
|
||||
broadcastState();
|
||||
});
|
||||
|
||||
audio.addEventListener('ended', function() {
|
||||
window.parent.postMessage({
|
||||
action: 'trackEnded'
|
||||
}, '*');
|
||||
});
|
||||
|
||||
// 页面加载完成后通知主页面
|
||||
window.addEventListener('load', function() {
|
||||
// 恢复之前保存的播放状态
|
||||
restorePlaybackState();
|
||||
|
||||
window.parent.postMessage({
|
||||
action: 'playerReady'
|
||||
}, '*');
|
||||
});
|
||||
|
||||
// 页面可见性变化时处理
|
||||
document.addEventListener('visibilitychange', function() {
|
||||
if (!document.hidden) {
|
||||
// 页面变为可见时,广播当前状态
|
||||
setTimeout(broadcastState, 100);
|
||||
}
|
||||
});
|
||||
|
||||
// 页面即将卸载时保存播放状态
|
||||
window.addEventListener('beforeunload', function() {
|
||||
savePlaybackState();
|
||||
});
|
||||
|
||||
// 保存播放状态到 sessionStorage
|
||||
function savePlaybackState() {
|
||||
try {
|
||||
sessionStorage.setItem('audioState', JSON.stringify({
|
||||
src: audio.src,
|
||||
currentTime: audio.currentTime,
|
||||
playing: !audio.paused,
|
||||
volume: audio.volume,
|
||||
timestamp: Date.now()
|
||||
}));
|
||||
} catch (e) {
|
||||
console.error('保存音频状态失败:', e);
|
||||
}
|
||||
}
|
||||
|
||||
// 从 sessionStorage 恢复播放状态
|
||||
function restorePlaybackState() {
|
||||
try {
|
||||
const savedState = sessionStorage.getItem('audioState');
|
||||
if (savedState) {
|
||||
const state = JSON.parse(savedState);
|
||||
// 如果状态保存时间不超过1小时,则恢复播放
|
||||
if (Date.now() - state.timestamp < 3600000) {
|
||||
currentSrc = state.src;
|
||||
audio.src = state.src;
|
||||
audio.volume = state.volume !== undefined ? state.volume : 1.0;
|
||||
audio.currentTime = state.currentTime || 0;
|
||||
|
||||
// 确保在用户交互后才尝试播放
|
||||
if (state.playing) {
|
||||
// 检查是否已经有用户交互
|
||||
if (document.hasFocus()) {
|
||||
// 稍微延迟播放,确保一切准备就绪
|
||||
setTimeout(() => {
|
||||
audio.play().catch(e => {
|
||||
console.error('恢复播放失败:', e);
|
||||
// 如果自动播放失败,等待用户交互后再播放
|
||||
const tryPlayOnInteraction = () => {
|
||||
audio.play().catch(console.error);
|
||||
document.removeEventListener('click', tryPlayOnInteraction);
|
||||
document.removeEventListener('touchstart', tryPlayOnInteraction);
|
||||
};
|
||||
|
||||
document.addEventListener('click', tryPlayOnInteraction, { once: true });
|
||||
document.addEventListener('touchstart', tryPlayOnInteraction, { once: true });
|
||||
});
|
||||
}, 300);
|
||||
} else {
|
||||
// 等待用户交互后再播放
|
||||
const tryPlayOnInteraction = () => {
|
||||
audio.play().catch(console.error);
|
||||
document.removeEventListener('click', tryPlayOnInteraction);
|
||||
document.removeEventListener('touchstart', tryPlayOnInteraction);
|
||||
};
|
||||
|
||||
document.addEventListener('click', tryPlayOnInteraction, { once: true });
|
||||
document.addEventListener('touchstart', tryPlayOnInteraction, { once: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('恢复音频状态失败:', e);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
23
index.html
23
index.html
@@ -413,5 +413,26 @@
|
||||
}()
|
||||
}({id: SiteConfig.analytics.tencent.id, ck: SiteConfig.analytics.tencent.ck});
|
||||
</script>
|
||||
|
||||
<!-- 隐藏的音频播放iframe -->
|
||||
<iframe id="audio-player-iframe" src="audio-player.html" style="display: none;"></iframe>
|
||||
|
||||
<script>
|
||||
// 音频控制逻辑
|
||||
let audioIframe = document.getElementById('audio-player-iframe');
|
||||
// 监听来自iframe的消息
|
||||
window.addEventListener('message', function(event) {
|
||||
// 确保消息来自我们的iframe
|
||||
if (event.source !== audioIframe.contentWindow) return;
|
||||
const data = event.data;
|
||||
if (data.playing && data.action === 'playerReady') {
|
||||
// iframe准备就绪,设置初始音频
|
||||
audioIframe.contentWindow.postMessage({
|
||||
action: 'setTrack',
|
||||
src: 'data/至少做一件离谱的事-Kiri T_compressed.mp3'
|
||||
}, '*');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
159
js/about.js
159
js/about.js
@@ -534,6 +534,9 @@ class DataManager {
|
||||
=========================== */
|
||||
class UIManager {
|
||||
constructor() {
|
||||
this.userInteracted = false;
|
||||
this.audioPlayer = null;
|
||||
this.audioIframe = null;
|
||||
this.initTechCloud();
|
||||
this.initModal();
|
||||
this.initArtalk();
|
||||
@@ -1080,10 +1083,33 @@ class UIManager {
|
||||
const theme = getStoredTheme();
|
||||
fLang.querySelector('.fab-text').textContent = lang === 'zh' ? 'English' : '中文';
|
||||
fTheme.querySelector('.fab-text').textContent = theme === 'night' ? 'Day' : 'Night';
|
||||
const playing = (this.audio && !this.audio.paused);
|
||||
fMusic.querySelector('.fab-text').textContent = lang === 'zh' ? (playing ? '暂停' : '播放') : (playing ? 'Pause' : 'Play');
|
||||
// 音频播放状态需要通过iframe通信获取
|
||||
const audioIframe = document.getElementById('audio-player-iframe');
|
||||
if (audioIframe && audioIframe.contentWindow) {
|
||||
// 请求当前播放状态
|
||||
audioIframe.contentWindow.postMessage({
|
||||
action: 'getCurrentState'
|
||||
}, '*');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 监听来自iframe的音频状态更新
|
||||
window.addEventListener('message', (event) => {
|
||||
const audioIframe = document.getElementById('audio-player-iframe');
|
||||
if (!audioIframe || event.source !== audioIframe.contentWindow) return;
|
||||
|
||||
const data = event.data;
|
||||
if (data.action === 'currentState') {
|
||||
const fMusic = document.getElementById('fab-music');
|
||||
if (fMusic) {
|
||||
const lang = getStoredLanguage();
|
||||
fMusic.querySelector('.fab-text').textContent =
|
||||
lang === 'zh' ? (data.playing ? '暂停' : '播放') : (data.playing ? 'Pause' : 'Play');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
main.addEventListener('click', () => {
|
||||
menu.classList.toggle('open');
|
||||
main.setAttribute('aria-expanded', menu.classList.contains('open') ? 'true' : 'false');
|
||||
@@ -1101,17 +1127,15 @@ class UIManager {
|
||||
requestAnimationFrame(updateLabels);
|
||||
});
|
||||
fMusic.addEventListener('click', () => {
|
||||
if (this.audio) {
|
||||
if (this.audio.paused) {
|
||||
this.audio.play().catch(() => {
|
||||
});
|
||||
// 清除暂停时间记录,允许下次自动播放
|
||||
this.clearMusicPauseTime();
|
||||
} else {
|
||||
this.audio.pause();
|
||||
// 记录暂停时间
|
||||
this.setMusicPauseTime();
|
||||
}
|
||||
const audioIframe = document.getElementById('audio-player-iframe');
|
||||
if (audioIframe && audioIframe.contentWindow) {
|
||||
// 直接发送播放指令,让iframe内部处理播放/暂停切换
|
||||
audioIframe.contentWindow.postMessage({
|
||||
action: 'play'
|
||||
}, '*');
|
||||
|
||||
// 记录用户操作
|
||||
this.setMusicPauseTime(); // 先记录暂停时间
|
||||
}
|
||||
// 延迟更新标签以避免阻塞
|
||||
requestAnimationFrame(updateLabels);
|
||||
@@ -1122,33 +1146,33 @@ class UIManager {
|
||||
|
||||
|
||||
initAudio() {
|
||||
const el = document.getElementById('site-audio');
|
||||
if (!el) return;
|
||||
this.audio = el;
|
||||
this.audio.loop = true;
|
||||
// 获取已存在的iframe或创建新的
|
||||
let audioIframe = document.getElementById('audio-player-iframe');
|
||||
if (!audioIframe) {
|
||||
audioIframe = document.createElement('iframe');
|
||||
audioIframe.src = 'audio-player.html';
|
||||
audioIframe.style.display = 'none';
|
||||
audioIframe.id = 'audio-player-iframe';
|
||||
document.body.appendChild(audioIframe);
|
||||
}
|
||||
this.audioIframe = audioIframe;
|
||||
|
||||
// 页面加载完成后根据条件决定是否播放
|
||||
window.addEventListener('load', () => {
|
||||
|
||||
const autoPlayer = () => {
|
||||
// 检查是否在24小时内用户暂停过音乐
|
||||
const shouldRemainPaused = this.shouldMusicRemainPaused();
|
||||
// 如果不应该保持暂停状态,则尝试播放
|
||||
if (!shouldRemainPaused) {
|
||||
this.audio.autoplay = true;
|
||||
// 添加用户交互检查,避免浏览器阻止自动播放
|
||||
const attemptAutoplay = () => {
|
||||
// 检查是否已有用户交互
|
||||
if (this.userInteracted) {
|
||||
this.audio.play().catch(() => {
|
||||
// 静默处理播放失败
|
||||
console.error('Failed to play audio.');
|
||||
});
|
||||
this.playAudio();
|
||||
} else {
|
||||
// 添加一次性用户交互监听器
|
||||
const enableAudio = () => {
|
||||
this.userInteracted = true;
|
||||
this.audio.play().catch(() => {
|
||||
console.error('Failed to play audio.');
|
||||
});
|
||||
this.playAudio();
|
||||
document.removeEventListener('click', enableAudio);
|
||||
document.removeEventListener('touchstart', enableAudio);
|
||||
document.removeEventListener('keydown', enableAudio);
|
||||
@@ -1161,9 +1185,86 @@ class UIManager {
|
||||
document.addEventListener('mousemove', enableAudio, { once: true });
|
||||
}
|
||||
};
|
||||
requestAnimationFrame(attemptAutoplay);
|
||||
setTimeout(attemptAutoplay, 500); // 稍微延迟以确保iframe加载完成
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 监听iframe发来的消息
|
||||
const handleMessage = (event) => {
|
||||
// 确保消息来自我们的iframe
|
||||
if (event.source !== this.audioIframe.contentWindow) return;
|
||||
|
||||
const data = event.data;
|
||||
switch (data.action) {
|
||||
case 'playerReady':
|
||||
// iframe准备就绪,设置初始音频
|
||||
this.setAudioTrack('data/至少做一件离谱的事-Kiri T_compressed.mp3');
|
||||
autoPlayer();
|
||||
break;
|
||||
|
||||
case 'stateChange':
|
||||
// 音频状态改变,更新UI
|
||||
this.updateAudioUI(data.playing);
|
||||
break;
|
||||
|
||||
case 'trackEnded':
|
||||
// 曲目结束
|
||||
this.updateAudioUI(false);
|
||||
break;
|
||||
|
||||
case 'currentState':
|
||||
// 当前状态响应
|
||||
this.updateAudioUI(data.playing);
|
||||
if (!data.playing) {
|
||||
autoPlayer();
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('message', handleMessage);
|
||||
}
|
||||
|
||||
// 播放音频
|
||||
playAudio() {
|
||||
const audioIframe = document.getElementById('audio-player-iframe');
|
||||
if (audioIframe && audioIframe.contentWindow) {
|
||||
audioIframe.contentWindow.postMessage({
|
||||
action: 'play'
|
||||
}, '*');
|
||||
}
|
||||
}
|
||||
|
||||
// 暂停音频
|
||||
pauseAudio() {
|
||||
const audioIframe = document.getElementById('audio-player-iframe');
|
||||
if (audioIframe && audioIframe.contentWindow) {
|
||||
audioIframe.contentWindow.postMessage({
|
||||
action: 'pause'
|
||||
}, '*');
|
||||
}
|
||||
}
|
||||
|
||||
// 设置音频曲目
|
||||
setAudioTrack(src) {
|
||||
const audioIframe = document.getElementById('audio-player-iframe');
|
||||
if (audioIframe && audioIframe.contentWindow) {
|
||||
audioIframe.contentWindow.postMessage({
|
||||
action: 'setTrack',
|
||||
src: src
|
||||
}, '*');
|
||||
}
|
||||
}
|
||||
|
||||
// 更新音频UI状态
|
||||
updateAudioUI(playing) {
|
||||
// 更新移动端fab按钮的文本
|
||||
const fMusic = document.getElementById('fab-music');
|
||||
if (fMusic) {
|
||||
const lang = getStoredLanguage();
|
||||
fMusic.querySelector('.fab-text').textContent =
|
||||
lang === 'zh' ? (playing ? '暂停' : '播放') : (playing ? 'Pause' : 'Play');
|
||||
}
|
||||
}
|
||||
|
||||
updateCustomStyles(container, theme) {
|
||||
|
||||
42
js/audio-service-worker.js
Normal file
42
js/audio-service-worker.js
Normal file
@@ -0,0 +1,42 @@
|
||||
// audio-service-worker.js
|
||||
// Service Worker for background audio playback
|
||||
|
||||
self.addEventListener('install', event => {
|
||||
self.skipWaiting();
|
||||
});
|
||||
|
||||
self.addEventListener('activate', event => {
|
||||
event.waitUntil(self.clients.claim());
|
||||
});
|
||||
|
||||
// 监听来自主页面的消息
|
||||
self.addEventListener('message', async event => {
|
||||
const client = event.source;
|
||||
const data = event.data;
|
||||
|
||||
switch (data.action) {
|
||||
case 'play':
|
||||
// 这里只是示例,实际上Service Worker无法直接播放音频
|
||||
// 我们需要采用另一种方式实现
|
||||
break;
|
||||
|
||||
case 'pause':
|
||||
break;
|
||||
|
||||
case 'setTrack':
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
// 使用 Broadcast Channel API 在页面间通信
|
||||
const broadcastChannel = new BroadcastChannel('audio-control');
|
||||
|
||||
broadcastChannel.addEventListener('message', event => {
|
||||
const data = event.data;
|
||||
// 将消息转发给所有客户端
|
||||
self.clients.matchAll().then(clients => {
|
||||
clients.forEach(client => {
|
||||
client.postMessage(data);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user