美 丽 中 国
本帖最后由 杨帆 于 2026-6-13 23:07 编辑 <br /><br /><!DOCTYPE html><html>
<head>
<meta charset="utf-8">
<meta name="referrer" content="never" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
<title>美丽中国</title>
<style>
@import url("https://fonts.googleapis.com/css2?family=Liu+Jian+Mao+Cao&family=Long+Cang&family=Ma+Shan+Zheng&family=ZCOOL+KuaiLe&family=ZCOOL+QingKe+HuangYou&display=swap");
.showcase {position: relative; display: grid; place-items: center; margin: 30px 0; width: 1280px; height: 750px; left: calc(50% - 81px); transform: translateX(-50%); box-shadow: 3px 3px 6px gray; overflow: hidden; z-index: 1; background: #000 url('shturl.cc/Xl44XwvECfb2xPoI0GdxSCpwpGKkBJg5Myo') no-repeat center/cover; font-family: "ZCOOL KuaiLe","Ma Shan Zheng","Long Cang","华文行楷","SimHei","Arial","sans-serif";}
.bg-canvas {position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: cover; z-index: 0; pointer-events: none;}
#spectrumCanvas {position: absolute; bottom: 0; left: 0; width: 100%; height: 100%; z-index: 2; display: block; background: transparent; pointer-events: none;}
#lyricCanvas {position: absolute; top: 300px; left: 50%; transform: translateX(-50%); width: 800px; height: 140px; z-index: 15; background: transparent; pointer-events: auto;}
#player {position: absolute; z-index: 6; right: 28px; top: 20px; width: 200px; height: 200px; cursor: pointer; transition: transform 0.3s ease, background 0.3s ease; display: flex; align-items: center; justify-content: center;}
@keyframes float {0%{transform: translateY(0);} 100%{transform: translateY(-20px);}}
#player.playing {animation: float 3s ease-in-out infinite alternate;}
.time-display {position: absolute; top: 180px; left: calc(50%); transform: translateX(-50%); padding: 6px 18px; font-size: 18px; font-weight: bold; color: #e6f7ff; letter-spacing: 1px; z-index: 16; pointer-events: none; font-family: monospace; white-space: nowrap; text-shadow: 1px 1px 0 #003366, -1px -1px 0 #003366, 1px -1px 0 #003366, -1px 1px 0 #003366;}
.song-title {position: absolute; top: 70px; left: calc(50% - 200px); transform: translateX(-50%); width: 30%; height: 10%; color: #ff3333; font-family: 'Microsoft YaHei', "华文新魏", cursive; font-size: 5rem; margin-bottom: 20px; letter-spacing: 6px; z-index: 2; transform: perspective(800px) rotateY(-20deg); text-shadow: 1px 1px 0 #ffffff, 2px 2px 0 #ffffff, 3px 3px 0 #ffffff; background: none; border: none; box-shadow: none; padding: 0; }
.dancer {position: relative; mix-blend-mode: screen; z-index: 6; }
.vid1 {position: absolute; z-index: 1; left: 0%; top: 0%; width: 100%; height: 100%; object-fit: cover; pointer-events: none; object-position: center; will-change: transform;}
.vid2 {position: absolute; z-index: 1; left: 0%; top: 0%; width: 100%; height: 100%; object-fit: cover; pointer-events: none; object-position: center; mix-blend-mode: screen; will-change: transform; opacity: .1;}
#fullscreen {border-radius: 4px; position: absolute; color: DarkGray; padding: 4px 8px; z-index: 12; font-size: 25px; border: none; cursor: pointer; top: 25px; left: 25px; background: transparent;}
#fullscreen:hover {font-style: italic;}
</style>
<div class="showcase" id="showcase">
<div id="fullscreen">全屏欣赏</div>
<div class="song-title">美丽中国</div>
<canvas id="bgCanvas" class="bg-canvas"></canvas>
<canvas id="spectrumCanvas"></canvas>
<canvas id="lyricCanvas" width="800" height="160" style="width:800px; height:160px;"></canvas>
<video class="vid1" src="https://i.uik.cc/view.php/350c657ead14f6d252e3ba9d90ff0a4e.mp4" preload="auto" autoplay loop playsinline muted></video>
<video class="vid2" src="https://wj.zp68.com/lxx/yunhua/2025/09/30/24.mp4" preload="auto" autoplay loop playsinline muted></video>
<img class="dancer" id="player" data-gif="https://pic1.imgdb.cn/item/6a2b701e240596c6e205bda9.gif" data-png="https://pic1.imgdb.cn/item/6a2b7067240596c6e205be33.png" src="https://pic1.imgdb.cn/item/6a2b701e240596c6e205bda9.gif" title = "播放/暂停">
<div class="time-display" id="timeDisplay">0:00 / 0:00</div>
</div>
<script>
(function() {
const CANVAS_W = 1280;
const CANVAS_H = 750;
const AUDIO_LOCAL = '';
const AUDIO_FALLBACK = 'http://music.163.com/song/media/outer/url?id=28465276.mp3';
const FILL_VIDEO_URL = 'https://img2.tukuppt.com/video_show/2421007/00/01/57/5b46335976efe.mp4';
const FILL_VIDEO_FALLBACK = 'https://img2.tukuppt.com/video_show/2421007/00/01/57/5b46335976efe.mp4';
const TOTAL_FRAMES = 24;
const FRAME_DURATION = 0.1;
const FRAME_BASE_PATH = 'https://img-baofun.zhhainiao.com/pcwallpaper_ugc/preview/4ac300b8cc4fe31f96e6d2d0109832f3_preview.webp';
const FRAME_EXT = 'https://img-baofun.zhhainiao.com/pcwallpaper_ugc/preview/4ac300b8cc4fe31f96e6d2d0109832f3_preview.webp';
const vids = document.querySelectorAll('video');
const fullscreen = document.getElementById('fullscreen');
const papa = document.getElementById('showcase');
function getFrameUrl(frameNumber) {
const padded = frameNumber.toString().padStart(2, '0');
return `${FRAME_BASE_PATH}frame_${padded}_delay-0.1s${FRAME_EXT}`;
}
let bgFrames = [];
let bgFramesLoaded = false;
let bgCanvas = document.getElementById('bgCanvas');
let bgCtx = bgCanvas.getContext('2d');
let bgAnimationId = null;
function loadBackgroundFrames() {
let loadedCount = 0;
let failedCount = 0;
for (let i = 0; i < TOTAL_FRAMES; i++) {
const img = new Image();
img.onload = () => {
loadedCount++;
if (loadedCount + failedCount === TOTAL_FRAMES) {
bgFramesLoaded = true;
drawBackgroundFrame(0);
if (isPlaying && !bgAnimationId) {
startBackgroundAnimation();
}
}
};
img.onerror = () => {
failedCount++;
bgFrames = null;
if (loadedCount + failedCount === TOTAL_FRAMES) {
bgFramesLoaded = true;
drawBackgroundFrame(0);
if (isPlaying && !bgAnimationId) {
startBackgroundAnimation();
}
}
};
img.src = getFrameUrl(i);
bgFrames.push(img);
}
}
function drawBackgroundFrame(frameIndex) {
if (!bgFramesLoaded) return;
const w = bgCanvas.clientWidth;
const h = bgCanvas.clientHeight;
if (bgCanvas.width !== w || bgCanvas.height !== h) {
bgCanvas.width = w;
bgCanvas.height = h;
}
let img = bgFrames;
let actualIndex = frameIndex;
while (actualIndex >= 0 && (!img || !img.complete || img.naturalWidth === 0)) {
actualIndex--;
img = actualIndex >= 0 ? bgFrames : null;
}
if (!img || !img.complete) return;
const imgW = img.naturalWidth;
const imgH = img.naturalHeight;
const scale = Math.max(w / imgW, h / imgH);
const drawW = imgW * scale;
const drawH = imgH * scale;
const dx = (w - drawW) / 2;
const dy = (h - drawH) / 2;
bgCtx.drawImage(img, dx, dy, drawW, drawH);
}
function getFrameIndexFromTime(currentTime) {
const totalCycle = TOTAL_FRAMES * FRAME_DURATION;
const elapsed = currentTime % totalCycle;
let idx = Math.floor(elapsed / FRAME_DURATION);
if (idx >= TOTAL_FRAMES) idx = TOTAL_FRAMES - 1;
return idx;
}
function startBackgroundAnimation() {
if (bgAnimationId) cancelAnimationFrame(bgAnimationId);
function animate() {
if (!isPlaying) {
bgAnimationId = requestAnimationFrame(animate);
return;
}
if (!bgFramesLoaded) {
bgAnimationId = requestAnimationFrame(animate);
return;
}
const current = audioElement ? audioElement.currentTime : 0;
const idx = getFrameIndexFromTime(current);
drawBackgroundFrame(idx);
bgAnimationId = requestAnimationFrame(animate);
}
animate();
}
function stopBackgroundAnimation() {
if (bgAnimationId) {
cancelAnimationFrame(bgAnimationId);
bgAnimationId = null;
}
if (bgFramesLoaded && audioElement) {
const idx = getFrameIndexFromTime(audioElement.currentTime);
drawBackgroundFrame(idx);
}
}
const spectrumCanvas = document.getElementById('spectrumCanvas');
const ctx = spectrumCanvas.getContext('2d');
const playerDiv = document.getElementById('player');
const timeDisplay = document.getElementById('timeDisplay');
const showcase = document.getElementById('showcase');
function setButtonState(playing) {
if (playing) {
playerDiv.classList.add('playing');
} else {
playerDiv.classList.remove('playing');
}
}
const BAR_COUNT = 25;
const BAR_SPACING = 25;
const FFT_SIZE = 64;
let maxBarHeight = 80;
let bottomBaseY = 0;
let canvasWidth = CANVAS_W, canvasHeight = CANVAS_H;
let barWidth = 0;
let audioContext = null;
let audioElement = null;
let sourceNode = null;
let analyserNode = null;
let spectrumAnimationId = null;
let isPlaying = false;
let frequencyArray = new Uint8Array(FFT_SIZE / 2);
let fillVideo = null;
let fillVideoReady = false;
let offscreenCanvas = null;
let offscreenCtx = null;
let videoDimensionsReady = false;
let lastTextureUpdate = 0;
const TEXTURE_UPDATE_INTERVAL = 50;
let spectrumTime = 0;
const SPECTRUM_SPEED = 0.022;
let isAudioInitializing = false, audioInitialized = false;
let pauseBarHeights = [];
function getBarHeights() {
spectrumTime += SPECTRUM_SPEED;
if (spectrumTime > Math.PI*2) spectrumTime -= Math.PI*2;
const minHeight = 8;
const range = maxBarHeight - minHeight;
const amp = 0.7;
const heights = new Array(BAR_COUNT);
for (let i = 0; i < BAR_COUNT; i++) {
const t = i / BAR_COUNT;
const val = (Math.sin(spectrumTime * 1.6 + t * Math.PI * 2.7) + 1) / 2;
const val2 = (Math.sin(spectrumTime * 1.2 + t * Math.PI * 3.4) + 1) / 2;
const mix = (val * 0.7 + val2 * 0.3) * amp;
const h = minHeight + mix * range;
heights = Math.min(maxBarHeight, Math.max(minHeight, h));
}
return heights;
}
function initFillVideo() {
if (fillVideo) return;
const video = document.createElement('video');
video.loop = true; video.muted = true; video.autoplay = true; video.playsInline = true;
video.src = FILL_VIDEO_URL;
video.addEventListener('loadedmetadata', () => {
if (video.videoWidth > 0) {
offscreenCanvas = document.createElement('canvas');
offscreenCanvas.width = video.videoWidth;
offscreenCanvas.height = video.videoHeight;
offscreenCtx = offscreenCanvas.getContext('2d');
videoDimensionsReady = true;
}
});
video.addEventListener('canplaythrough', () => { fillVideoReady = true; video.play().catch(()=>{}); });
video.addEventListener('error', () => { video.src = FILL_VIDEO_FALLBACK; video.load(); });
fillVideo = video;
video.load();
}
function updateTextureIfNeeded(now) {
if (!isPlaying) return false;
if (!fillVideo || fillVideo.readyState < 2) return false;
if (!offscreenCtx || !videoDimensionsReady) return false;
if (now - lastTextureUpdate >= TEXTURE_UPDATE_INTERVAL) {
try {
offscreenCtx.drawImage(fillVideo, 0, 0);
lastTextureUpdate = now;
return true;
} catch(e) {}
}
return false;
}
function drawBarsWithVideo(heights, startX, nowTime) {
if (!offscreenCtx || !videoDimensionsReady || !fillVideoReady) {
drawSolidFallback(heights, startX);
return;
}
updateTextureIfNeeded(nowTime);
const vw = offscreenCanvas.width, vh = offscreenCanvas.height;
const sliceW = vw / BAR_COUNT;
for (let i = 0; i < BAR_COUNT; i++) {
const x = startX + i * (barWidth + BAR_SPACING);
const h = heights;
const y = bottomBaseY - h;
if (h <= 4) continue;
const srcX = i * sliceW;
ctx.save();
ctx.beginPath();
ctx.rect(x, y, barWidth, h);
ctx.clip();
try { ctx.drawImage(offscreenCanvas, srcX, 0, sliceW, vh, x, y, barWidth, h); } catch(e) {}
ctx.restore();
ctx.strokeStyle = '#b0f0ff';
ctx.lineWidth = 1;
ctx.strokeRect(x, y, barWidth, h);
ctx.beginPath();
ctx.moveTo(x+3, y);
ctx.lineTo(x+barWidth-3, y);
ctx.lineTo(x+barWidth-6, y+3);
ctx.lineTo(x+6, y+3);
ctx.fillStyle = 'DeepSkyBlue';
ctx.fill();
}
}
function drawSolidFallback(heights, startX) {
for (let i = 0; i < BAR_COUNT; i++) {
const x = startX + i * (barWidth + BAR_SPACING);
const h = heights;
const y = bottomBaseY - h;
if (h <= 4) continue;
const grad = ctx.createLinearGradient(x, y, x+barWidth, y+h);
grad.addColorStop(0, '#1287ae');
grad.addColorStop(1, '#064e66');
ctx.fillStyle = grad;
ctx.fillRect(x, y, barWidth, h);
ctx.strokeStyle = '#8fd6ff';
ctx.strokeRect(x, y, barWidth, h);
ctx.beginPath();
ctx.moveTo(x+3, y);
ctx.lineTo(x+barWidth-3, y);
ctx.lineTo(x+barWidth-6, y+5);
ctx.lineTo(x+6, y+5);
ctx.fillStyle = '#fff6cf';
ctx.fill();
}
}
function renderSpectrum() {
const now = performance.now();
let heights = isPlaying ? getBarHeights() : [...pauseBarHeights];
if (!isPlaying && pauseBarHeights.length === 0) {
heights = getBarHeights();
pauseBarHeights = [...heights];
}
if(isPlaying) pauseBarHeights = [...heights];
const totalW = BAR_COUNT * barWidth + BAR_SPACING * (BAR_COUNT - 1);
let startX = (canvasWidth - totalW) / 2;
if (startX < 12) startX = 12;
const clearTop = bottomBaseY - maxBarHeight - 10;
const clearHeight = maxBarHeight + 20;
if (clearTop >= 0 && clearHeight > 0) {
ctx.clearRect(0, clearTop, canvasWidth, clearHeight);
}
drawBarsWithVideo(heights, startX, now);
ctx.fillStyle = 'rgba(70, 180, 210, 0.2)';
for (let i = 0; i < BAR_COUNT; i++) {
let x = startX + i * (barWidth + BAR_SPACING);
ctx.fillRect(x, bottomBaseY - 2, barWidth, 1);
}
spectrumAnimationId = requestAnimationFrame(renderSpectrum);
}
function startSpectrumLoop() {
if (spectrumAnimationId) cancelAnimationFrame(spectrumAnimationId);
renderSpectrum();
}
function updateDimensions() {
canvasWidth = spectrumCanvas.width;
canvasHeight = spectrumCanvas.height;
bottomBaseY = canvasHeight - 15;
const totalSpacing = BAR_SPACING * (BAR_COUNT - 1);
const avail = canvasWidth - totalSpacing - 40;
barWidth = 20;
}
window.addEventListener('resize', () => {
updateDimensions();
if (!isPlaying && bgFramesLoaded && audioElement) {
const idx = getFrameIndexFromTime(audioElement.currentTime);
drawBackgroundFrame(idx);
}
});
const lyricCanvas = document.getElementById('lyricCanvas');
let gl = null;
let webglProgram = null;
let webglTexture = null;
let webglUniforms = {};
let currentLyricIndex = -1;
let webglTimeAcc = 0;
let lyricRenderId = null;
const lrcText = `
美丽中国 - 陈思思
词:孟文豪
曲:孟文豪
壮美的北国
青山绿水如此辽阔
蓝天上白云朵朵
每寸土地都是爱的寄托
美丽中国美丽中国
绿意融融生机勃勃
处处都是绚丽的春色
美丽中国美丽中国
尊重自然坚守承诺
一起收获美好的生活
秀丽的南国
绿树红花如此婀娜
南湖上荡起碧波
每个孩子都唱起欢歌
美丽中国美丽中国
绿意融融生机勃勃
处处都是绚丽的春色
美丽中国美丽中国
尊重自然坚守承诺
一起收获美好的生活
美丽中国美丽中国
绿意融融生机勃勃
处处都是绚丽的春色
美丽中国美丽中国
尊重自然坚守承诺
一起收获美好的生活
我的话要对你诉说
我最美丽的祖国
`;
const lyricsData = [];
lrcText.trim().split('\n').forEach(line => {
const match = line.match(/^\[(\d+):(\d+\.\d+)\](.*)$/);
if (match) {
const m = parseFloat(match);
const s = parseFloat(match);
const time = m * 60 + s;
const text = match.trim();
lyricsData.push({ time, text });
}
});
function initWebGLLyric() {
if (!lyricCanvas) return;
gl = lyricCanvas.getContext('webgl') || lyricCanvas.getContext('experimental-webgl');
if (!gl) return;
const vsSource = `
attribute vec2 a_pos;
attribute vec2 a_uv;
varying vec2 v_uv;
void main() {
v_uv = a_uv;
gl_Position = vec4(a_pos, 0.0, 1.0);
}
`;
const fsSource = `
precision highp float;
varying vec2 v_uv;
uniform sampler2D u_text;
uniform float u_time;
uniform vec2 u_mouse;
uniform float u_hover;
void main() {
vec2 uv = vec2(v_uv.x, 1.0 - v_uv.y);
float waveX = sin(uv.y * 10.0 + u_time * 1.2) * 0.008;
float waveY = cos(uv.x * 8.0 - u_time * 1.0) * 0.006;
vec2 distorted = uv + vec2(waveX, waveY);
float dist = distance(distorted, u_mouse);
if (u_hover > 0.1 && dist < 0.25) {
float off = sin(dist * 15.0 - u_time * 6.0) * 0.008 * u_hover;
distorted += normalize(distorted - u_mouse) * off;
}
vec4 col = texture2D(u_text, distorted);
if (col.a < 0.05) discard;
float glow = smoothstep(0.3, 0.9, col.a);
col.rgb = mix(col.rgb, vec3(1.0, 0.96, 0.88), glow * 0.6);
if (u_hover > 0.1 && dist < 0.2) {
float halo = (1.0 - smoothstep(0.0,0.2,dist)) * u_hover * 0.5;
col.rgb += vec3(0.5, 0.3, 0.1) * halo;
}
gl_FragColor = col;
}
`;
function compileShader(src, type) {
const shader = gl.createShader(type);
gl.shaderSource(shader, src);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) return null;
return shader;
}
const vs = compileShader(vsSource, gl.VERTEX_SHADER);
const fs = compileShader(fsSource, gl.FRAGMENT_SHADER);
const prog = gl.createProgram();
gl.attachShader(prog, vs);
gl.attachShader(prog, fs);
gl.linkProgram(prog);
webglProgram = prog;
const vertices = new Float32Array([
-1, -1, 0, 0,
1, -1, 1, 0,
-1,1, 0, 1,
1,1, 1, 1
]);
const vbo = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
const a_pos = gl.getAttribLocation(prog, 'a_pos');
const a_uv = gl.getAttribLocation(prog, 'a_uv');
gl.enableVertexAttribArray(a_pos);
gl.vertexAttribPointer(a_pos, 2, gl.FLOAT, false, 16, 0);
gl.enableVertexAttribArray(a_uv);
gl.vertexAttribPointer(a_uv, 2, gl.FLOAT, false, 16, 8);
webglUniforms = {
u_time: gl.getUniformLocation(prog, 'u_time'),
u_text: gl.getUniformLocation(prog, 'u_text'),
u_mouse: gl.getUniformLocation(prog, 'u_mouse'),
u_hover: gl.getUniformLocation(prog, 'u_hover')
};
webglTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, webglTexture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
const textCanvas = document.createElement('canvas');
textCanvas.width = lyricCanvas.width;
textCanvas.height = lyricCanvas.height;
const tctx = textCanvas.getContext('2d');
function updateTextTexture(text) {
tctx.clearRect(0, 0, textCanvas.width, textCanvas.height);
tctx.letterSpacing = "4px";
tctx.font = '65px "Long Cang","Ma Shan Zheng","ZCOOL+KuaiLe",KaiTi,STKaiti,SimHei,cursive, serif,"Microsoft YaHei","Segoe UI", "Liu Jian Mao Cao","ZCOOL QingKe HuangYou",Poppins, system-ui';
tctx.textAlign = 'center';
tctx.textBaseline = 'middle';
tctx.lineWidth = 2;
tctx.strokeStyle = '#F8F3EB';
tctx.shadowColor = "#00BBFF";
tctx.shadowBlur = 1;
tctx.strokeText(text, textCanvas.width/2, textCanvas.height/2);
tctx.fillStyle = '#BBFF00';
tctx.fillText(text, textCanvas.width/2, textCanvas.height/2);
gl.bindTexture(gl.TEXTURE_2D, webglTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, textCanvas);
}
let mouseX = 0.5, mouseY = 0.5, hoverValue = 0;
lyricCanvas.addEventListener('mousemove', (e) => {
const rect = lyricCanvas.getBoundingClientRect();
mouseX = (e.clientX - rect.left) / rect.width;
mouseY = 1.0 - (e.clientY - rect.top) / rect.height;
hoverValue = Math.min(1.0, hoverValue + 0.06);
});
lyricCanvas.addEventListener('mouseleave', () => { hoverValue = 0; });
let startTime = performance.now() / 1000;
function renderLoop() {
if (!gl || !webglProgram) return;
if (isPlaying) {
const now = performance.now() / 1000;
const delta = now - startTime;
webglTimeAcc += delta;
startTime = now;
} else {
startTime = performance.now() / 1000;
}
const nowTime = webglTimeAcc;
gl.viewport(0, 0, lyricCanvas.width, lyricCanvas.height);
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(webglProgram);
gl.uniform1f(webglUniforms.u_time, nowTime);
gl.uniform2f(webglUniforms.u_mouse, mouseX, mouseY);
gl.uniform1f(webglUniforms.u_hover, hoverValue);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, webglTexture);
gl.uniform1i(webglUniforms.u_text, 0);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
lyricRenderId = requestAnimationFrame(renderLoop);
}
renderLoop();
return { updateTextTexture };
}
let webglUpdater = null;
function syncLyricByTime(currentTime) {
let idx = 0;
for (let i = lyricsData.length-1; i>=0; i--) {
if (currentTime >= lyricsData.time) { idx = i; break; }
}
if (currentTime >= lyricsData.time) idx = lyricsData.length-1;
if (idx !== currentLyricIndex) {
currentLyricIndex = idx;
if (webglUpdater) webglUpdater.updateTextTexture(lyricsData.text);
}
}
function formatTime(sec) {
if (isNaN(sec)) sec = 0;
const mins = Math.floor(sec / 60);
const seconds = Math.floor(sec % 60);
return `${mins}:${seconds < 10 ? '0' : ''}${seconds}`;
}
function updateTimeDisplay() {
if (!audioElement) return;
const current = audioElement.currentTime;
const duration = audioElement.duration || 0;
timeDisplay.textContent = `${formatTime(current)} / ${formatTime(duration)}`;
syncLyricByTime(current);
}
function syncDancers(dancer) {
const list = document.querySelectorAll('.dancer');
list.forEach(el => {
if(el.dataset.gif && el.dataset.png){
el.src = dancer ? el.dataset.png : el.dataset.gif;
}
});
}
async function initAudio() {
if (audioInitialized || isAudioInitializing) return;
isAudioInitializing = true;
try {
audioElement = new Audio();
audioElement.src = AUDIO_LOCAL;
audioElement.preload = 'auto';
audioElement.loop = true;
audioElement.addEventListener('ended', () => {
isPlaying = false;
setButtonState(false);
syncDancers(true);
audioElement.currentTime = 0;
currentLyricIndex = -1;
if (webglUpdater) webglUpdater.updateTextTexture(lyricsData.text);
updateTimeDisplay();
stopBackgroundAnimation();
});
let hasSwitch = false;
audioElement.addEventListener('error', () => {
if (!hasSwitch) {
hasSwitch = true;
audioElement.src = AUDIO_FALLBACK;
audioElement.load();
}
});
await new Promise((resolve, reject) => {
audioElement.addEventListener('canplaythrough', resolve, { once: true });
audioElement.addEventListener('error', reject, { once: true });
audioElement.load();
});
audioContext = new (window.AudioContext || window.webkitAudioContext)();
await audioContext.suspend();
analyserNode = audioContext.createAnalyser();
analyserNode.fftSize = FFT_SIZE;
analyserNode.smoothingTimeConstant = 0.68;
frequencyArray = new Uint8Array(analyserNode.frequencyBinCount);
sourceNode = audioContext.createMediaElementSource(audioElement);
sourceNode.connect(analyserNode);
analyserNode.connect(audioContext.destination);
audioInitialized = true;
updateTimeDisplay();
} catch (err) {
console.error("音频初始化失败", err);
} finally {
isAudioInitializing = false;
}
}
async function startPlayback() {
if (!audioInitialized) await initAudio().then(() => _start());
else _start();
async function _start() {
if (!audioElement) return;
if (isPlaying && !audioElement.paused) return;
try {
if (audioContext && audioContext.state === 'suspended') {
await audioContext.resume();
}
await audioElement.play();
isPlaying = true;
setButtonState(true);
syncDancers(false);
if (!bgAnimationId && bgFramesLoaded) startBackgroundAnimation();
if (window.timeInterval) clearInterval(window.timeInterval);
window.timeInterval = setInterval(() => {
if (isPlaying && audioElement) updateTimeDisplay();
}, 200);
} catch (err) {
console.warn("播放失败", err);
isPlaying = false;
setButtonState(false);
syncDancers(true);
}
}
}
function pausePlayback() {
if (audioElement && !audioElement.paused) {
audioElement.pause();
isPlaying = false;
setButtonState(false);
syncDancers(true);
if (window.timeInterval) clearInterval(window.timeInterval);
stopBackgroundAnimation();
}
}
function togglePlayback() {
vids.forEach(vid => !audioElement.paused ? vid.pause() : vid.play());
if (isPlaying && audioElement && !audioElement.paused) {
pausePlayback();
} else {
startPlayback();
}
}
async function attemptAutoPlay() {
await initAudio();
startSpectrumLoop();
try {
if (audioContext && audioContext.state === 'suspended') await audioContext.resume();
await audioElement.play();
isPlaying = true;
setButtonState(true);
syncDancers(false);
if (!bgAnimationId && bgFramesLoaded) startBackgroundAnimation();
if (window.timeInterval) clearInterval(window.timeInterval);
window.timeInterval = setInterval(() => {
if (isPlaying && audioElement) updateTimeDisplay();
}, 200);
} catch (e) {
setButtonState(false);
isPlaying = false;
syncDancers(true);
}
}
let fs = true,fsTimer;
fullscreen.onclick = ()=>{
if(fs){
fullscreen.innerText = '退出全屏';
papa.requestFullscreen();
}else{
fullscreen.innerText = '全屏欣赏';
document.exitFullscreen();
}
fs = !fs;
};
papa.addEventListener('mousemove', () => {
clearTimeout(fsTimer);
fullscreen.style.opacity = '1';
fsTimer = setTimeout(() => fullscreen.style.opacity = '0', 2500);
});
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
if (!audioElement.paused) audioElement.pause();
} else {
if (audioElement.paused && !audioElement.ended) audioElement.play().catch(()=>{});
}
});
['contextmenu', 'dragstart', 'selectstart'].forEach(ev =>
papa.addEventListener(ev, (e) => { e.preventDefault(); })
);
async function init() {
spectrumCanvas.width = CANVAS_W;
spectrumCanvas.height = CANVAS_H;
bgCanvas.width = CANVAS_W;
bgCanvas.height = CANVAS_H;
updateDimensions();
initFillVideo();
loadBackgroundFrames();
await initAudio();
const webglObj = initWebGLLyric();
if (webglObj) webglUpdater = webglObj;
if (webglUpdater) webglUpdater.updateTextTexture(lyricsData.text);
playerDiv.addEventListener('click', togglePlayback);
vids.forEach(vid => !audioElement.paused ? vid.pause() : vid.play());
setButtonState(false);
isPlaying = false;
syncDancers(true);
attemptAutoPlay();
}
init();
})();
</script>
</body>
</html>
代码来自了了子老师和小辣椒管理员分享作品,在此一并表示感谢~ 没法启动啊{:5_102:} 点击不响应,看看那里不合适了{:4_204:}{:4_190:}
也曾年轻 发表于 2026-6-12 21:54
没法启动啊
预览正常,发在论坛就不启动了,不知其因 霜染枫丹 发表于 2026-6-12 21:56
点击不响应,看看那里不合适了
是,还是发网页空间帖吧 把代码拿到本地看,很漂亮的一个帖子 杨帆 发表于 2026-6-12 21:59
预览正常,发在论坛就不启动了,不知其因
用电脑上的代码覆盖论坛上的代码重新发一次,并且
把这两个选项也勾选上
漂亮的海浪频谱和波浪的歌词同步,这个制作真漂亮。
欣赏杨帆好帖{:4_187:} 也曾年轻 发表于 2026-6-12 22:24
用电脑上的代码覆盖论坛上的代码重新发一次,并且
没用过呢,谢谢年轻老师{:4_190:} 红影 发表于 2026-6-12 22:35
漂亮的海浪频谱和波浪的歌词同步,这个制作真漂亮。
欣赏杨帆好帖
鼓捣着玩呢,谢谢影子鼓励{:4_187:} 也曾年轻 发表于 2026-6-12 22:18
把代码拿到本地看,很漂亮的一个帖子
谢谢老师鼓励,祝开心{:4_190:} 也曾年轻 发表于 2026-6-12 22:24
用电脑上的代码覆盖论坛上的代码重新发一次,并且
老师威武,果真管用,由衷感谢{:4_180:} 我这看vid1那视频没显示出来,大概因为不是https的原因吧。 我这里看到是波浪式的歌词和下面波浪形的频谱,背影是纯黑色的,标题字红色的,还有一白鸽。{:4_187:} 也曾年轻 发表于 2026-6-13 07:33
我这看vid1那视频没显示出来,大概因为不是https的原因吧。
刚处理,老师现在能看到吗{:4_190:} 梦江南 发表于 2026-6-13 07:48
我这里看到是波浪式的歌词和下面波浪形的频谱,背影是纯黑色的,标题字红色的,还有一白鸽。
问好江南,我这显示正常,还看不到的话,就再传一下试试{:4_187:} 杨帆 发表于 2026-6-13 09:18
问好江南,我这显示正常,还看不到的话,就再传一下试试
可能是我的电脑问题,不用辛苦了。 杨帆 发表于 2026-6-13 09:15
刚处理,老师现在能看到吗
<video class="vid1" src="http://121.40.101.60/video/mp3/2025/12/jizhi.mp4" preload="auto" autoplay loop playsinline muted></video>
是这个视频没显示, 它采用的是http协议 杨帆 发表于 2026-6-12 23:34
鼓捣着玩呢,谢谢影子鼓励
这个代码真的很不错{:4_187:}