抹去泪水 - 韩宝仪
<meta charset="utf-8"><style> @import url("https://fonts.googleapis.com/css2?family=Ma+Shan+Zheng&display=swap");
#bj {
position: relative;
width: 1300px;
height: 867px;
margin-left: -300px;
margin-top: 0;
overflow: hidden;z-index:12345;
background:url(https://bbs.cnzv.cc/up/upload/pic/12/20260309-9094c8233ef3f417b6e373bf33d73b9d.jpg)no-repeat center/cover;
--state: running;font-family: "Ma Shan Zheng","仿宋体","SimHei", "Arial", "sans-serif";
}
canvas {animation: flash 2s linear infinite ;
position: absolute;
width: 100%;
height: 100%;z-index: 7;
display: block;
cursor: pointer; pointer-events: none;
transition: transform 0.3s ease;
margin-top: -80px;margin-left: -120px;
}
canvas:hover {
transform: scale(1.05);
}
@keyframes flash {
to { filter: hue-rotate(360deg)brightness(260%); }
}
.lyric-preview { display: none;
position: absolute;
top: 240px;
left: 35%;
transform: translateX(-50%);
color: #ff0000;
font-size: 45px;
text-align: center;
width: 80%;
z-index: 7;
cursor: pointer; /**/
padding: 10px;
border-radius: 0px;
transition: background-color 0.2s ease;
}
.lyric-preview:hover {
color: #fff000;
}
.prev-lyric {
opacity: 0.8;color: #fff000;
margin-bottom: 8px;
transition: opacity 0.2s ease;
}
.current-lyric {
font-size: 55px;
font-weight: 300;
color: #ff0000
transition: opacity 0.2s ease;
}
.prev-lyric:hover {
transform: scale(1.15);
}
.next-lyric {
opacity: 0.8;color: #fff000;
margin-top: 8px;
transition: opacity 0.2s ease;
}
.lyric-preview:active .current-lyric {
opacity: 0.9;
}
#mdiv {top:12%; left:85%;cursor: pointer;width:120px;height: 120px;animation:rot 10s linear infinite var(--state);position: absolute;filter:drop-shadow(#000 0px 0 1px);z-index: 40;}
@keyframes rot { to { transform: rotate(2turn);} }
#toggleButton{position: absolute;margin-top: 30px; margin-left: 1050px;color:#ff0000; width:180px; height:80px; font-size:25px;cursor:pointer; text-align: center;z-index:1235;}
#toggleButton:hover { color:#fff;}
#canv {display: block; position: absolute;width:1300px; height:250px; bottom: 0px; left: 100px;z-index:6; animation: sp 60s linear infinite;}
@keyframes sp {
0% { filter:hue-rotate(360deg)contrast(250%)brightness(120%); }
}
</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>
<img id="mdiv"src="https://pic1.imgdb.cn/item/690c41ba3203f7be00db7fed.png">
<div id="toggleButton">多行歌词</div>
<canvas id="glcanvas" width="900" height="420"></canvas>
<canvas id='canv'width="1300" height="250"></canvas>
<audio id="audio" src="https://aod.cos.tx.xmcdn.com/storages/88c2-audiofreehighqps/2D/16/GKwRIDoIHq0BABsn0QIUAUEq.m4a" loop autoplay crossOrigin="anonymous"></audio>
</div>
<script>
const state = {
currentLyricIndex: 0,
isAudioEnabled: true,
isPlaying: false,
mouseX: 0,
mouseY: 0,
isHovering: false,
lyrics: [],
hoverValue: 0,
hoverSpeed: 0.05, // 降低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)) {
console.error('着色器错误:', gl.getShaderInfoLog(shader));
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.5,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.3,dist);
float pulseEffect = 0.7 + 0.3*sin(u_time*4.0 + dist*8.0);
vec3 goldColor = vec3(0.9, 0.6+0.3*pulseEffect, 0.2+0.2*sin(u_time*3.0));
vec3 blueColor = vec3(0.3+0.4*sin(u_time*2.0 + dist*6.0), 0.6+0.3*pulseEffect, 0.9);
spectrumColor = mix(spectrumColor, mix(goldColor, blueColor, 0.5), hoverIntensity*u_hover*0.8);
glow += hoverIntensity*u_hover*0.8;
}
col.rgb = mix(col.rgb, spectrumColor, 0.6*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) {
tctx.clearRect(0, 0, textCanvas.width, textCanvas.height);
tctx.fillStyle = '#000';
tctx.font = '600 42px "Ma Shan Zheng","仿宋体","SimHei", "Arial"';
tctx.textAlign = 'center';
tctx.textBaseline = 'middle';
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.001;
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) => {
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 = `
抹去泪水 - 韩宝仪
词:卡斯
曲:陈宏
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);
mdiv.onclick = () => audio.paused ?audio.play(): audio.pause();
mState = () => {bj.style.setProperty('--state', audio.paused ?'paused' : 'running');
};
audio.onplaying = audio.onpause = () => mState();;
let fss = true;toggleButton.innerText = '多行歌词';
toggleButton.onclick = () => {
if (fss) {toggleButton.innerText = '多行歌词';
lyricPreview.style.display = 'none';
glcanvas.style.display = 'block';
}else {toggleButton.innerText = '特效歌词';
lyricPreview.style.display = 'block';
glcanvas.style.display = 'none';
}
fss = !fss;
};
</script>
<script>
(function () {
let Act = new AudioContext();
let audSrc = Act.createMediaElementSource(audio);
let analyser = Act.createAnalyser();
audSrc.connect(analyser);
analyser.connect(Act.destination);
let ctx = canv.getContext('2d');
let width = canv.width;
let height = canv.height;
let ppColor = ctx.createLinearGradient(250,200,250,0);
ppColor.addColorStop(0.98, '#ffaa00');
let ppNum = 1300;
let voiceHeight = new Uint8Array(analyser.frequencyBinCount);
(function draw() {
analyser.getByteFrequencyData(voiceHeight);
let step = Math.round(voiceHeight.length / ppNum);
ctx.clearRect(0, 0, width, height);
for (let j = 0; j < ppNum; j++) {
let audiheighteight = voiceHeight;
ctx.fillStyle = ppColor;
ctx.fillRect(width / 2+ (j * 4), height, 3, -audiheighteight);
ctx.fillRect(width / 2- (j * 4), height, 3, -audiheighteight);
}
window.requestAnimationFrame(draw);
})();
})();
</script>
漂亮~谢谢友友精彩分享 klxf 发表于 2026-3-10 12:11
漂亮~谢谢友友精彩分享
谢谢友友支持! 好作品,欣赏,学习了。赞! 武朝歌 发表于 2026-3-10 20:19
好作品,欣赏,学习了。赞!
谢谢老师光临!
页:
[1]