论坛大侠帮我控制了动画文字感谢!
本帖最后由 亚伦影音工作室 于 2025-11-7 20:21 编辑 <br /><br /><style>#papa{
position: relative;
width: 1286px;
height: 720px;
margin-left: -300px;
margin-top: 10px;
overflow: hidden;z-index:12345;
background:url(https://pic1.imgdb.cn/item/68625f9658cb8da5c87f755d.jpg)no-repeat center/cover;
--state: running;
font-family: Arial,sans-serif
}
canvas{
max-width: 100%;
height: auto;
display: block;
cursor: pointer;
transition: transform 0.3s ease
}
canvas:hover {
transform:scale(1.05);
}
.lyrics{margin: 0;z-index: 20;
top: 89%;
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;
top: 0;
left: 0;
width: 0;
overflow: hidden;
color: #8B4513;
height: 100%;
white-space: nowrap;
}
.lyric-original {
color: #ag0000;
white-space: nowrap;
}
li-zi {
position: absolute;
width: 2px;
height: 2px;
left: 300px;
bottom: 300px;
border-radius: 50%;
box-shadow: inset 0 0 11px #00aa00;
background: #fff000;
}
@keyframes moving {
from { opacity: 1; transform: translate(0,0); }
to { opacity: 0.1; transform: translate(var(--x0),var(--y0)); }
}
#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);} }
</style>
<div id="papa">
<canvas id="glcanvas" width="1200" height="420"></canvas>
<li-zi id="lz" ></li-zi>
<div class="lyrics" >
<div class="lyric-line">
<div class="lyric-mask"></div>
<div class="lyric-original"></div>
</div>
</div>
<img id="mdiv"src="https://pic1.imgdb.cn/item/690c41ba3203f7be00db7fed.png">
</div>
<audio id="audio" src="https://cccimg.com/view.php/fa3a6fe1c45ddd2b6f0dbf41ec2d28bc.mp3"loopautoplay ></audio>
<script>
mdiv.onclick = () => audio.paused ?audio.play(): audio.pause();
mState = () => {papa.style.setProperty('--state', audio.paused ?'paused' : 'running');
mdiv.style.setProperty(requestAnimationFrame(draw), audio.paused ?'paused' : 'running');
};
audio.onplaying = audio.onpause = () => mState();
</script>
<script>
const canvas = document.getElementById('glcanvas');
const gl = canvas.getContext('webgl');
if (!gl) {
alert('WebGL no soportado');
}
let mouseX = 0;
let mouseY = 0;
let isHovering = false;
// Variables para el efecto hover
canvas.addEventListener('mousemove', (e) => {
if (!audio.paused) {
const rect = canvas.getBoundingClientRect();
mouseX = (e.clientX - rect.left) / rect.width;
mouseY = 1.0 - (e.clientY - rect.top) / rect.height; // Invertir Y
isHovering = true;
}
});
canvas.addEventListener('mouseleave', () => {
isHovering = false;
});
function compileShader(src, type) {
const s = gl.createShader(type);
gl.shaderSource(s, src);
gl.compileShader(s);
if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) {
console.error(gl.getShaderInfoLog(s));
}
return s;
}
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() {
// Corregir coordenadas UV para que el texto no est茅 al rev茅s
vec2 correctedUV = vec2(v_uv.x, 1.0 - v_uv.y);
// Efecto de derretimiento base
float strength = smoothstep(0.2, 1.0, correctedUV.y);
float n = noise(vec2(correctedUV.x * 5.0, (correctedUV.y * 2.0 - u_time * 0.6) * 2.0));
float melt = n * 0.4 * (correctedUV.y); // Cambiar para que se derrita desde arriba
// Efecto hover controlado - ondas suaves y elegantes
float dist = distance(correctedUV, u_mouse);
float hoverWave1 = sin(dist * 12.0 - u_time * 6.0) * 0.08 * u_hover * exp( - dist * 3.5);
float hoverWave2 = sin(dist * 20.0 - u_time * 10.0) * 0.04 * u_hover * exp( - dist * 5.0);
float hoverWave = hoverWave1 + hoverWave2;
// Efecto de distorsi贸n magn茅tica controlado
vec2 hoverDistort = normalize(correctedUV - u_mouse) * hoverWave * (1.0 + sin(u_time * 2.0) * 0.1);
// Combinar efectos
vec2 finalUV = vec2(correctedUV.x + melt * 0.3 + hoverDistort.x, correctedUV.y - melt * 0.8 + hoverDistort.y);
vec4 col = texture2D(u_text, finalUV);
// Efecto de brillo base con colores tipo espectro
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);
// Efecto hover controlado - colores suaves y elegantes
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);
// Efecto de color dorado suave
vec3 goldColor = vec3(0.9, 0.6 + 0.3 * pulseEffect, 0.2 + 0.2 * sin(u_time * 3.0));
// Efecto de color azul elegante
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);
// Efecto de part铆culas controlado en hover
if (u_hover > 0.1) {
// Part铆culas suaves y elegantes
float particles = noise(correctedUV * 15.0 + u_time * 2.0);
if (particles > 0.85 && dist < 0.2) {
col.rgb += vec3(1.0, 0.8, 0.4) * (particles - 0.85) * 8.0 * u_hover;
}
// Efecto de brillo sutil
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;
}
}
}
// Efecto de iluminaci贸n ambiental controlado en 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;
// Aura suave y elegante
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;
// Efecto de halo exterior sutil
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 u_time = gl.getUniformLocation(program, 'u_time');
const u_text = gl.getUniformLocation(program, 'u_text');
const u_mouse = gl.getUniformLocation(program, 'u_mouse');
const u_hover = gl.getUniformLocation(program, 'u_hover');
const textCanvas = document.createElement('canvas');
const tctx = textCanvas.getContext('2d');
textCanvas.width = 1200;
textCanvas.height = 420;
tctx.fillStyle = '#000';
tctx.font = '600 100px 微软雅黑';
tctx.textAlign = 'center';
tctx.textBaseline = 'middle';
tctx.fillText('终于把你遇见', textCanvas.width / 2, textCanvas.height / 2);
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);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, textCanvas);
// Suavizar la transici贸n del hover
let hoverValue = 0;
const hoverSpeed = 0.05;
function draw(t) {
// Suavizar el efecto hover
if (isHovering && hoverValue < 1.0) {
hoverValue = Math.min(1.0, hoverValue + hoverSpeed);
} else if (!isHovering && hoverValue > 0.0) {
hoverValue = Math.max(0.0, hoverValue - hoverSpeed);
}
gl.viewport(0, 0, canvas.width, canvas.height);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(program);
gl.uniform1f(u_time, t * 0.001);
gl.uniform2f(u_mouse, mouseX, mouseY);
gl.uniform1f(u_hover, hoverValue);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, textTex);
gl.uniform1i(u_text, 0);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
if (!audio.paused) requestAnimationFrame(draw);
}
</script>
<script>
// 歌词解析ksc歌词或lrc歌词
const lrc = `karaoke.add('00:02.847', '00:06.903', '《终于把你遇见》', '496,464,368,392,416,416,424,1080'); karaoke.add('00:11.815', '00:15.768', '词曲/徐子崴', '560,608,656,521,528,1080'); karaoke.add('00:18.351', '00:24.063', '演唱/徐子崴|罗姣', '728,576,544,544,576,568,536,560,1080'); karaoke.add('00:28.944', '00:34.776', '如果一生有一百年', '560,344,696,1264,936,416,536,1080'); karaoke.add('00:35.360', '00:41.352', '不过是三万六千五百天', '384,776,424,311,1144,601,360,367,545,1080'); karaoke.add('00:41.929', '00:47.848', '我穿过河流啊我越过高山', '431,265,319,472,833,799,456,304,543,417,1080'); karaoke.add('00:48.433', '00:54.336', '只为能和你见呀见一面', '304,1191,344,424,920,408,328,424,480,1080'); karaoke.add('00:54.892', '01:00.818', '如果一生有一百年', '375,384,720,1480,784,320,783,1080'); karaoke.add('01:01.387', '01:07.267', '说起来很长说来也很短', '360,800,392,776,848,368,336,408,512,1080'); karaoke.add('01:07.879', '01:13.775', '我仰望日月啊我祈求上天', '352,344,911,234,719,624,344,304,600,384,1080'); karaoke.add('01:14.419', '01:21.836', '只为能和你走呀走一段', '376,1129,328,455,817,1264,368,864,736,1080'); karaoke.add('01:27.503', '01:31.630', '多想把你遇见', '872,472,543,344,816,1080'); karaoke.add('01:33.750', '01:38.102', '恰好在对的时间', '368,752,536,392,344,880,1080'); karaoke.add('01:40.246', '01:44.815', '我愿用前世的苦难', '425,808,424,543,513,367,409,1080'); karaoke.add('01:46.703', '01:51.078', '换与你今生的情缘', '352,344,808,423,392,360,616,1080'); karaoke.add('01:53.292', '01:57.548', '终于把你遇见', '992,592,368,432,792,1080'); karaoke.add('01:59.692', '02:04.069', '恰好在对的时间', '409,743,465,392,408,880,1080'); karaoke.add('02:06.141', '02:10.525', '我愿以今生的相伴', '480,856,392,536,456,200,384,1080'); karaoke.add('02:12.716', '02:17.036', '许和你来世的圆满', '392,1048,369,295,257,415,464,1080'); karaoke.add('02:20.595', '02:26.916', '字幕制作/翁奕童苑', '496,481,496,544,625,695,680,640,584,1080'); karaoke.add('02:45.204', '02:51.011', '如果一生有一百年', '336,384,728,1568,552,328,831,1080'); karaoke.add('02:51.555', '02:57.403', '说起来很长说来也很短', '408,760,440,760,848,360,344,344,504,1080'); karaoke.add('02:58.092', '03:03.996', '我仰望日月啊我祈求上天', '375,608,529,232,352,1008,376,328,392,624,1080'); karaoke.add('03:04.515', '03:12.107', '只为能和你走呀走一段', '609,944,344,424,911,808,864,832,776,1080'); karaoke.add('03:14.504', '03:18.655', '多想把你遇见', '1088,384,463,344,792,1080'); karaoke.add('03:20.815', '03:25.128', '恰好在对的时间', '337,767,464,385,408,872,1080'); karaoke.add('03:27.320', '03:31.688', '我愿用前世的苦难', '400,728,464,344,288,672,392,1080'); karaoke.add('03:33.759', '03:38.135', '换与你今生的情缘', '352,376,872,312,328,296,760,1080'); karaoke.add('03:40.232', '03:44.567', '终于把你遇见', '1255,392,328,448,832,1080'); karaoke.add('03:46.567', '03:51.143', '恰好在对的时间', '504,712,520,432,433,895,1080'); karaoke.add('03:53.224', '03:57.583', '我愿以今生的相伴', '431,769,423,353,576,295,432,1080'); karaoke.add('03:59.719', '04:04.054', '许和你来世的圆满', '351,801,448,343,344,537,431,1080'); karaoke.add('04:06.229', '04:19.277', '许和你来世的圆满', '425,848,471,361,5392,656,3815,1080'); karaoke.add('04:25.509', '04:29.358', '谢谢欣赏', '857,728,1184,1080');
`;
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 = [];
if (lrcText.includes('karaoke.add')) {
const lineRegex = /karaoke\.add\('([^']+)', '([^']+)', '([^']+)', '([^']+)'\);/g;
let match;
while ((match = lineRegex.exec(lrcText)) !== null) {
const startTime = timeToMs(match);
const endTime = timeToMs(match);
const text = match.replace(/\[|\]/g, '').trim();
const durations = match.split(',').map(Number);
if (text) {
lyrics.push({startTime, endTime, text, durations});
}
}
}
else if (lrcText.includes('[')) {
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(/\[.*?\]/, '').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 lyricStartTime = currentLyric.startTime;
const elapsed = currentTimeMs - lyricStartTime;
const totalDuration = currentLyric.durations.reduce((sum, d) => sum + d, 0);
let charIndex = 0;
let accumulatedTime = 0;
for (let i = 0; i < currentLyric.durations.length; i++) {
accumulatedTime += currentLyric.durations;
if (elapsed <= accumulatedTime) {
charIndex = i + 1;
break;
}
}
if (elapsed >= totalDuration) {
charIndex = currentLyric.text.length;
}
charIndex = Math.min(charIndex, currentLyric.text.length);
const tempSpan = document.createElement('span');
tempSpan.style.visibility = 'hidden';
tempSpan.style.position = 'absolute';
tempSpan.style.fontSize = '50px';
tempSpan.style.fontWeight = '800';
document.body.appendChild(tempSpan);
const visibleText = currentLyric.text.substring(0, charIndex);
tempSpan.textContent = visibleText;
const width = tempSpan.offsetWidth;
document.body.removeChild(tempSpan);
lyricMask.style.width = `${width}px`;
}
// 监听更新歌词
audio.addEventListener('timeupdate', () => {
const currentTimeMs = audio.currentTime * 1000;
const index = getCurrentLyricIndex(lyrics, currentTimeMs);
if (index !== currentIndex) {
updateLyricDisplay(index);
}
updateLyricMask(currentTimeMs);
});
updateLyricDisplay(0);
</script>
<script>
for(let i = 0, all = 2000; i < all; i++) {
let lz = document.createElement('li-zi');
let hudu = Math.PI / 180 * 360 / all * i;
let xx = 900 * Math.cos(hudu), yy = 600 * Math.sin(hudu);
lz.style.cssText += `
--x0: ${xx}px;
--y0: ${yy}px;
left: 360px;
bottom: 360px;
animation: moving ${Math.random() * 6 + 3}s -${Math.random() * 6}s infinite var(--state);
`;
papa.prepend(lz);
}
</script> https://upfile.mp3.wf/view.php/e04cb8f1f9ee19a35ace89ec80b4d958.html 波浪线的文字挺好看的{:4_199:}背景和动画真漂亮!歌曲也好听。{:4_204:} 本帖最后由 也曾年轻 于 2025-11-7 20:25 编辑 <br /><br /><!--
<script>
const canvas = document.getElementById('glcanvas');
const gl = canvas.getContext('webgl');
if (!gl) {
alert('WebGL no soportado');
}
let mouseX = 0;
let mouseY = 0;
let isHovering = false;
// Variables para el efecto hover
canvas.addEventListener('mousemove', (e) => {
if (!audio.paused) {
const rect = canvas.getBoundingClientRect();
mouseX = (e.clientX - rect.left) / rect.width;
mouseY = 1.0 - (e.clientY - rect.top) / rect.height; // Invertir Y
isHovering = true;
}
});
canvas.addEventListener('mouseleave', () => {
isHovering = false;
});
function compileShader(src, type) {
const s = gl.createShader(type);
gl.shaderSource(s, src);
gl.compileShader(s);
if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) {
console.error(gl.getShaderInfoLog(s));
}
return s;
}
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() {
// Corregir coordenadas UV para que el texto no est茅 al rev茅s
vec2 correctedUV = vec2(v_uv.x, 1.0 - v_uv.y);
// Efecto de derretimiento base
float strength = smoothstep(0.2, 1.0, correctedUV.y);
float n = noise(vec2(correctedUV.x * 5.0, (correctedUV.y * 2.0 - u_time * 0.6) * 2.0));
float melt = n * 0.4 * (correctedUV.y); // Cambiar para que se derrita desde arriba
// Efecto hover controlado - ondas suaves y elegantes
float dist = distance(correctedUV, u_mouse);
float hoverWave1 = sin(dist * 12.0 - u_time * 6.0) * 0.08 * u_hover * exp( - dist * 3.5);
float hoverWave2 = sin(dist * 20.0 - u_time * 10.0) * 0.04 * u_hover * exp( - dist * 5.0);
float hoverWave = hoverWave1 + hoverWave2;
// Efecto de distorsi贸n magn茅tica controlado
vec2 hoverDistort = normalize(correctedUV - u_mouse) * hoverWave * (1.0 + sin(u_time * 2.0) * 0.1);
// Combinar efectos
vec2 finalUV = vec2(correctedUV.x + melt * 0.3 + hoverDistort.x, correctedUV.y - melt * 0.8 + hoverDistort.y);
vec4 col = texture2D(u_text, finalUV);
// Efecto de brillo base con colores tipo espectro
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);
// Efecto hover controlado - colores suaves y elegantes
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);
// Efecto de color dorado suave
vec3 goldColor = vec3(0.9, 0.6 + 0.3 * pulseEffect, 0.2 + 0.2 * sin(u_time * 3.0));
// Efecto de color azul elegante
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);
// Efecto de part铆culas controlado en hover
if (u_hover > 0.1) {
// Part铆culas suaves y elegantes
float particles = noise(correctedUV * 15.0 + u_time * 2.0);
if (particles > 0.85 && dist < 0.2) {
col.rgb += vec3(1.0, 0.8, 0.4) * (particles - 0.85) * 8.0 * u_hover;
}
// Efecto de brillo sutil
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;
}
}
}
// Efecto de iluminaci贸n ambiental controlado en 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;
// Aura suave y elegante
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;
// Efecto de halo exterior sutil
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 u_time = gl.getUniformLocation(program, 'u_time');
const u_text = gl.getUniformLocation(program, 'u_text');
const u_mouse = gl.getUniformLocation(program, 'u_mouse');
const u_hover = gl.getUniformLocation(program, 'u_hover');
const textCanvas = document.createElement('canvas');
const tctx = textCanvas.getContext('2d');
textCanvas.width = 1200;
textCanvas.height = 420;
tctx.fillStyle = '#000';
tctx.font = '600 120px 微软雅黑';
tctx.textAlign = 'center';
tctx.textBaseline = 'middle';
tctx.fillText('终于把你遇见', textCanvas.width / 2, textCanvas.height / 2);
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);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, textCanvas);
// Suavizar la transici贸n del hover
let hoverValue = 0;
const hoverSpeed = 0.05;
function draw(t) {
// Suavizar el efecto hover
if (isHovering && hoverValue < 1.0) {
hoverValue = Math.min(1.0, hoverValue + hoverSpeed);
} else if (!isHovering && hoverValue > 0.0) {
hoverValue = Math.max(0.0, hoverValue - hoverSpeed);
}
gl.viewport(0, 0, canvas.width, canvas.height);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(program);
gl.uniform1f(u_time, t * 0.001);
gl.uniform2f(u_mouse, mouseX, mouseY);
gl.uniform1f(u_hover, hoverValue);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, textTex);
gl.uniform1i(u_text, 0);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
if (!audio.paused) requestAnimationFrame(draw);
}
// 歌词解析ksc歌词或lrc歌词
const lrc = `karaoke.add('00:02.847', '00:06.903', '《终于把你遇见》', '496,464,368,392,416,416,424,1080');
karaoke.add('00:11.815', '00:15.768', '词曲/徐子崴', '560,608,656,521,528,1080');
karaoke.add('00:18.351', '00:24.063', '演唱/徐子崴|罗姣', '728,576,544,544,576,568,536,560,1080');
karaoke.add('00:28.944', '00:34.776', '如果一生有一百年', '560,344,696,1264,936,416,536,1080');
karaoke.add('00:35.360', '00:41.352', '不过是三万六千五百天', '384,776,424,311,1144,601,360,367,545,1080');
karaoke.add('00:41.929', '00:47.848', '我穿过河流啊我越过高山', '431,265,319,472,833,799,456,304,543,417,1080');
karaoke.add('00:48.433', '00:54.336', '只为能和你见呀见一面', '304,1191,344,424,920,408,328,424,480,1080');
karaoke.add('00:54.892', '01:00.818', '如果一生有一百年', '375,384,720,1480,784,320,783,1080');
karaoke.add('01:01.387', '01:07.267', '说起来很长说来也很短', '360,800,392,776,848,368,336,408,512,1080');
karaoke.add('01:07.879', '01:13.775', '我仰望日月啊我祈求上天', '352,344,911,234,719,624,344,304,600,384,1080');
karaoke.add('01:14.419', '01:21.836', '只为能和你走呀走一段', '376,1129,328,455,817,1264,368,864,736,1080');
karaoke.add('01:27.503', '01:31.630', '多想把你遇见', '872,472,543,344,816,1080');
karaoke.add('01:33.750', '01:38.102', '恰好在对的时间', '368,752,536,392,344,880,1080');
karaoke.add('01:40.246', '01:44.815', '我愿用前世的苦难', '425,808,424,543,513,367,409,1080');
karaoke.add('01:46.703', '01:51.078', '换与你今生的情缘', '352,344,808,423,392,360,616,1080');
karaoke.add('01:53.292', '01:57.548', '终于把你遇见', '992,592,368,432,792,1080');
karaoke.add('01:59.692', '02:04.069', '恰好在对的时间', '409,743,465,392,408,880,1080');
karaoke.add('02:06.141', '02:10.525', '我愿以今生的相伴', '480,856,392,536,456,200,384,1080');
karaoke.add('02:12.716', '02:17.036', '许和你来世的圆满', '392,1048,369,295,257,415,464,1080');
karaoke.add('02:20.595', '02:26.916', '字幕制作/翁奕童苑', '496,481,496,544,625,695,680,640,584,1080');
karaoke.add('02:45.204', '02:51.011', '如果一生有一百年', '336,384,728,1568,552,328,831,1080');
karaoke.add('02:51.555', '02:57.403', '说起来很长说来也很短', '408,760,440,760,848,360,344,344,504,1080');
karaoke.add('02:58.092', '03:03.996', '我仰望日月啊我祈求上天', '375,608,529,232,352,1008,376,328,392,624,1080');
karaoke.add('03:04.515', '03:12.107', '只为能和你走呀走一段', '609,944,344,424,911,808,864,832,776,1080');
karaoke.add('03:14.504', '03:18.655', '多想把你遇见', '1088,384,463,344,792,1080');
karaoke.add('03:20.815', '03:25.128', '恰好在对的时间', '337,767,464,385,408,872,1080');
karaoke.add('03:27.320', '03:31.688', '我愿用前世的苦难', '400,728,464,344,288,672,392,1080');
karaoke.add('03:33.759', '03:38.135', '换与你今生的情缘', '352,376,872,312,328,296,760,1080');
karaoke.add('03:40.232', '03:44.567', '终于把你遇见', '1255,392,328,448,832,1080');
karaoke.add('03:46.567', '03:51.143', '恰好在对的时间', '504,712,520,432,433,895,1080');
karaoke.add('03:53.224', '03:57.583', '我愿以今生的相伴', '431,769,423,353,576,295,432,1080');
karaoke.add('03:59.719', '04:04.054', '许和你来世的圆满', '351,801,448,343,344,537,431,1080');
karaoke.add('04:06.229', '04:19.277', '许和你来世的圆满', '425,848,471,361,5392,656,3815,1080');
karaoke.add('04:25.509', '04:29.358', '谢谢欣赏', '857,728,1184,1080');
`;
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 = [];
if (lrcText.includes('karaoke.add')) {
const lineRegex = /karaoke\.add\('([^']+)', '([^']+)', '([^']+)', '([^']+)'\);/g;
let match;
while ((match = lineRegex.exec(lrcText)) !== null) {
const startTime = timeToMs(match);
const endTime = timeToMs(match);
const text = match.replace(/\[|\]/g, '').trim();
const durations = match.split(',').map(Number);
if (text) {
lyrics.push({
startTime,
endTime,
text,
durations
});
}
}
} else if (lrcText.includes('[')) {
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(/\[.*?\]/, '').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 lyricStartTime = currentLyric.startTime;
const elapsed = currentTimeMs - lyricStartTime;
const totalDuration = currentLyric.durations.reduce((sum, d) => sum + d, 0);
let charIndex = 0;
let accumulatedTime = 0;
for (let i = 0; i < currentLyric.durations.length; i++) {
accumulatedTime += currentLyric.durations;
if (elapsed <= accumulatedTime) {
charIndex = i + 1;
break;
}
}
if (elapsed >= totalDuration) {
charIndex = currentLyric.text.length;
}
charIndex = Math.min(charIndex, currentLyric.text.length);
const tempSpan = document.createElement('span');
tempSpan.style.visibility = 'hidden';
tempSpan.style.position = 'absolute';
tempSpan.style.fontSize = '50px';
tempSpan.style.fontWeight = '800';
document.body.appendChild(tempSpan);
const visibleText = currentLyric.text.substring(0, charIndex);
tempSpan.textContent = visibleText;
const width = tempSpan.offsetWidth;
document.body.removeChild(tempSpan);
lyricMask.style.width = `$ {
width
}
px`;
}
// 监听更新歌词
audio.addEventListener('timeupdate', () => {
const currentTimeMs = audio.currentTime * 1000;
const index = getCurrentLyricIndex(lyrics, currentTimeMs);
if (index !== currentIndex) {
updateLyricDisplay(index);
}
updateLyricMask(currentTimeMs);
});
updateLyricDisplay(0);
mdiv.onclick = () => audio.paused ? (audio.play(), requestAnimationFrame(draw)) : audio.pause();
audio.addEventListener('playing', () => mdiv.style.animationPlayState = 'running');
audio.addEventListener('pause', () => mdiv.style.animationPlayState = 'paused');
mdiv.style.animationPlayState = audio.paused ? 'paused': 'running';
</script>
--> 也曾年轻 发表于 2025-11-7 18:25
谢谢老师我已经改过了! 这个标题字会跳舞,真漂亮。
欣赏亚伦老师好帖{:4_199:} 有趣的探索,吸睛的特效,尤其文字特效
谢谢亚伦老师,谢谢也曾年轻老师{:4_176:} 亚纶又出新花招了,上次那个小圆的二个图我做好了,结果图的尺寸做大了,转换就不灵了,明天改一下再发 小辣椒 发表于 2025-11-7 22:40
亚纶又出新花招了,上次那个小圆的二个图我做好了,结果图的尺寸做大了,转换就不灵了,明天改一下再发
恭喜小辣椒作品圆满完成! 亚伦影音工作室 发表于 2025-11-8 11:00
恭喜小辣椒作品圆满完成!
我没有时间玩,作业也是没有时间做,但很喜欢亚纶的代码制作{:4_199:} 欣赏老师的新代码,点赞!
页:
[1]