杨帆 发表于 2026-5-8 10:11

一路繁花为我开

<!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>
</head>
<body>
<style>
#papa{margin:130px 0 20px calc(50% - 781px);width:1400px;height:850px;background:lightblue url('https://cccimg.com/view.php/2c2334bf5d7804d9ca358b78bbe921e0.gif') no-repeat center/cover;box-shadow:3px 3px 6px gray;z-index:1;overflow:hidden;user-select:none;display:grid;place-items:center;position:relative;--state:running;border-radius:32px;}
zxx-slide{display:block;width:100%;height:100%;position:absolute;}
.zxx-slide-a{width:100%;height:110%;position:absolute;bottom:-50px;display:none;}
.zxx-slide-a.in{z-index:1;}
.zxx-slide-img{width:100%;height:100%;object-fit:cover;pointer-events:none;display:block;}
.zxx-slide-index-x{position:absolute;left:0px;right:0;bottom:0px;text-align:center;font-size:0;z-index:1;}
@keyframes seed{0%{--seed:0}1%{--seed:1}2%{--seed:2}3%{--seed:3}4%{--seed:4}5%{--seed:5}6%{--seed:6}7%{--seed:7}8%{--seed:8}9%{--seed:9}10%{--seed:10}11%{--seed:11}12%{--seed:12}13%{--seed:13}14%{--seed:14}15%{--seed:15}16%{--seed:16}17%{--seed:17}18%{--seed:18}19%{--seed:19}20%{--seed:20}21%{--seed:21}22%{--seed:22}23%{--seed:23}24%{--seed:24}25%{--seed:25}26%{--seed:26}27%{--seed:27}28%{--seed:28}29%{--seed:29}30%{--seed:30}31%{--seed:31}32%{--seed:32}33%{--seed:33}34%{--seed:34}35%{--seed:35}36%{--seed:36}37%{--seed:37}38%{--seed:38}39%{--seed:39}40%{--seed:40}41%{--seed:41}42%{--seed:42}43%{--seed:43}44%{--seed:44}45%{--seed:45}46%{--seed:46}47%{--seed:47}48%{--seed:48}49%{--seed:49}50%{--seed:50}51%{--seed:51}52%{--seed:52}53%{--seed:53}54%{--seed:54}55%{--seed:55}56%{--seed:56}57%{--seed:57}58%{--seed:58}59%{--seed:59}60%{--seed:60}61%{--seed:61}62%{--seed:62}63%{--seed:63}64%{--seed:64}65%{--seed:65}66%{--seed:66}67%{--seed:67}68%{--seed:68}69%{--seed:69}70%{--seed:70}71%{--seed:71}72%{--seed:72}73%{--seed:73}74%{--seed:74}75%{--seed:75}76%{--seed:76}77%{--seed:77}78%{--seed:78}79%{--seed:79}80%{--seed:80}81%{--seed:81}82%{--seed:82}83%{--seed:83}84%{--seed:84}85%{--seed:85}86%{--seed:86}87%{--seed:87}88%{--seed:88}89%{--seed:89}90%{--seed:90}91%{--seed:91}92%{--seed:92}93%{--seed:93}94%{--seed:94}95%{--seed:95}96%{--seed:96}97%{--seed:97}98%{--seed:98}99%{--seed:99}100%{--seed:100}}
.zxx-slide-a.effect-round{-webkit-mask:radial-gradient(#000 calc(1% * var(--seed)), transparent calc(1% * var(--seed)));-webkit-mask-size:50px 50px;mask:radial-gradient(#000 calc(1% * var(--seed)), transparent calc(1% * var(--seed)));mask-size:50px 50px;animation:seed 1s ease-out;}
.zxx-slide-a.effect-topdown{-webkit-mask:linear-gradient(to bottom, #000 calc(1% * var(--seed)), transparent calc(1% * var(--seed)));mask:linear-gradient(to bottom, #000 calc(1% * var(--seed)), transparent calc(1% * var(--seed)));-webkit-mask-size:100% 100%;mask-size:100% 100%;animation:seed 1s ease-out;}
@keyframes fadeIn{0%{opacity:0;}100%{opacity:1;}}
.zxx-slide-a.effect-fade{mask:none;-webkit-mask:none;animation:fadeIn 1s ease-out forwards;}
@keyframes blurToClear{from{filter:blur(20px);opacity:0;}to{filter:blur(0);opacity:1;}}
.zxx-slide-a.effect-clear{mask:none;-webkit-mask:none;animation:blurToClear 1.5s ease-out forwards;}
.block-effect-wrapper{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10;display:grid;pointer-events:none;}
.dissolve-wrapper{grid-template-columns:repeat(30, 1fr);grid-template-rows:repeat(20, 1fr);}
.pixelate-wrapper{grid-template-columns:repeat(40, 1fr);grid-template-rows:repeat(30, 1fr);}
.mosaic-wrapper{grid-template-columns:repeat(15, 1fr);grid-template-rows:repeat(10, 1fr);}
.block-piece{background-color:#006400;transition:all 1.2s;opacity:0.6;border-radius:2%;transform:scale(1);box-shadow:0px 0px 0px 2px #ffffff;}
.shatter-wrapper{perspective:1500px;position:absolute;top:0;left:0;width:100%;height:100%;z-index:10;pointer-events:none;}
.shatter-piece{position:absolute;background:no-repeat center/cover;transition:transform 1.4s, opacity 1s;transform-origin:center;opacity:1;}
@keyframes blockHide{to{transform:scale(0);opacity:0;}}
.zxx-slide-a.effect-dissolve .block-piece,.zxx-slide-a.effect-pixelate .block-piece,.zxx-slide-a.effect-mosaic .block-piece{animation:blockHide 1.2s ease-out forwards;}
.zxx-slide-a.effect-shatter .shatter-piece{transform:translate3d(100px, -100px, -500px) rotate(180deg);opacity:0;}
.lyrics{margin:0;z-index:20;top:92%;left:50%;transform:translate(-50%, -50%);height:100px;text-align:center;position:absolute;}
.lyric-line{width:100%;position:relative;height:60px;overflow:visible;font:300 50px '华文行楷', sans-serif;line-height:60px;text-align:left;white-space:nowrap;filter:drop-shadow(#fff 1px 0 0) drop-shadow(#fff 0 1px 0) drop-shadow(#fff -1px 0 0) drop-shadow(#fff 0 -1px 0);}
.lyric-mask{position:absolute;color:transparent;background:linear-gradient(90deg,cyan,teal);background-clip:text;-webkit-background-clip:text;text-shadow:1px 1px 1px rgba(0,0,0,.45);white-space:nowrap;}
.lyric-original{color:#ADFF2F;white-space:nowrap;}
#fullscreen{position:absolute;top:30px;left:30px;font:normal 1.5em 楷体;color:#fff;text-shadow:0 0 3px #000;opacity:1;cursor:pointer;user-select:none;z-index:8;}
#fullscreen:hover{font-style:italic;}
.spectrum-player{position:absolute;top:4%;right:2%;z-index:30;width:110px;height:110px;cursor:pointer;user-select:none;filter:none;box-shadow:none;}
.progress-ring-svg{position:absolute;top:0;left:0;width:100%;height:100%;transform:rotate(-90deg);filter:none;}
.progress-ring-circle{fill:none;stroke:#ffd700;stroke-width:2.5;stroke-linecap:round;stroke-dasharray:238.76;stroke-dashoffset:238.76;transition:stroke-dashoffset 0.08s linear;}
#fireworksCanvas{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:31;}
.spectrum-dots{position:absolute;width:100%;height:100%;top:0;left:0;pointer-events:none;transform:translate(-3px, -3px);}
.spectrum-dot{position:absolute;width:6px;height:6px;border-radius:50%;background:#ffaa33;box-shadow:0 0 4px #ffcc55, 0 0 2px #ffaa44;transform-origin:center;transition:transform 0.05s linear, background 0.05s linear, box-shadow 0.05s ease;will-change:transform, background, box-shadow;}
.center-btn{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);width:70px;height:70px;border-radius:50%;background:rgba(0, 0, 0, 0.45);backdrop-filter:blur(8px);border:1px solid rgba(255, 215, 0, 0.9);display:flex;flex-direction:column;align-items:center;justify-content:center;cursor:pointer;transition:all 0.2s;z-index:5;font-family:'Microsoft YaHei', monospace;text-align:center;color:#ffd700;text-shadow:0 0 2px #ff0000;line-height:1.2;box-shadow:none;}
.center-btn:hover{background:rgba(0, 0, 0, 0.8);border-color:#ffaa00;transform:translate(-50%, -50%) scale(1.02);box-shadow:none;}
.time-remain{font-size:18px;font-family:monospace;margin-top:2px;}
@media (max-width:1000px){.spectrum-player{width:95px;height:95px;}.center-btn{width:60px;height:60px;}.time-remain{font-size:10px;}}
</style>
<div id="papa">
<div class="spectrum-player" id="spectrumPlayer">
<canvas id="fireworksCanvas"></canvas>
<svg class="progress-ring-svg" viewBox="0 0 110 110">
<circle class="progress-ring-circle" cx="55" cy="55" r="38"></circle>
</svg>
<div class="spectrum-dots" id="spectrumDots"></div>
<div class="center-btn" id="centerBtn">
<div class="time-remain" id="btnTimeRemain">-00:00</div>
</div>
</div>
<zxx-slide>
<div class="zxx-slide-x">
<p class="zxx-slide-a">      
<video class="zxx-slide-img" src="https://video-qn.51miz.com/preview/video/00/00/65/77/V-657748-CDAD249E.mp4" loop muted autoplay preload="auto" playsinline></video>
</p>
<p class="zxx-slide-a">
<video class="zxx-slide-img" src="https://imgpp.ztupic.com/u/30j5Llsx4y/1599104785483_4d8b815a.mp4" loop muted autoplay preload="auto" playsinline></video></p>
<p class="zxx-slide-a">
<video class="zxx-slide-img" src="https://test-videos.co.uk/vids/bigbuckbunny/mp4/h264/720/Big_Buck_Bunny_720_10s_1MB.mp4" loop muted autoplay preload="auto" playsinline></video></p>
<p class="zxx-slide-a">
<video class="zxx-slide-img" src="https://video-qn.51miz.com/preview/video/00/00/64/07/V-640739-E07CE2A9.mp4" loop muted autoplay preload="auto" playsinline></video></p>
<p class="zxx-slide-a">
<video class="zxx-slide-img" src="https://wj.zp68.com/qg01/2025/12/a1d5ecd7babe175c75b65e164aa3d07a.mp4" loop muted autoplay preload="auto" playsinline></video></p>
<p class="zxx-slide-a">
<video class="zxx-slide-img" src="https://pic.3gbizhi.com/uploads/20250702/b69a0ef8750eb6290944775a3758a905.mp4" loop muted autoplay preload="auto" playsinline></video></p>
<p class="zxx-slide-a">
<video class="zxx-slide-img" src="https://img-baofun.zhhainiao.com/pcwallpaper_ugc/preview/3395cde759c126980ab0c9b23c15d2c5_preview.mp4" loop muted autoplay preload="auto" playsinline></video></p>
<p class="zxx-slide-a">
<video class="zxx-slide-img" src="https://cccimg.com/view.php/85549f7a56d26714002df2df83e45a34.mp4" loop muted autoplay preload="auto" playsinline></video></p>
</div>
</zxx-slide>
<span id="fullscreen">全屏欣赏</span>
<div class="lyrics">
<div class="lyric-line">
<div class="lyric-mask"></div>
<div class="lyric-original"></div>
</div>
</div>
</div>
<audio id="audio" src="http://cccimg.com/view.php/ce7a5bdae19f401ee770c48b2e10adcb.mp3" autoplay loop></audio>
<script>
var eleZxxSlides = document.querySelectorAll('zxx-slide');
const effectList = ['effect-round','effect-topdown','effect-fade','effect-clear','effect-dissolve','effect-pixelate','effect-mosaic','effect-shatter'];
[].slice.call(eleZxxSlides).forEach(function (container) {
    var timerSlide = null;
    var indexSlide = 0;
    var ANIMATION_DURATION = 1200;
    var WAIT_DURATION = 8800;
    var TOTAL_INTERVAL = ANIMATION_DURATION + WAIT_DURATION;       
    var eleSlides = [].slice.call(container.querySelectorAll('p'));
    function getRandomEffect(){
      let ran = Math.floor(Math.random() * effectList.length);
      return effectList;
    }
    function createEffectLayer(slide, effect) {
      let oldLayer = slide.querySelector('.block-effect-wrapper, .shatter-wrapper');
      if(oldLayer) oldLayer.remove();
      let imgSrc = slide.querySelector('.zxx-slide-img')?.poster || '';
      let video = slide.querySelector('.zxx-slide-img');
      if(video && video.poster) imgSrc = video.poster;
      if(effect === 'effect-dissolve' || effect === 'effect-pixelate' || effect === 'effect-mosaic'){
            let wrapper = document.createElement('div');
            wrapper.className = `block-effect-wrapper ${effect.replace('effect-','')}-wrapper`;
            let cols = wrapper.classList.contains('dissolve-wrapper') ? 30 : wrapper.classList.contains('pixelate-wrapper') ? 40 : 15;
            let rows = wrapper.classList.contains('dissolve-wrapper') ? 20 : wrapper.classList.contains('pixelate-wrapper') ? 30 : 10;
            let total = cols * rows;
            for(let i=0; i<total; i++){
                let piece = document.createElement('div');
                piece.className = 'block-piece';
                wrapper.appendChild(piece);
            }
            slide.appendChild(wrapper);
      }
      else if(effect === 'effect-shatter'){
            let wrapper = document.createElement('div');
            wrapper.className = 'shatter-wrapper';
            let cols = 8, rows = 5;
            let w = 100/cols, h = 100/rows;
            for(let y=0; y<rows; y++){
                for(let x=0; x<cols; x++){
                  let piece = document.createElement('div');
                  piece.className = 'shatter-piece';
                  piece.style.width = w+'%';
                  piece.style.height = h+'%';
                  piece.style.left = x*w+'%';
                  piece.style.top = y*h+'%';
                  piece.style.backgroundImage = `url(${imgSrc})`;
                  piece.style.backgroundPosition = `${-x*w}% ${-y*h}%`;
                  wrapper.appendChild(piece);
                }
            }
            slide.appendChild(wrapper);
      }
    }
    function showCurrentSlide(){
      eleSlides.forEach(function (slide, index) {       
            if (indexSlide == index) {
                effectList.forEach(eff=>slide.classList.remove(eff));
                let randEff = getRandomEffect();
                createEffectLayer(slide, randEff);
                slide.classList.add('in', randEff);
                slide.style.display = 'block';
            } else {
                slide.classList.remove('in');
                effectList.forEach(eff=>slide.classList.remove(eff));
                slide.style.display = 'none';
            }
      });
    }
    function nextSlide(){
      indexSlide++;
      if (indexSlide >= eleSlides.length) indexSlide = 0;
      showCurrentSlide();
      if(!audio.paused){
            timerSlide = setTimeout(nextSlide, TOTAL_INTERVAL);
      }
    }
    showCurrentSlide();
    function startLoop(){
      clearTimeout(timerSlide);
      timerSlide = setTimeout(nextSlide, TOTAL_INTERVAL);
    }
    function stopLoop(){
      clearTimeout(timerSlide);
    }
    audio.addEventListener('play', startLoop);
    audio.addEventListener('pause', stopLoop);
    if(!audio.paused){
      startLoop();
    }
});
</script>
<script>
const lrc = `一路繁花为我开
风掠过眉弯
云漫过心海
天地作舞台
不必等谁来
山有千重绿
海有万里白
我自踏歌去
心事都放开
走遍山看遍海
一路繁花为我开
云也轻梦也快
跟着心情的节拍
踏过霜越过雾
一路繁花为我开
天也宽心也迈
此生尽兴不徘徊
星落进眼底
月照在窗台
山河皆温柔
岁月不亏待
风引我前行
光为我等待
不必回头望
前路更精彩
走遍山看遍海
一路繁花为我开
云也轻梦也快
跟着心情的节拍
踏过霜越过雾
一路繁花为我开
天也宽心也迈
此生尽兴不徘徊
不问来处不问归途
心向远方自有坦途
光落肩头风入我怀
人间万象皆可期待
走遍山看遍海
一路繁花为我开
云也轻梦也快
跟着心情的节拍
踏过霜越过雾
一路繁花为我开
天也宽心也迈
此生尽兴不徘徊
走遍山看遍海
都是我最好的未来
`;
const audio = document.getElementById('audio');
const lyrics = parseLyrics(lrc);
const lyricMask = document.querySelector('.lyric-mask');
const lyricOriginal = document.querySelector('.lyric-original');
let currentIndex = -1;
let currentLyric = null;
function parseLyrics(lrcText) {
    const lyrics = [];
    const lines = lrcText.split('\n').filter(line => line.trim());
    lines.forEach((line, index) => {
      const timeMatch = line.match(/\[(\d+:\d+\.\d+)\]/);
      if (timeMatch) {
            const timeStr = timeMatch;
            const text = line.replace(/\[.*?\]/g, '').trim();
            if (text) {
                const startTime = timeToMs(timeStr);
                const nextLine = lines;
                const nextTimeMatch = nextLine ? nextLine.match(/\[(\d+:\d+\.\d+)\]/) : null;
                const endTime = nextTimeMatch ? timeToMs(nextTimeMatch) : startTime + 5000;
                lyrics.push({
                  startTime,
                  endTime,
                  text,
                  durations: calculateCharDurations(text, startTime, endTime)
                });
            }
      }
    });
    return lyrics;
}
function calculateCharDurations(text, startTime, endTime) {
    const totalDuration = endTime - startTime;
    const charCount = text.length;
    const baseDur = Math.floor(totalDuration / charCount);
    const durations = new Array(charCount).fill(baseDur);
    const remainder = totalDuration % charCount;
    for (let i = 0; i < remainder; i++) {
      durations++;
    }
    return durations;
}
function timeToMs(timeStr) {
    const parts = timeStr.split(':');
    const minutes = parseInt(parts, 10);
    const secondsAndMs = parts.split('.');
    const seconds = parseInt(secondsAndMs, 10);
    const ms = parseInt(secondsAndMs || 0, 10);
    return minutes * 60 * 1000 + seconds * 1000 + ms;
}
function getCurrentLyricIndex(lyrics, currentTimeMs) {
    for (let i = 0; i < lyrics.length; i++) {
      if (currentTimeMs >= lyrics.startTime && currentTimeMs <= lyrics.endTime) {
            return i;
      }
    }
    return -1;
}
function updateLyricDisplay(index) {
    if (index < 0 || index >= lyrics.length) return;
    currentIndex = index;
    currentLyric = lyrics;
    lyricOriginal.textContent = currentLyric.text;
    lyricMask.textContent = currentLyric.text;
    lyricMask.style.width = '0';
}
function updateLyricMask(currentTimeMs) {
    if (!currentLyric) return;
    const elapsed = currentTimeMs - currentLyric.startTime;
    const totalDuration = currentLyric.durations.reduce((a, b) => a + b, 0);
    let charIndex = 0;
    let accumulated = 0;
    for (let i = 0; i < currentLyric.durations.length; i++) {
      accumulated += currentLyric.durations;
      if (elapsed <= accumulated) {
            charIndex = i + 1;
            break;
      }
    }
    if (elapsed >= totalDuration) charIndex = currentLyric.text.length;
    charIndex = Math.min(charIndex, currentLyric.text.length);
    const temp = document.createElement('span');
    temp.style.cssText = 'visibility:hidden;position:absolute;font:300 50px "华文隶书"';
    document.body.appendChild(temp);
    temp.textContent = currentLyric.text.substring(0, charIndex);
    lyricMask.style.width = temp.offsetWidth + 'px';
    document.body.removeChild(temp);
}
audio.addEventListener('timeupdate', () => {
    const now = audio.currentTime * 1000;
    const idx = getCurrentLyricIndex(lyrics, now);
    if (idx !== currentIndex) updateLyricDisplay(idx);
    updateLyricMask(now);
});
updateLyricDisplay(0);
let state = { isPlaying: false, isDragging: false };
const dom = {
    spectrumPlayer: document.getElementById('spectrumPlayer'),
    progressCircle: document.querySelector('.progress-ring-circle'),
    centerBtn: document.getElementById('centerBtn'),
    btnTimeRemain: document.getElementById('btnTimeRemain'),
    fireworksCanvas: document.getElementById('fireworksCanvas'),
    audio: document.getElementById('audio')
};
const innerRadius = 38;
const circumference = 2 * Math.PI * innerRadius;
let spectrumAnimationId = null, spectrumDots = [];
const totalDots = 24;
let globalRotationRad = 0;
const ROTATION_SPEED = 0.0068;
let originalAngles = [];
let dotRadius = 0;
let dotCenter = { x: 0, y: 0 };
const fwCtx = dom.fireworksCanvas.getContext('2d');
let particles = [];
let fwWidth, fwHeight, fwCenterX, fwCenterY;
function resizeFireworksCanvas() {
    fwWidth = dom.spectrumPlayer.clientWidth;
    fwHeight = dom.spectrumPlayer.clientHeight;
    dom.fireworksCanvas.width = fwWidth;
    dom.fireworksCanvas.height = fwHeight;
    fwCenterX = fwWidth / 2;
    fwCenterY = fwHeight / 2;
}
resizeFireworksCanvas();
window.addEventListener('resize', resizeFireworksCanvas);
class Particle {
    constructor(angle, speed, color) {
      this.x = fwCenterX;
      this.y = fwCenterY;
      this.vx = Math.cos(angle) * speed;
      this.vy = Math.sin(angle) * speed;
      this.alpha = 1;
      this.life = 1;
      this.radius = Math.random() * 2 + 1;
      this.color = color;
    }
    update() {
      this.x += this.vx;
      this.y += this.vy;
      this.life -= 0.02;
      this.alpha = this.life;
    }
    draw(ctx) {
      ctx.save();
      ctx.globalAlpha = this.alpha;
      ctx.beginPath();
      ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
      ctx.fillStyle = this.color;
      ctx.fill();
      ctx.restore();
    }
}
function emitFireworks(intensity) {
    const count = Math.floor(intensity * 8);
    const baseSpeed = 1.5 + intensity * 2;
    const colors = ['#ffdd00', '#ff8800', '#ff4444', '#ffffff'];
    for (let i = 0; i < count; i++) {
      const angle = Math.random() * Math.PI * 2;
      const speed = baseSpeed * (0.5 + Math.random() * 0.5);
      const color = colors;
      particles.push(new Particle(angle, speed, color));
    }
}
function animateFireworks() {
    fwCtx.clearRect(0, 0, fwWidth, fwHeight);
    particles = particles.filter(p => p.life > 0);
    particles.forEach(p => {
      p.update();
      p.draw(fwCtx);
    });
    if (state.isPlaying) {
      requestAnimationFrame(animateFireworks);
    } else {
      fwCtx.clearRect(0, 0, fwWidth, fwHeight);
    }
}
function updateDotGeometry() {
    const w = dom.spectrumPlayer.clientWidth;
    const h = dom.spectrumPlayer.clientHeight;
    dotCenter.x = w / 2;
    dotCenter.y = h / 2;
    dotRadius = Math.min(w, h) * 0.52;
}
function rebuildSpectrumDots() {
    const container = document.getElementById('spectrumDots');
    if (!container) return;
    container.innerHTML = '';
    spectrumDots = [];
    originalAngles = [];
    updateDotGeometry();
    for (let i = 0; i < totalDots; i++) {
      const angle = (i / totalDots) * 2 * Math.PI;
      originalAngles.push(angle);
      const dot = document.createElement('div');
      dot.className = 'spectrum-dot';
      const x = dotCenter.x + dotRadius * Math.cos(angle);
      const y = dotCenter.y + dotRadius * Math.sin(angle);
      dot.style.left = `${x}px`;
      dot.style.top = `${y}px`;
      container.appendChild(dot);
      spectrumDots.push(dot);
    }
}
function updateDotsPosition() {
    for (let i = 0; i < spectrumDots.length; i++) {
      const dot = spectrumDots;
      const rawAngle = originalAngles;
      const finalAngle = rawAngle + globalRotationRad;
      const px = dotCenter.x + dotRadius * Math.cos(finalAngle);
      const py = dotCenter.y + dotRadius * Math.sin(finalAngle);
      dot.style.left = `${px}px`;
      dot.style.top = `${py}px`;
    }
}
let resizeTimeout;
function handleResize() {
    if (resizeTimeout) clearTimeout(resizeTimeout);
    resizeTimeout = setTimeout(() => {
      if (!dom.spectrumPlayer) return;
      const wasPlaying = state.isPlaying;
      if (wasPlaying && spectrumAnimationId) {
            cancelAnimationFrame(spectrumAnimationId);
            spectrumAnimationId = null;
      }
      updateDotGeometry();
      rebuildSpectrumDots();
      updateDotsPosition();
      if (wasPlaying) startSpectrumAnimation();
    }, 100);
}
window.addEventListener('resize', handleResize);
function formatNegativeTime(seconds) {
    if (isNaN(seconds) || !isFinite(seconds) || seconds <= 0) return "-00:00";
    const mins = Math.floor(seconds / 60), secs = Math.floor(seconds % 60);
    return `-${mins}:${secs < 10 ? '0' : ''}${secs}`;
}
function updateProgressCircle() {
    if (!dom.audio.duration) return;
    const progress = dom.audio.currentTime / dom.audio.duration;
    dom.progressCircle.style.strokeDashoffset = circumference * (1 - progress);
    dom.btnTimeRemain.textContent = formatNegativeTime(dom.audio.duration - dom.audio.currentTime);
    dom.progressCircle.style.stroke = `hsl(${40 + progress * 20}, 100%, 55%)`;
}
function generateSpectrum() {
    const t = Date.now() / 250;
    let maxVal = 0;
    const data = [];
    for (let i = 0; i < totalDots; i++) {
      let val = 0.4 + Math.sin(t + i * 0.3) * 0.4 + Math.random() * 0.3;
      val = Math.min(1.0, Math.max(0.3, val));
      data.push(val);
      if(val > maxVal) maxVal = val;
    }
    return {data, intensity: maxVal};
}
function startSpectrumAnimation() {
    if (spectrumAnimationId) cancelAnimationFrame(spectrumAnimationId);
    if (!state.isPlaying) return;
    animateFireworks();
    function animate() {
      if (!state.isPlaying) return;
      globalRotationRad += ROTATION_SPEED;
      if (globalRotationRad > Math.PI * 2) globalRotationRad -= Math.PI * 2;
      updateDotsPosition();
      const {data, intensity} = generateSpectrum();
      if (intensity > 0.68) emitFireworks(intensity);
      for (let i = 0; i < spectrumDots.length; i++) {
            const dot = spectrumDots;
            const val = data;
            const scale = 0.7 + val * 0.9;
            dot.style.transform = `scale(${scale})`;
            const hue = 35 + val * 45;
            const sat = 95;
            const light = 58 + val * 28;
            dot.style.background = `hsl(${hue}, ${sat}%, ${light}%)`;
            const glowIntensity = 4 + val * 12;
            const shadowColor = `hsl(${hue + 10}, 100%, 65%)`;
            dot.style.boxShadow = `0 0 ${glowIntensity}px ${shadowColor}, 0 0 3px #ffcc88`;
            if (val > 0.85) {
                dot.style.boxShadow = `0 0 ${glowIntensity + 6}px #fff5b0, 0 0 6px #ffaa44`;
            }
      }
      spectrumAnimationId = requestAnimationFrame(animate);
    }
    animate();
}
function togglePlayback() {
    if (dom.audio.paused) {
      dom.audio.play().catch(e => {});
    } else {
      dom.audio.pause();
    }
    updatePlayerUI();
}
function updatePlayerUI() {   
    state.isPlaying = !dom.audio.paused;
    const vids = document.querySelectorAll('.zxx-slide-img');
    var mState = () => {
         vids.forEach(vid => audio.paused ? vid.pause(): vid.play() );
    };
audio.onplaying = audio.onpause = () => mState();
    if (state.isPlaying) {
      startSpectrumAnimation();
      updateProgressCircle();
      centerBtn.title = "暂停";
    } else {
      if (spectrumAnimationId) cancelAnimationFrame(spectrumAnimationId);
      spectrumAnimationId = null;
      centerBtn.title = "播放";
    }
}
function getAngleFromEvent(e, rect) {
    const cx = rect.left + rect.width / 2, cy = rect.top + rect.height / 2;
    let clientX = e.touches ? e.touches.clientX : e.clientX, clientY = e.touches ? e.touches.clientY : e.clientY;
    const dx = clientX - cx, dy = clientY - cy;
    let angle = Math.atan2(dy, dx);
    if(angle < 0) angle += 2 * Math.PI;
    let progressAngle = angle - Math.PI/2;
    if(progressAngle < 0) progressAngle += 2 * Math.PI;
    return progressAngle / (2 * Math.PI);
}
function seekByEvent(e, rect) {
    const progress = getAngleFromEvent(e, rect);
    if(!dom.audio.duration) return;
    dom.audio.currentTime = progress * dom.audio.duration;
    updateProgressCircle();
}
function bindCircularProgressEvents() {
    const p = dom.spectrumPlayer;
    let d = false;
    const m = (e) => { if(!d) return; e.preventDefault(); seekByEvent(e, p.getBoundingClientRect()); };
    const u = () => { d = false; document.removeEventListener('mousemove', m); document.removeEventListener('mouseup', u); };
    p.addEventListener('mousedown', (e) => {
      if(e.target === dom.centerBtn || dom.centerBtn.contains(e.target)) return;
      e.preventDefault(); d = true;
      seekByEvent(e, p.getBoundingClientRect());
      document.addEventListener('mousemove', m);
      document.addEventListener('mouseup', u);
    });
    p.addEventListener('touchstart', (e) => {
      if(e.target === dom.centerBtn || dom.centerBtn.contains(e.target)) return;
      e.preventDefault(); d = true;
      seekByEvent(e, p.getBoundingClientRect());
      document.addEventListener('touchmove', m);
      document.addEventListener('touchend', u);
    });
    p.addEventListener('click', (e) => {
      if(e.target === dom.centerBtn || dom.centerBtn.contains(e.target)) return;
      seekByEvent(e, p.getBoundingClientRect());
    });
}
function initPlayer() {
    dom.progressCircle.style.strokeDashoffset = circumference;
    updateDotGeometry();
    rebuildSpectrumDots();
    bindCircularProgressEvents();
    dom.centerBtn.addEventListener('click', (e) => { e.stopPropagation(); togglePlayback(); });
    dom.audio.addEventListener('timeupdate', updateProgressCircle);
    dom.audio.addEventListener('play', updatePlayerUI);
    dom.audio.addEventListener('pause', updatePlayerUI);   
}
initPlayer();
papa.oncontextmenu = (e) => e.preventDefault();
let fs = true;
let 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';
    }, 3000);
});
</script>
</body>
</html>

杨帆 发表于 2026-5-8 10:12

代码来自亚伦老师、了了子老师分享作品,在此表示感谢{:4_180:}

也曾年轻 发表于 2026-5-8 10:55

https://pic.rmb.bdstatic.com/bjh/down/6e6c1ba9ea2a2b6649a03140b3b074c8.gif

彩云归 发表于 2026-5-8 11:16

多视频转场占用太多资源,造成歌曲播放断断续续,影响整体欣赏。建议减少视频数量,最好只留一部视频。

梦江南 发表于 2026-5-8 12:03

https://pic.rmb.bdstatic.com/bjh/down/6e6c1ba9ea2a2b6649a03140b3b074c8.gif

梦油 发表于 2026-5-8 17:05

欣赏精彩制作,问好杨帆朋友。

小辣椒 发表于 2026-5-8 22:31

杨帆制作的特点就是加多个视频做特效,平时我手机基本都看不了,特别转换会卡。

小辣椒 发表于 2026-5-8 22:31

欣赏杨帆精彩的制作{:4_187:}

杨帆 发表于 2026-5-8 22:59

也曾年轻 发表于 2026-5-8 10:55


谢谢年轻老师鼓励,请多指导{:4_190:}

杨帆 发表于 2026-5-8 23:01

彩云归 发表于 2026-5-8 11:16
多视频转场占用太多资源,造成歌曲播放断断续续,影响整体欣赏。建议减少视频数量,最好只留一部视频。

有道理,谢谢彩云归老师的建议{:4_190:}

杨帆 发表于 2026-5-8 23:02

梦江南 发表于 2026-5-8 12:03


谢谢江南鼓励,祝开心{:4_204:}

杨帆 发表于 2026-5-8 23:03

梦油 发表于 2026-5-8 17:05
欣赏精彩制作,问好杨帆朋友。

谢谢梦兄鼓励,祝开心{:4_176:}

杨帆 发表于 2026-5-8 23:05

小辣椒 发表于 2026-5-8 22:31
杨帆制作的特点就是加多个视频做特效,平时我手机基本都看不了,特别转换会卡。

我这儿运行正常呀,刚试看手机也正常,或许与网速有关?

杨帆 发表于 2026-5-8 23:06

小辣椒 发表于 2026-5-8 22:31
欣赏杨帆精彩的制作

谢谢小辣椒鼓励,请多指导{:4_204:}

霜染枫丹 发表于 2026-5-9 00:11

欣赏杨帆非精彩制作,祝你制作愉快!!{:4_204:}{:4_190:}

梦油 发表于 2026-5-9 10:21

杨帆 发表于 2026-5-8 23:03
谢谢梦兄鼓励,祝开心

杨帆朋友别客气。

杨帆 发表于 2026-5-9 12:40

本帖最后由 杨帆 于 2026-5-9 12:41 编辑

霜染枫丹 发表于 2026-5-9 00:11
欣赏杨帆非精彩制作,祝你制作愉快!!
谢谢枫丹老师鼓励,用到您的视频素材,在此表示感谢并祝欣赏愉快{:4_187:}

小辣椒 发表于 2026-5-9 15:25

杨帆 发表于 2026-5-8 23:05
我这儿运行正常呀,刚试看手机也正常,或许与网速有关?

是啊,我网卡,好郁闷,自己的都要刷新才看见,你的我只能下载源码在电脑预览,单位现在不能玩电脑了

霜染枫丹 发表于 2026-5-9 18:13

杨帆 发表于 2026-5-9 12:40
谢谢枫丹老师鼓励,用到您的视频素材,在此表示感谢并祝欣赏愉快

杨帆别客气,我的素材您随便用。我们一起玩多高兴!

杨帆 发表于 2026-5-9 20:54

小辣椒 发表于 2026-5-9 15:25
是啊,我网卡,好郁闷,自己的都要刷新才看见,你的我只能下载源码在电脑预览,单位现在不能玩电脑了

是,网速挺重要呢{:4_187:}
页: [1] 2
查看完整版本: 一路繁花为我开