原生lrc歌词同步(测试三)
<style>#papa {
margin: auto;
padding: 20px;
width: 700px;
height: 400px;
box-shadow: 3px 3px 20px #000;
position: relative;
}
#lrc {
--motion: cover2;
--tt: 2s;
--state: paused;
--bg: linear-gradient(180deg, hsla(60, 50%, 50%, .45), hsla(80, 70%, 50%, .65));
position: absolute;
font: bold 2em sans-serif;
color: snow;
white-space: pre;
-webkit-background-clip: text;
filter: drop-shadow(1px 1px 2px hsla(0, 0%, 0%, .95));
}
#lrc::before {
position: absolute;
content: attr(data-lrc);
width: 20%;
height: 100%;
color: transparent;
overflow: hidden;
white-space: pre;
background: var(--bg);
filter: inherit;
-webkit-background-clip: text;
animation: var(--motion) var(--tt) linear forwards;
animation-play-state: var(--state);
}
@keyframes cover1 { from { width: 0; } to { width: 100%; } }
@keyframes cover2 { from { width: 0; } to { width: 100%; } }
@keyframes rot { to { transform: rotate(1turn); } }
</style>
<div id="papa">
<audio id="aud" src="https://music.163.com/song/media/outer/url?id=2022397273.mp3" controls loop autoplay></audio>
<div id="lrc" data-lrc="HCPlayer">HCPlayer</div>
</div>
<script>
/*原始lrc歌词*/
let lrcStr = `
邹沛沛 - 爱如火(浪漫版)
Oh Na na na yeah Ah oh
心在跳 是爱情如烈火
你在笑 疯狂的人是我
爱如火 会温暖了心窝
我看见 爱的火焰闪烁
从第一刻见到你
哦我的心在怦怦跳
刻板标准都不重要
自你闯入我瞳孔
蝴蝶拥着火微笑
时间暂停掉
你说的每个字
我都牢记在心头
见面的每一次
都有不同的心动
我只想一辈子
都有你牵我的手
走到很久以后
Oh my baby
心在跳 是爱情如烈火
你在笑 疯狂的人是我
爱如火 会温暖了心窝
我看见 爱的火焰闪烁
Na na na na na na yeah
Woo woo
爱的火焰闪烁
Na na na na na na yeah
Woo woo my baby
`;
/*变量 :mKey - 当前歌词索引;mFlag :调用关键帧动画索引*/
let mKey = 0, mFlag = true;
/*函数 :获取每句歌词用时,歌词用时若超过平均值则取平均值,最后一句歌词则取平均值*/
let lrcTime = (ar) => {
let tmpAr = [];
for(j = 0; j <ar.length - 1; j ++) {
if(j !== ar.length - 1) tmpAr = parseFloat((ar - ar).toFixed(1));
}
let aver = parseInt(tmpAr.reduce((a,b) => a + b) / (tmpAr.length - 1));
tmpAr.push(aver);
tmpAr.forEach((item,key) => {
ar = item > aver ? aver : item;
});
return ar;
};
/*函数 :从原始lrc歌词获取信息并存入 n*3 数组*/
let getLrcAr = (text) => {
let lrcAr = [];
let calcRule = ;
for(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(y of time) {
let tmp = y.match(/\d+/g);
let sec = 0;
for(z in tmp) sec += tmp * calcRule;
ar = ;
lrcAr.push(ar);
}
}
}
}
lrcAr.sort((a,b)=> a - b);
return(lrcTime(lrcAr));
};
/*函数 :模拟显示同步歌词*/
let showLrc = (time) => {
let name = mFlag ? 'cover1' : 'cover2';
lrc.innerHTML = lrcAr;
lrc.dataset.lrc = lrcAr;
lrc.style.setProperty('--motion', name);
lrc.style.setProperty('--tt', time + 's');
lrc.style.setProperty('--state', 'running');
mKey += 1;
mFlag = !mFlag;
};
/*函数 :处理当前歌词索引 mKey*/
let calcKey = () => {
for (j = 0; j < lrcAr.length; j++) {
if (aud.currentTime <= lrcAr) {
mKey = j - 1;
break;
}
}
if (mKey < 0) mKey = 0;
if (mKey > lrcAr.length - 1) mKey = lrcAr.length - 1;
let time = lrcAr - (aud.currentTime - lrcAr);
showLrc(time);
};
/*格式化时间信息*/
let toMin = (val) => {
if (!val) return '00:00';
val = Math.floor(val);
let min = parseInt(val / 60),
sec = parseFloat(val % 60);
if (min < 10) min = '0' + min;
if (sec < 10) sec = '0' + sec;
return min + ':' + sec;
}
/*函数 :歌词同步状态切换*/
let mState = () => lrc.style.setProperty('--state', aud.paused ? 'paused' : 'running');
/*监听播放进度*/
aud.addEventListener('timeupdate', () => {
for (j = 0; j < lrcAr.length; j++) {
if (aud.currentTime >= lrcAr) {
cKey = j;
if (mKey === j) showLrc(lrcAr);
else continue;
}
}
});
aud.addEventListener('pause', () => mState());/*监听暂停事件*/
aud.addEventListener('play', () => mState());/*监听播放事件*/
aud.addEventListener('seeked', () => calcKey());/*监听查询事件*/
let lrcAr = getLrcAr(lrcStr); /*获得歌词数组*/
</script> 代码
<style>
#papa {
margin: auto;
padding: 20px;
width: 700px;
height: 400px;
box-shadow: 3px 3px 20px #000;
position: relative;
}
#lrc {
--motion: cover2;
--tt: 2s;
--state: paused;
--bg: linear-gradient(180deg, hsla(60, 50%, 50%, .45), hsla(80, 70%, 50%, .65));
position: absolute;
font: bold 2em sans-serif;
color: snow;
white-space: pre;
-webkit-background-clip: text;
filter: drop-shadow(1px 1px 2px hsla(0, 0%, 0%, .95));
}
#lrc::before {
position: absolute;
content: attr(data-lrc);
width: 20%;
height: 100%;
color: transparent;
overflow: hidden;
white-space: pre;
background: var(--bg);
filter: inherit;
-webkit-background-clip: text;
animation: var(--motion) var(--tt) linear forwards;
animation-play-state: var(--state);
}
@keyframes cover1 { from { width: 0; } to { width: 100%; } }
@keyframes cover2 { from { width: 0; } to { width: 100%; } }
@keyframes rot { to { transform: rotate(1turn); } }
</style>
<div id="papa">
<audio id="aud" src="https://music.163.com/song/media/outer/url?id=2022397273.mp3" controls loop autoplay></audio>
<div id="lrc" data-lrc="HCPlayer">HCPlayer</div>
</div>
<script>
/*原始lrc歌词*/
let lrcStr = `
邹沛沛 - 爱如火(浪漫版)
Oh Na na na yeah Ah oh
心在跳 是爱情如烈火
你在笑 疯狂的人是我
爱如火 会温暖了心窝
我看见 爱的火焰闪烁
从第一刻见到你
哦我的心在怦怦跳
刻板标准都不重要
自你闯入我瞳孔
蝴蝶拥着火微笑
时间暂停掉
你说的每个字
我都牢记在心头
见面的每一次
都有不同的心动
我只想一辈子
都有你牵我的手
走到很久以后
Oh my baby
心在跳 是爱情如烈火
你在笑 疯狂的人是我
爱如火 会温暖了心窝
我看见 爱的火焰闪烁
Na na na na na na yeah
Woo woo
爱的火焰闪烁
Na na na na na na yeah
Woo woo my baby
`;
/*变量 :mKey - 当前歌词索引;mFlag :调用关键帧动画索引*/
let mKey = 0, mFlag = true;
/*函数 :获取每句歌词用时,歌词用时若超过平均值则取平均值,最后一句歌词则取平均值*/
let lrcTime = (ar) => {
let tmpAr = [];
for(j = 0; j <ar.length - 1; j ++) {
if(j !== ar.length - 1) tmpAr = parseFloat((ar - ar).toFixed(1));
}
let aver = parseInt(tmpAr.reduce((a,b) => a + b) / (tmpAr.length - 1));
tmpAr.push(aver);
tmpAr.forEach((item,key) => {
ar = item > aver ? aver : item;
});
return ar;
};
/*函数 :从原始lrc歌词获取信息并存入 n*3 数组*/
let getLrcAr = (text) => {
let lrcAr = [];
let calcRule = ;
for(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(y of time) {
let tmp = y.match(/\d+/g);
let sec = 0;
for(z in tmp) sec += tmp * calcRule;
ar = ;
lrcAr.push(ar);
}
}
}
}
lrcAr.sort((a,b)=> a - b);
return(lrcTime(lrcAr));
};
/*函数 :模拟显示同步歌词*/
let showLrc = (time) => {
let name = mFlag ? 'cover1' : 'cover2';
lrc.innerHTML = lrcAr;
lrc.dataset.lrc = lrcAr;
lrc.style.setProperty('--motion', name);
lrc.style.setProperty('--tt', time + 's');
lrc.style.setProperty('--state', 'running');
mKey += 1;
mFlag = !mFlag;
};
/*函数 :处理当前歌词索引 mKey*/
let calcKey = () => {
for (j = 0; j < lrcAr.length; j++) {
if (aud.currentTime <= lrcAr) {
mKey = j - 1;
break;
}
}
if (mKey < 0) mKey = 0;
if (mKey > lrcAr.length - 1) mKey = lrcAr.length - 1;
let time = lrcAr - (aud.currentTime - lrcAr);
showLrc(time);
};
/*格式化时间信息*/
let toMin = (val) => {
if (!val) return '00:00';
val = Math.floor(val);
let min = parseInt(val / 60),
sec = parseFloat(val % 60);
if (min < 10) min = '0' + min;
if (sec < 10) sec = '0' + sec;
return min + ':' + sec;
}
/*函数 :歌词同步状态切换*/
let mState = () => lrc.style.setProperty('--state', aud.paused ? 'paused' : 'running');
/*监听播放进度*/
aud.addEventListener('timeupdate', () => {
for (j = 0; j < lrcAr.length; j++) {
if (aud.currentTime >= lrcAr) {
cKey = j;
if (mKey === j) showLrc(lrcAr);
else continue;
}
}
});
aud.addEventListener('pause', () => mState());/*监听暂停事件*/
aud.addEventListener('play', () => mState());/*监听播放事件*/
aud.addEventListener('seeked', () => calcKey());/*监听查询事件*/
let lrcAr = getLrcAr(lrcStr); /*获得歌词数组*/
</script>
本帖最后由 马黑黑 于 2023-2-19 11:02 编辑
测试三相关说明:
一、对lrc歌词格式的支持
mm:ss.ms例:12:02.69 或 02:36.659 或 14:5.8 等
mm:ss:ms例:12:02:69 或 02:36:659 或 14:5:8 等
并:实现机制能自动过滤多余字符;支持一行中存在多个时间标签(歌词叠句)。
二、歌词变量的书写方式
第一种:写在一行里,每一个原始行单位用 \n 表示,最后一句歌词末尾不加 \n 。歌词属于字符串,所以需要用反引号 ` 或 小角单引号 ' 或 小角双引号 " 将歌词包裹起来。例:
let lrcStr = `歌词一\n歌词二\n歌词N`;
第二种:分行写。分行写只能使用反引号将歌词包裹起来。各行可以顶格或退格书写。分行书写时,行尾不能有 \n 符号。例:
let lrcStr = `
歌词一
歌词二
歌词N
`;
不论何种书写方式,歌词内若有小角引号 ' ,请将其转义,写成 : \'
三、其他未尽事宜碰到再说
欣赏美乐,问候朋友。 梦油 发表于 2023-2-19 09:06
欣赏美乐,问候朋友。
感谢支持 黑黑越修改越完美了,厉害{:4_199:} 马黑黑 发表于 2023-2-19 10:14
感谢支持
别客气。 红影 发表于 2023-2-19 11:19
黑黑越修改越完美了,厉害
{:4_190:} 醉美水芙蓉 发表于 2023-2-19 12:54
老师厉害!这么快就做好了!
这个,有部分是原来做好的 原来黑黑已经捣鼓三次了{:4_199:} 小辣椒 发表于 2023-2-19 13:26
原来黑黑已经捣鼓三次了
还需要鼓捣 马黑黑 发表于 2023-2-19 13:57
还需要鼓捣
黑黑我直接套用以前做的,感觉还是你的精确度高,你这个是逐字逐句的,我那以前是一整句出来的,应该很完美,我还没有上传,马上就好的 小辣椒 发表于 2023-2-19 14:00
黑黑我直接套用以前做的,感觉还是你的精确度高,你这个是逐字逐句的,我那以前是一整句出来的,应该很完 ...
好的 谢谢老师的代码分享,问好!{:4_204:} 梦缘 发表于 2023-2-19 14:35
谢谢老师的代码分享,问好!
下午好 马黑黑 发表于 2023-2-19 08:51
测试三相关说明:
一、对lrc歌词格式的支持
请教疑问
for(z in tmp) sec += tmp * calcRule;
14:5.8 按这样计算得到 14*60+5+0.008 , 毫秒无形中就被去掉了。
geci = geci.replace(/[\[\]\'\"\t,]s?/g,'');
如果去掉其中的 \'\" 就不需要对歌词中的引号转义了。 起个网名好难 发表于 2023-2-21 07:18
请教疑问
14:5.8 按这样计算得到 14*60+5+0.008 , 毫秒无形中就被去掉了。
也是
起个网名好难 发表于 2023-2-21 07:34
感谢反馈。新做的函数已经改变了机制。
页:
[1]