醉美水芙蓉 发表于 2025-11-29 22:30:04

【单幅音画】风带不走的思念 TO云裳老师生辰快乐!

<style>
    #bj {
      position: relative;
      width: 1300px;
      height: 720px;
      margin-left: -150px;
      margin-top: 0;
       overflow: hidden;z-index:12345;
            background:url(https://bbs.cnzv.cc/up/upload/pic/12/20251129-1cc4db68158d54e47e3fa3cc69472932.jpg)0px 0px/100% 100%;
   
    }
    canvas {
position: absolute;
      width: 100%;
      height: 100%;
      display: block;
      cursor: pointer;
      transition: transform 0.3s ease;
      margin-top: -50px;margin-left: -120px;
    }
    canvas:hover {
      transform: scale(1.05);
    }

    .lyric-preview { opacity: 0;
      position: absolute;
      top: 40px;
      left: 50%;
      transform: translateX(-50%);
      color: blue;
      font-size: 18px;
      text-align: center;
      width: 80%;
      z-index: 5;
      cursor: pointer; /* 提示可点击 */
      padding: 10px;
      border-radius: 8px;
      transition: background-color 0.2s ease;
    }
    .lyric-preview:hover {
   color: #00ff00;
    }
    .prev-lyric {
      opacity: 0.6;
      margin-bottom: 8px;
      transition: opacity 0.2s ease;
    }
    .current-lyric {
      font-size: 22px;
      font-weight: bold;
      color: #ff00ff;
      transition: opacity 0.2s ease;
    }
    .next-lyric {
      opacity: 0.6;
      margin-top: 8px;
      transition: opacity 0.2s ease;
    }
    .lyric-preview:active .current-lyric {
      opacity: 0.9;
    }
#player {
        position: absolute;z-index: 40;
        left:5%;
        bottom: 50px;
        width: 150px;
        height: 150px;
         opacity: 1;
        transition: .4s;
display: grid;
        place-items: center;
        --sp1: 0; --sp2: 1;
}
#player::before, #player::after {
        position: absolute;
        content: '';
        cursor: pointer;
        transition: .4s;
}
#player::before {width: 150px;height: 150px;background:url(https://pic.imgdb.cn/item/65f0d8669f345e8d03ff5c34.png)no-repeat center/cover;
        opacity: var(--sp1);}
#player::after {width: 150px;height: 150px;background:url(https://pic.imgdb.cn/item/65f0d8299f345e8d03ff0b31.gif)no-repeat center/cover;
        opacity: var(--sp2);}
</style>

<div id="bj">
    <div class="lyric-preview" id="lyricPreview">
      <div class="prev-lyric" id="prevLyric"></div>
      <div class="current-lyric" id="currentLyric"></div>
      <div class="next-lyric" id="nextLyric"></div>
    </div>
<div id="player" title="暂停/播放" ></div>
    <canvas id="glcanvas" width="900" height="420"></canvas>
    <audio id="audio" src="https://www.joy127.com/url/138820.mp3" loop autoplay></audio>
</div>

<script>
    const state = {
      currentLyricIndex: 0,
      isAudioEnabled: true,
      isPlaying: false,
      mouseX: 0,
      mouseY: 0,
      isHovering: false,
      lyrics: [],
      hoverValue: 0,
      hoverSpeed: 0.005, // 降低hover动画速度,更流畅
      lastMouseMoveTime: 0,
      throttleDelay: 100 // 鼠标事件节流延迟
    };

    const elements = {
      canvas: document.getElementById('glcanvas'),
      audio: document.getElementById('audio'),
      lyricPreview: document.getElementById('lyricPreview'), // 歌词容器
      prevLyric: document.getElementById('prevLyric'),
      currentLyric: document.getElementById('currentLyric'),
      nextLyric: document.getElementById('nextLyric')
    };

    // ===================== 初始化检测 =====================
    function initCheck() {
      // WebGL兼容性检测
      const gl = elements.canvas.getContext('webgl') || elements.canvas.getContext('experimental-webgl');
      if (!gl) {
            state.isAudioEnabled = false; //
            return null;
      }
      return gl;
    }

    // ===================== LRC歌词解析 =====================
    function parsePureLRC(lrcText) {
      const lines = [];
      const lrcLines = lrcText.split('\n')
            .map(line => line.trim())
            .filter(line => line && !line.includes('●') && !line.includes('谢谢欣赏'));

      for (const line of lrcLines) {
            const timeRegex = /\[(\d{2}):(\d{2})(?:\.(\d{1,3}))?\]/g;
            const text = line.replace(timeRegex, '').trim();
            let timeMatch;

            while ((timeMatch = timeRegex.exec(line)) !== null) {
                const minutes = parseInt(timeMatch);
                const seconds = parseInt(timeMatch);
                const ms = timeMatch ? parseInt(timeMatch.padEnd(3, '0')) : 0;
                const time = minutes * 60 + seconds + ms / 1000;

                if (text) {
                  lines.push({ time, text });
                }
            }
      }

      return lines.sort((a, b) => a.time - b.time);
    }

    // ===================== 歌词跳转 =====================
    function jumpToLyricByText(clickText) {
      if (!clickText.trim()) return;
      const targetLyric = state.lyrics.find(lyric => lyric.text === clickText.trim());
      if (!targetLyric) return;
      elements.audio.currentTime = targetLyric.time;
      const targetIndex = state.lyrics.findIndex(lyric => lyric.text === clickText.trim());
      if (targetIndex !== -1) {
            state.currentLyricIndex = targetIndex;
            elements.prevLyric.textContent = state.lyrics?.text || '';
            elements.currentLyric.textContent = state.lyrics.text;
            elements.nextLyric.textContent = state.lyrics?.text || '';
            if (window.render && window.render.updateTextTexture) {
                window.render.updateTextTexture(state.lyrics.text);
            }
      }
    }

    // ===================== WebGL渲染 =====================
    function initWebGL(gl) {
      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 vertexShaderSource = `
            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 fragmentShaderSource = `
            precision highp float;
            varying vec2 v_uv;
            uniform sampler2D u_text;
            uniform float u_time;
            uniform vec2 u_mouse;
            uniform float u_hover;

            float random(vec2 st) {
                return fract(sin(dot(st.xy, vec2(12.9898,78.233)))*43758.5453123);
            }

            float noise(vec2 st) {
                vec2 i = floor(st);
                vec2 f = fract(st);
                float a = random(i);
                float b = random(i + vec2(1.0,0.0));
                float c = random(i + vec2(0.0,1.0));
                float d = random(i + vec2(1.0,1.0));
                vec2 u = f*f*(3.0-2.0*f);
                return mix(a,b,u.x) + (c-a)*u.y*(1.0-u.x) + (d-b)*u.x*u.y;
            }

            void main() {
                vec2 correctedUV = vec2(v_uv.x, 1.0-v_uv.y);
                float strength = smoothstep(0.2,1.0,correctedUV.y);
                float noiseTime = u_time * 0.3;
                float n = noise(vec2(correctedUV.x*5.0, (correctedUV.y*2.0 - noiseTime)*2.0));
               
                float melt = n * 0.4 * (correctedUV.y);
                float dist = distance(correctedUV, u_mouse);
            
                float hoverWave = 0.0;
                if (u_hover > 0.1) {
                  hoverWave = sin(dist*12.0 - u_time*6.0)*0.08*u_hover*exp(-dist*3.5) +
                               sin(dist*20.0 - u_time*10.0)*0.04*u_hover*exp(-dist*5.0);
                }
                  
                vec2 hoverDistort = normalize(correctedUV - u_mouse) * hoverWave * (1.0 + sin(u_time*2.0)*0.1);
                vec2 finalUV = vec2(
                  correctedUV.x + melt*0.3 + hoverDistort.x,
                  correctedUV.y - melt*0.8 + hoverDistort.y
                );

                vec4 col = texture2D(u_text, finalUV);
                if (col.a > 0.1) {
                  float glow = smoothstep(0.8,1.0,col.a);
                  vec3 spectrumColor = vec3(correctedUV.y, abs(sin(u_time + correctedUV.x*5.0)), 1.0-correctedUV.y);
                  
                  if (u_hover > 0.1) {
                        float hoverIntensity = 1.0 - smoothstep(0.0,0.1,dist);
                        float pulseEffect = 0.99 + 0.1*sin(u_time*4.0 + dist*8.0);
                        vec3 goldColor = vec3(0.99, 0.99+0.8*pulseEffect, 0.2+0.2*sin(u_time*3.0));
                        vec3 blueColor = vec3(0.99+0.99*sin(u_time*2.0 + dist*6.0), 0.99+0.8*pulseEffect, 0.5);
                        spectrumColor = mix(spectrumColor, mix(goldColor, blueColor, 0.99), hoverIntensity*u_hover*0.99);
                        glow += hoverIntensity*u_hover*0.99;
                  }
                  
                  col.rgb = mix(col.rgb, spectrumColor, 0.99*glow);
                  
                  // 优化粒子效果
                   if (u_hover > 0.1 && dist < 0.2) {
                        float particles = noise(correctedUV*15.0 + u_time*2.0);
                        if (particles > 0.85) col.rgb += vec3(1.0,0.8,0.4)*(particles-0.85)*8.0*u_hover;
                        
                        float sparkles = noise(correctedUV*30.0 + u_time*4.0);
                        if (sparkles > 0.92 && dist < 0.15) col.rgb += vec3(0.9,0.9,1.0)*(sparkles-0.92)*6.0*u_hover;
                  }
                }
                     
                if (u_hover > 0.1) {
                  float ambientGlow = 1.0 - smoothstep(0.0,0.4,dist);
                  float auraEffect = sin(dist*6.0 - u_time*3.0)*0.2 + 0.8;
                  vec3 auraColor = vec3(
                        0.15+0.1*sin(u_time*1.5),
                        0.1+0.1*sin(u_time*2.0+1.0),
                        0.2+0.15*sin(u_time*1.2+2.0)
                  );
                  col.rgb += auraColor*ambientGlow*u_hover*auraEffect;
                  
                  float outerHalo = 1.0 - smoothstep(0.2,0.5,dist);
                  col.rgb += vec3(0.08,0.04,0.12)*outerHalo*u_hover*0.3;
                }
               
                gl_FragColor = col;
            }
      `;
      const vs = compileShader(vertexShaderSource, gl.VERTEX_SHADER);
      const fs = compileShader(fragmentShaderSource, gl.FRAGMENT_SHADER);
      const program = gl.createProgram();
      gl.attachShader(program, vs);
      gl.attachShader(program, fs);
      gl.linkProgram(program);
      const quad = new Float32Array([
          -1, -1, 0, 0,
            1, -1, 1, 0,
            -1, 1, 0, 1,
            1, 1, 1, 1
      ]);
      const buf = gl.createBuffer();
      gl.bindBuffer(gl.ARRAY_BUFFER, buf);
      gl.bufferData(gl.ARRAY_BUFFER, quad, gl.STATIC_DRAW);
      const a_pos = gl.getAttribLocation(program, 'a_pos');
      const a_uv = gl.getAttribLocation(program, '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);
      const uniforms = {
            u_time: gl.getUniformLocation(program, 'u_time'),
            u_text: gl.getUniformLocation(program, 'u_text'),
            u_mouse: gl.getUniformLocation(program, 'u_mouse'),
            u_hover: gl.getUniformLocation(program, 'u_hover')
      };
      const textCanvas = document.createElement('canvas');
      const tctx = textCanvas.getContext('2d');
      textCanvas.width = elements.canvas.width;
      textCanvas.height = elements.canvas.height;

      const textTex = gl.createTexture();
      gl.bindTexture(gl.TEXTURE_2D, textTex);
      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);
      function updateTextTexture(text) {if (!audio.paused) {
            tctx.clearRect(1, 1, textCanvas.width, textCanvas.height);
            tctx.fillStyle = '#000';
            tctx.font = '800 30px 微软雅黑';
            tctx.textAlign = 'center';
            tctx.textBaseline = '800';
            tctx.fillText(text || '', textCanvas.width / 2, textCanvas.height / 2);
            
            gl.bindTexture(gl.TEXTURE_2D, textTex);
            gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, textCanvas);
      }
}
      // 绘制函数
      function draw(t) {
            if (!state.isAudioEnabled) return;
            if (state.isHovering && state.hoverValue < 1.0) {
                state.hoverValue = Math.min(1.0, state.hoverValue + state.hoverSpeed);
            } else if (!state.isHovering && state.hoverValue > 0.0) {
                state.hoverValue = Math.max(0.0, state.hoverValue - state.hoverSpeed);
            }
            gl.viewport(0, 0, elements.canvas.width, elements.canvas.height);
            gl.clear(gl.COLOR_BUFFER_BIT);
            gl.useProgram(program);
            const time = t * 0.0005;
            gl.uniform1f(uniforms.u_time, time);
            gl.uniform2f(uniforms.u_mouse, state.mouseX, state.mouseY);
            gl.uniform1f(uniforms.u_hover, state.hoverValue);
            gl.activeTexture(gl.TEXTURE0);
            gl.bindTexture(gl.TEXTURE_2D, textTex);
            gl.uniform1i(uniforms.u_text, 0);

            gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

          requestAnimationFrame(draw);
      }

      return { updateTextTexture, draw};
    }

    // ===================== 歌词同步 =====================
    function updateLyric() {
      if (!state.isAudioEnabled || !state.lyrics.length) return;
      const currentTime = elements.audio.currentTime;
      const { lyrics } = state;
      for (let i = 0; i < lyrics.length; i++) {
            const currentLyricTime = lyrics.time;
            const nextLyricTime = i < lyrics.length - 1 ? lyrics.time : Infinity;
            if (currentTime >= currentLyricTime && currentTime < nextLyricTime && state.currentLyricIndex !== i) {
                state.currentLyricIndex = i;
                if (window.render && window.render.updateTextTexture) {
                  window.render.updateTextTexture(lyrics.text);
                }
                // 更新歌词预览
                elements.prevLyric.textContent = lyrics?.text || '';
                elements.currentLyric.textContent = lyrics.text;
                elements.nextLyric.textContent = lyrics?.text || '';
                break;
            }
      }

       requestAnimationFrame(updateLyric);
    }

    // ===================== 事件绑定 =====================
    function bindEvents(gl) {
      window.addEventListener('resize', () => {
            if (!gl) return;
            const width = elements.canvas.parentElement.clientWidth * 0.95;
            const height = elements.canvas.parentElement.clientHeight * 0.6;
            elements.canvas.width = width;
            elements.canvas.height = height;
      });
      elements.canvas.addEventListener('mousemove', (e) => {if (!audio.paused) {
            const now = Date.now();
            if (now - state.lastMouseMoveTime < state.throttleDelay) return;
            state.lastMouseMoveTime = now;

            if (state.isAudioEnabled) {
                const rect = elements.canvas.getBoundingClientRect();
                state.mouseX = (e.clientX - rect.left) / rect.width;
                state.mouseY = 1.0 - (e.clientY - rect.top) / rect.height;
                state.isHovering = true;
            }
       } });
      elements.canvas.addEventListener('mouseleave', () => {
            state.isHovering = false;
      });
      elements.lyricPreview.addEventListener('click', (e) => {
            const target = e.target;
            if (target.classList.contains('prev-lyric') || target.classList.contains('current-lyric') || target.classList.contains('next-lyric')) {
                const clickText = target.textContent;
                jumpToLyricByText(clickText);
            }
      });
    }

    // ===================== 初始化 =====================
    async function init() {
      // LRC歌词文本
      const pureLRCText = `
风带不走的思念
词:江琳琳
曲:江琳琳
编曲:江琳琳
和声:江琳琳
混音:江琳琳
录音棚:亦辰studio
监制:李江山
制作人:陈大创
出品人:向明
LRC编辑:醉美水芙蓉
把往事酿成一壶酒啊
就着回忆啊 一口饮下
旷野的风吹过我的心
带走我最后的回答
你似流星般的划过天空
我如残烛还守着你的梦
泪水是对你无声的告白
却耗尽我的余生去遗忘
རླུང་གིས་ངའི་སེམས་པ་འཁྱོག
བྱ་རྒོད་མཐོང་བའི་ས་མཚམས
ད་ནི་ཁྲི་ཤོག་གི་བར
མཇུག་མེད་ཀྱི་བདེ་སྐྱིད
风带不走的思念 沉入了深海
眼泪冲不走回忆 刻画了空白
我们相遇在人生岔路的交叉口
用一生忘记 那一秒的心动
把往事酿成一壶酒啊
就着回忆啊 一口饮下
旷野的风吹过我的心
带走我最后的回答
你似流星般的划过天空
我如残烛还守着你的梦
泪水是对你无声的告白
却耗尽我的余生去遗忘
རླུང་གིས་ངའི་སེམས་པ་འཁྱོག
བྱ་རྒོད་མཐོང་བའི་ས་མཚམས
ད་ནི་ཁྲི་ཤོག་གི་བར
མཇུག་མེད་ཀྱི་བདེ་སྐྱིད
风带不走的思念 沉入了深海
眼泪冲不走回忆 刻画了空白
我们相遇在人生岔路的交叉口
用一生忘记 那一秒的心动
རླུང་གིས་ངའི་སེམས་པ་འཁྱོག
བྱ་རྒོད་མཐོང་བའི་ས་མཚམས
ད་ནི་ཁྲི་ཤོག་གི་བར
མཇུག་མེད་ཀྱི་བདེ་སྐྱིད
风带不走的思念 沉入了深海
眼泪冲不走回忆 刻画了空白
རླུང་གིས་ངའི་སེམས་པ་འཁྱོག
བྱ་རྒོད་མཐོང་བའི་ས་མཚམས
ད་ནི་ཁྲི་ཤོག་གི་བར
མཇུག་མེད་ཀྱི་བདེ་སྐྱིད
风带不走的思念 沉入了深海
眼泪冲不走回忆 刻画了空白
我们相遇在人生岔路的交叉口
用一生忘记 那一秒的心动
谢谢欣赏!
      `;
      state.lyrics = parsePureLRC(pureLRCText);
      const gl = initCheck();
      if (!gl) return;
      window.render = initWebGL(gl);
      bindEvents(gl);
      requestAnimationFrame(window.render.draw);
      updateLyric();
      window.render.updateTextTexture(state.lyrics?.text || '');
      elements.prevLyric.textContent = '';
      elements.currentLyric.textContent = state.lyrics?.text || '';
      elements.nextLyric.textContent = state.lyrics?.text || '';
    }
    window.addEventListener('load', init);


player.addEventListener('click', () => audio.paused ? audio.play() : audio.pause());
let mState = () => audio.paused ? (player.style.setProperty('--sp1','1'), player.style.setProperty('--sp2','0')) : (player.style.setProperty('--sp1','0'), player.style.setProperty('--sp2','1'));
audio.addEventListener('play', () => mState());
audio.addEventListener('pause', () => mState());
</script>

绿蔷薇 发表于 2025-11-29 22:40:18

同步歌词做成了灵动的彩色动态字幕,唱片式播放器还能随节奏变色哈~{:4_123:}

绿蔷薇 发表于 2025-11-29 22:42:09

暗调底色让女子的美感更显清晰动人,蛐蛐真好听,睡前聆听会儿~~~{:6_174:}

绿蔷薇 发表于 2025-11-29 22:43:14

醉美制作的棒棒哒
一起祝云裳老师生日快乐
{:S06:}

醉美水芙蓉 发表于 2025-11-30 07:59:40

绿蔷薇 发表于 2025-11-29 22:43
醉美制作的棒棒哒
一起祝云裳老师生日快乐

谢谢友友支持!同祝云裳老师生日快乐!

绿蔷薇 发表于 2025-11-30 09:20:31

醉美水芙蓉 发表于 2025-11-30 07:59
谢谢友友支持!同祝云裳老师生日快乐!

醉美早上好~~~{:6_174:}

欧阳风刀 发表于 2025-11-30 10:49:38

别具特色的音画美帖。元素丰富,很是有炫酷感。歌曲动听。

欧阳风刀 发表于 2025-11-30 10:50:24

感谢醉美朋友的分享。顺祝云裳生日快乐。{:1_155:}

玫の玫 发表于 2025-11-30 12:19:32

欣赏友友佳作,感谢精彩分享,感谢给大家带来美滴享受!祝创作愉快,天天好心情!

玫の玫 发表于 2025-11-30 12:20:04

画面优美,歌曲好听,制作辛苦,祝创作愉快!
页: [1] 2 3
查看完整版本: 【单幅音画】风带不走的思念 TO云裳老师生辰快乐!