马黑黑 发表于 2023-2-18 20:30

原生lrc歌词同步(测试二)

本帖最后由 马黑黑 于 2023-2-18 20:40 编辑 <br /><br /><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=2015286363.mp3" controls loop autoplay></audio>
        <div id="lrc" data-lrc="HCPlayer">HCPlayer</div>
</div>

<script>

/*原始lrc歌词*/
let lrcStr = `
韩红 - 望
长风落进斜阳
悲歌多断肠
一卷天下涛与浪
惊魂破绝然踏蹄扬
燃尽卑微的我
无声轻轻坠落
怀一腔血赤胆忠良
撑一撑就到天亮
迢迢归乡之路
孤单伴着满足
曾几时梦里
遇见过你
就此别过亦是幸福
遥遥壮烈时刻
幸得慷慨一搏
不畏浮云遮望眼
何惧世间起浓烟
唱一生忠义
颂英雄之躯
蓦然间回首
收紧了心头
听那首人间悲曲
一曲悲歌
一世离合
皆化作山河
一曲悲歌
一世离合
仰天长啸一首歌
感谢欣赏
`;

/*变量 :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*2 数组*/
let getLrcAr = (str) => {
        str = str.trim();
        let lines = [], lrcAr = [];
        let reg = /\[(\d{1,}:\d{1,}.\d{1,})\](.*)/g;
        if(!str.match(reg)) return;
        lines = str.replace(reg,'$1-{}-$2').split('\n');
        for(k = 0; k < lines.length; k ++) {
                lrcAr = [];
                for(j = 0; j < 3; j ++) {
                        let tmpAr = lines.split('-{}-');
                        lrcAr = j === 0 ? toSecs(tmpAr) : tmpAr;
                }
        }
        return lrcTime(lrcAr); /* 数组变为 n*3 */
};

/*函数 :原始lrc时间转为秒数*/
let toSecs = (lrcTime) => {
        let reg = /\d{2,}/g;
        let ar = lrcTime.match(reg);
        return ar*60 + parseInt(ar) + parseInt((ar)/1000);
};

/*函数 :模拟显示同步歌词*/
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-18 20:30

代码
<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=2015286363.mp3" controls loop autoplay></audio>
        <div id="lrc" data-lrc="HCPlayer">HCPlayer</div>
</div>

<script>

/*原始lrc歌词*/
let lrcStr = `
韩红 - 望
长风落进斜阳
悲歌多断肠
一卷天下涛与浪
惊魂破绝然踏蹄扬
燃尽卑微的我
无声轻轻坠落
怀一腔血赤胆忠良
撑一撑就到天亮
迢迢归乡之路
孤单伴着满足
曾几时梦里
遇见过你
就此别过亦是幸福
遥遥壮烈时刻
幸得慷慨一搏
不畏浮云遮望眼
何惧世间起浓烟
唱一生忠义
颂英雄之躯
蓦然间回首
收紧了心头
听那首人间悲曲
一曲悲歌
一世离合
皆化作山河
一曲悲歌
一世离合
仰天长啸一首歌
感谢欣赏
`;

/*变量 :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*2 数组*/
let getLrcAr = (str) => {
        str = str.trim();
        let lines = [], lrcAr = [];
        let reg = /\[(\d{1,}:\d{1,}.\d{1,})\](.*)/g;
        if(!str.match(reg)) return;
        lines = str.replace(reg,'$1-{}-$2').split('\n');
        for(k = 0; k < lines.length; k ++) {
                lrcAr = [];
                for(j = 0; j < 3; j ++) {
                        let tmpAr = lines.split('-{}-');
                        lrcAr = j === 0 ? toSecs(tmpAr) : tmpAr;
                }
        }
        return lrcTime(lrcAr); /* 数组变为 n*3 */
};

/*函数 :原始lrc时间转为秒数*/
let toSecs = (lrcTime) => {
        let reg = /\d{2,}/g;
        let ar = lrcTime.match(reg);
        return ar*60 + parseInt(ar) + parseInt((ar)/1000);
};

/*函数 :模拟显示同步歌词*/
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-18 20:33

此测试与测试一相比:解决分行歌词的问题,即,原始lrc歌词可以是这样定义——

let lrcStr = `
第一句歌词
第二句歌词
第N句歌词
`;

你若喜欢,每句歌词信息可以不顶格

马黑黑 发表于 2023-2-18 20:36

说明一下:网络lrc歌词,正如 @小辣椒 所说的,都不太准确,有条件、有时间还是自己制作的好,这类软件还是很多的。如果不想自己制作,那么,可以自己调整,大约仅调整秒数就好

起个网名好难 发表于 2023-2-18 21:05

parseInt((ar)/1000)恒等于 0

醉美水芙蓉 发表于 2023-2-18 21:41

红影 发表于 2023-2-18 21:50

歌词信息顶格和不顶格没明白是怎么回事。

红影 发表于 2023-2-18 21:50

黑黑一弄再弄,辛苦了{:4_187:}

马黑黑 发表于 2023-2-18 23:10

红影 发表于 2023-2-18 21:50
黑黑一弄再弄,辛苦了

辛苦谈不上

庶民 发表于 2023-2-19 05:05

好方式,有创意!

马黑黑 发表于 2023-2-19 08:40

庶民 发表于 2023-2-19 05:05
好方式,有创意!

{:5_108:}

小辣椒 发表于 2023-2-19 13:27

小辣椒迟到的观众{:4_170:}

马黑黑 发表于 2023-2-19 13:56

本帖最后由 马黑黑 于 2023-2-19 14:12 编辑

起个网名好难 发表于 2023-2-18 21:05
parseInt((ar)/1000)恒等于 0
对,强制整数。开始用的是parseFloat,后来反复修改改错地方了。

马黑黑 发表于 2023-2-19 13:57

小辣椒 发表于 2023-2-19 13:27
小辣椒迟到的观众

测试中,没关系

起个网名好难 发表于 2023-2-19 15:50

马黑黑 发表于 2023-2-19 13:56
对,强制整数。开始用的是parseFloat,后来反复修改改错地方了。

反正是秒也就无所谓了

马黑黑 发表于 2023-2-19 18:14

起个网名好难 发表于 2023-2-19 15:50
反正是秒也就无所谓了

嗯,试过效果,区别肉眼几乎看不出

起个网名好难 发表于 2023-2-19 18:24

马黑黑 发表于 2023-2-19 18:14
嗯,试过效果,区别肉眼几乎看不出

确实

起个网名好难 发表于 2023-2-19 18:33

马黑黑 发表于 2023-2-19 18:14
嗯,试过效果,区别肉眼几乎看不出

请教“获取每句歌词用时,歌词用时若超过平均值则取平均值,最后一句歌词则取平均值” 有什么根据

红影 发表于 2023-2-19 18:47

马黑黑 发表于 2023-2-18 23:10
辛苦谈不上

前面光顾着看代码了,现在听来这首还真好听呢{:4_187:}

马黑黑 发表于 2023-2-19 19:15

红影 发表于 2023-2-19 18:47
前面光顾着看代码了,现在听来这首还真好听呢

猜猜谁唱的
页: [1] 2
查看完整版本: 原生lrc歌词同步(测试二)