来生再和你相爱[源码来自:了了子]
本帖最后由 亚伦影音工作室 于 2026-3-20 09:40 编辑 <br /><br /><style>@import url("https://fonts.googleapis.com/css2?family=Ma+Shan+Zheng&display=swap");
#bj {
position: relative;
width: 1280px;
height: 710px;
margin: 0 -330px;
overflow: hidden;
z-index: 1;
background:#000 url(https://pic1.imgdb.cn/item/69bb7fab16acd2f37b860150.jpg) no-repeat center/cover;
border-radius: 2px;
box-shadow: 0px 0px 4px 0px #fff;
font-family: "Ma Shan Zheng","","SimHei", "Arial", "sans-serif"
}
.intro {margin: 0px0px;z-index:1;
width: 100%;
height:100%;
position: absolute;
background: url(https://pic1.imgdb.cn/item/67162d12d29ded1a8c2f45cf.jpg), linear-gradient(45deg, #e56420, #c555aa, #3d9c31, #37bbde);
background-size: cover;
background-blend-mode: hard-light;
animation: hue-rotate 3s linear infinite;
}
@keyframes hue-rotate {
from {
filter: hue-rotate(0);
}
to {
filter: hue-rotate(360deg);
}
}
.song-title {z-index: 15;
font-size: 40px;position: absolute;
right: 60px;
top: 120px;
font-weight: 300;
color: #fff000;text-align: center;
writing-mode: vertical-rl;
margin-bottom: 0px;
letter-spacing: 5px;
animation: hue-rotate 0.5s linear infinite;
}
#lrc {
position: absolute;
left: 50%;
top: 40%;
transform: translate(-50%, -50%);
width: 900px;
height: 500px;
z-index: 8;
filter:drop-shadow(#fff 1px 0 0)drop-shadow(#fff 0 1px 0)drop-shadow(#fff -1px 0 0) drop-shadow(#fff 0 -1px0);
pointer-events: none;
}
.lrc-char {
position: absolute;
font-size: 3em;
color: #ff0000;
animation: hue-rotate 3s linear infinite;
white-space: nowrap;
left: 0;
top: 0;
transform: translate(-50%, -50%);
will-change: transform;
}
.lrc-char.spinning {
animation: spinColorContinuous 1s linear infinite;
}
.lrc-char.spinning.paused-animation {
animation-play-state: paused;
}
@keyframes spinColorContinuous {
0% {
transform: translate(-50%, -50%) rotate(0deg) scale(0.2);
filter: brightness(250%) hue-rotate(0deg);
}
50% {
transform: translate(-50%, -50%) rotate(180deg) scale(2);
filter: brightness(250%) contrast(250%)hue-rotate(180deg);
}
100% {
transform: translate(-50%, -50%) rotate(360deg) scale(1);
filter: brightness(250%) hue-rotate(360deg);
}
}
.lrc-space {
position: absolute;
color: transparent;
text-shadow: none;
left: 0;
top: 0;
transform: translate(-50%, -50%);
pointer-events: none;
}
#player {
position:absolute;top: 75%;z-index: 99;
left: 44%;transform: translate(-50%, -50%);
width: 150px;
height: 150px;
display: grid;text-align: center;
place-items: center;animation: rotateHalo 3s linear infinite,hue-rotate 1s linear infinite;
}
#rect {position: absolute;
display: grid;
place-items: center;
width:35%;
height: 35%;
clip-path: polygon(60% 0, 100% 0, 50% 50%, 40% 100%, 0 100%, 50% 50%);
}
#rect:nth-of-type(1) { background:#5500ff;transform: rotate(0deg);
}
#rect:nth-of-type(2) { background:#ff0000;transform: rotate(45deg);
}
#rect:nth-of-type(3) { background:#00aa00;transform: rotate(90deg);
}
#rect:nth-of-type(4) { background:#fff000;transform: rotate(135deg);
}
#player.paused{
animation-play-state: paused;
}
@keyframes rotateHalo {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.progress-container {
position: absolute;
bottom: 40px;
left: 50%;
transform: translateX(-50%);
width: 40%;
z-index: 20;
background:#bbb;
height: 6px;
border-radius: 2px;
cursor: pointer;
border: 0px solid rgba(255, 215, 0, 0.3);
}
.progress-bar {
height: 100%;
width: 0%;
background: #800;
border-radius: 2px;
transition: width 0.1s linear;
position: relative;
box-shadow: 0 0 0px rgba(255, 215, 0, 0.5);
}
.progress-bar::after {
display: none;
}
.time-display {
display: flex;
justify-content: space-between;
margin-top: -40px;
font-size: 20px;
color: #bbb;
}
.lyric-preview, canvas {
display: none;
}
</style>
<div id="bj">
<div class='intro'></div>
<div class="song-title"></div>
<div id="lrc"></div>
<div class="progress-container" id="progressContainer">
<div class="progress-bar" id="progressBar"></div>
<div class="time-display">
<span id="currentTime">00:00</span>
<span id="duration">00:00</span>
</div>
</div>
<div id="player" title="/">
<pid="rect"></p>
<pid="rect"></p>
<pid="rect"></p>
<pid="rect"></p>
</div>
<audio id="audio" loop></audio>
</div>
<script>
(function() {
// ===================== 歌曲数据 =====================
const songs = [
{
title: "来生再和你相爱(对唱版)",
audioUrl: "https://s2.cldisk.com/sv-w8/audio/d3/fe/48/b466ba797cabd8f7fd4f5ef3571cc118/audio.mp3",
lyrics: `
来生再和你相爱-烟嗓船长
曲:徐嘉良/词:未子夫
源码设计:了了子
代码改编:亚伦
歌词编辑:亚伦
亚伦影音工作室出品
离别站台 晚风吹过来
回忆涌入 脑海脑海
山盟海誓 化作了尘埃
泪在眼底 澎湃澎湃
彼岸花开花败又一载
我在奈何桥上等你回来
孟婆的碗 盛着前世的债
黄泉路上我也难以忘怀
如果今生 注定了分开
来世有缘 再和你相爱
蝴蝶它飞不过曾经沧海
我也戒不掉对你的依赖
如果今生 注定了分开
等下辈子我再拥你入怀
就算你走之后再没回来
岁月更替 我心不改
任春去秋来 人山与人海
你是我唯一 挚爱至爱
彼岸花开花败又一载
我在奈何桥上等你回来
孟婆的碗 盛着前世的债
黄泉路上我也难以忘怀
如果今生 注定了分开
来世有缘 再和你相爱
蝴蝶它飞不过曾经沧海
我也戒不掉对你的依赖
如果今生 注定了分开
等下辈子我再拥你入怀
就算你走之后再没回来
岁月更替 我心不改
任春去秋来 人山与人海
你是我唯一 挚爱至爱
`
}
];
function lrcTime(ar) {
let tmpAr = [];
for(let j = 0; j < ar.length - 1; j ++) {
if(j !== ar.length - 1)
tmpAr = parseFloat((ar - ar).toFixed(1));
}
let averAdd = 0;
let aver = parseInt(tmpAr.reduce((a,b) => a + b, 0) / (tmpAr.length - 1)) + averAdd;
tmpAr.push(aver);
tmpAr.forEach((item, key) => {
ar = item > aver ? aver : item;
});
return ar;
}
function getLrcAr(text) {
let lrcAr = [];
let calcRule = ;
let offset = 0;
for(let x of text.split('\n')) {
let ar = [];
let re = /\d+[\.:]\d+([\.:]\d+)?/g;
let geci = x.replace(re,'');
if(geci) {
geci = geci.replace(/[\[\]\'\"\t,\s]?/g,'');
let time = x.match(re);
if(time != null) {
for(let y of time) {
let tmp = y.match(/\d+/g);
let sec = 0;
for(let z in tmp)
sec += tmp * calcRule;
ar = ;
lrcAr.push(ar);
}
}
}
}
lrcAr.sort((a,b)=> a - b);
lrcAr = lrcAr.map(item => , item, 0]);
return lrcTime(lrcAr);
}
const currentSong = songs;
const lrcArray = getLrcAr(currentSong.lyrics);
const state = {
currentSongIndex: 0,
lyrics: lrcArray,
currentLyricIndex: 0,
isPlaying: false,
volume: 0.8,
isMuted: false,
videoMode: 3,
circularChars: [],
circularActiveIndex: -1,
currentLyricStart: 0,
currentLyricDuration: 5,
isDragging: false,
animationId: null,
};
const elements = {
audio: document.getElementById('audio'),
lrc: document.getElementById('lrc'),
progressContainer: document.getElementById('progressContainer'),
progressBar: document.getElementById('progressBar'),
currentTime: document.getElementById('currentTime'),
duration: document.getElementById('duration'),
player: document.getElementById('player'),
songTitle: document.querySelector('.song-title'),
};
const playerVideo = elements.player.querySelector('video');
function renderCircularLyric(text) {
if (!text) return;
elements.lrc.innerHTML = '';
const chars = text.split('');
const total = chars.length;
if (total === 0) return;
const centerX = 200;
const centerY = 270;
const radius = 140;
const step = total === 1 ? 0 : (2 * Math.PI) / total;
const newChars = [];
for (let i = 0; i < total; i++) {
const ch = chars;
const angle = i * step;
const x = centerX + radius * Math.cos(angle);
const y = centerY + radius * Math.sin(angle);
const span = document.createElement('span');
if (ch.trim() === '') {
span.className = 'lrc-space';
span.innerHTML = ' ';
} else {
span.className = 'lrc-char';
span.textContent = ch;
}
span.style.left = x + 'px';
span.style.top = y + 'px';
span.style.transform = 'translate(-50%, -50%)';
elements.lrc.appendChild(span);
newChars.push(span);
}
state.circularChars = newChars;
state.circularActiveIndex = -1;
}
function updateCircularActivation(now) {
const chars = state.circularChars;
if (!chars || chars.length === 0) return;
const start = state.currentLyricStart;
const duration = state.currentLyricDuration;
let progress = (now - start) / duration;
if (progress < 0) progress = 0;
if (progress > 1) progress = 1;
let targetIndex;
if (progress >= 1) {
targetIndex = chars.length - 1;
} else {
targetIndex = Math.floor(progress * chars.length);
}
if (targetIndex < 0) targetIndex = 0;
if (targetIndex >= chars.length) targetIndex = chars.length - 1;
if (targetIndex !== state.circularActiveIndex) {
if (state.circularActiveIndex !== -1) {
chars?.classList.remove('spinning', 'paused-animation');
}
chars?.classList.add('spinning');
if (!state.isPlaying) {
chars?.classList.add('paused-animation');
}
state.circularActiveIndex = targetIndex;
}
}
function animationLoop() {
if (!state.isPlaying) return;
const currentTime = elements.audio.currentTime;
const lyrics = state.lyrics;
for (let i = 0; i < lyrics.length; i++) {
const currentLyricTime = lyrics;
const nextLyricTime = i < lyrics.length - 1 ? lyrics : Infinity;
if (currentTime >= currentLyricTime && currentTime < nextLyricTime) {
if (state.currentLyricIndex !== i) {
state.currentLyricIndex = i;
const text = lyrics;
renderCircularLyric(text);
state.currentLyricStart = currentLyricTime;
state.currentLyricDuration = lyrics;
}
break;
}
}
updateCircularActivation(currentTime);
const duration = elements.audio.duration;
if (duration) {
const progressPercent = (currentTime / duration) * 100;
elements.progressBar.style.width = `${progressPercent}%`;
elements.currentTime.textContent = formatTime(currentTime);
elements.duration.textContent = formatTime(duration);
}
state.animationId = requestAnimationFrame(animationLoop);
}
function formatTime(seconds) {
const mins = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60);
return `${mins}:${secs < 10 ? '0' : ''}${secs}`;
}
function togglePlayback() {
if (elements.audio.paused) {
elements.audio.play().catch(e => console.log(e));
} else {
elements.audio.pause();
}
updatePlayerState();
}
function updatePlayerState() {
if (elements.audio.paused) {
state.isPlaying = false;
if (playerVideo) playerVideo.pause();
elements.player.classList.add('paused');
document.querySelectorAll('#lrc .lrc-char.spinning').forEach(char => {
char.classList.add('paused-animation');
});
if (state.animationId) {
cancelAnimationFrame(state.animationId);
state.animationId = null;
}
} else {
state.isPlaying = true;
if (playerVideo) playerVideo.play().catch(e => {});
elements.player.classList.remove('paused');
document.querySelectorAll('#lrc .lrc-char.spinning').forEach(char => {
char.classList.remove('paused-animation');
});
if (!state.animationId) {
state.animationId = requestAnimationFrame(animationLoop);
}
}
}
function bindEvents() {
elements.player.addEventListener('click', togglePlayback);
elements.progressContainer.addEventListener('click', (e) => {
const rect = elements.progressContainer.getBoundingClientRect();
const percent = (e.clientX - rect.left) / rect.width;
elements.audio.currentTime = percent * elements.audio.duration;
});
elements.progressContainer.addEventListener('mousedown', (e) => {
state.isDragging = true;
const rect = elements.progressContainer.getBoundingClientRect();
const percent = (e.clientX - rect.left) / rect.width;
elements.audio.currentTime = percent * elements.audio.duration;
});
document.addEventListener('mousemove', (e) => {
if (state.isDragging) {
const rect = elements.progressContainer.getBoundingClientRect();
const percent = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
elements.audio.currentTime = percent * elements.audio.duration;
}
});
document.addEventListener('mouseup', () => { state.isDragging = false; });
elements.audio.addEventListener('play', updatePlayerState);
elements.audio.addEventListener('pause', updatePlayerState);
elements.audio.addEventListener('seeked', () => {
if (state.lyrics.length) {
const now = elements.audio.currentTime;
for (let i = 0; i < state.lyrics.length; i++) {
const currentTime = state.lyrics;
const nextTime = i < state.lyrics.length-1 ? state.lyrics : Infinity;
if (now >= currentTime && now < nextTime) {
state.currentLyricIndex = i;
renderCircularLyric(state.lyrics);
state.currentLyricStart = currentTime;
state.currentLyricDuration = state.lyrics;
updateCircularActivation(now);
break;
}
}
}
});
document.addEventListener('keydown', (e) => {
if (e.code === 'Space') { e.preventDefault(); togglePlayback(); }
else if (e.code === 'ArrowLeft') elements.audio.currentTime -= 5;
else if (e.code === 'ArrowRight') elements.audio.currentTime += 5;
});
}
functioninitVideos() {}
async function init() {
elements.audio.src = currentSong.audioUrl;
elements.audio.loop = true;
elements.songTitle.textContent = currentSong.title;
initVideos();
state.videoMode = 3;
if (state.lyrics.length > 0) {
renderCircularLyric(state.lyrics);
state.currentLyricStart = state.lyrics;
state.currentLyricDuration = state.lyrics;
}
bindEvents();
elements.audio.play().then(() => {
updatePlayerState();
}).catch(() => {
const hint = document.createElement('div');
hint.textContent = '点击任意位置开始播放';
hint.style.position = 'absolute';
hint.style.top = '50%';
hint.style.left = '50%';
hint.style.transform = 'translate(-50%, -50%)';
hint.style.background = 'rgba(139, 0, 0, 0.8)';
hint.style.color = '#ffd700';
hint.style.padding = '15px 30px';
hint.style.borderRadius = '10px';
hint.style.zIndex = '30';
hint.style.cursor = 'pointer';
hint.style.border = '1px solid #ffd700';
document.getElementById('bj').appendChild(hint);
hint.addEventListener('click', function start() {
elements.audio.play().then(() => {
updatePlayerState();
hint.remove();
});
});
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();
const intro= document.querySelector('.intro');
let mState = () => audio.paused ? (intro.style.animationPlayState = 'paused') : (intro.style.animationPlayState = 'running');
audio.addEventListener('play', () => mState());
audio.addEventListener('pause', () => mState());
</script> 这个制作真漂亮,环形歌词同步很别致,变色标题字的设计也漂亮。
欣赏亚伦老师好帖{:4_187:} 了了子的这个效果真不错,亚伦老师的制作也很赞{:4_187:} 亚纶这个了了子老师的代码制作的歌词非常漂亮{:4_199:} 小辣椒 发表于 2026-3-20 22:13
亚纶这个了了子老师的代码制作的歌词非常漂亮
很多地方加了特效,对代码进行了修改简化了很多! 这个歌词同步很有创意。点赞!
亚伦影音工作室 发表于 2026-3-20 22:38
很多地方加了特效,对代码进行了修改简化了很多!
看得出啊,效果也是不一样的
页:
[1]