|
|

楼主 |
发表于 2026-5-5 22:08
|
显示全部楼层
- /* yslrc.js 独立歌词同步 (by Mhh, 2025-8-12)
- 参数:
- @param pa : 宿主元素 dom 实体 (必选)
- @param aud :audio实体标签(必选)
- @param lrcText : 原生歌词 (必选)
- @param skip : 同步微调系数 (可选)
- @param average : 歌句用时均摊系数 (可选)
- 前台css设置:
- #lrc { 定位、修改前景色、字号等,可选 }
- #lrc::before { 主要设置同步颜色,可选 }
- 前台HTML:
- <div id="lrc" data-lrc="HC">HC</div> (可选)
- */
- const cssCode = `
- #lrc { --motion: cover1; --tt: 1s; --color1: hsla(100,10%,50%,.75); --color2: hsla(100,100%,20%,.65); position: absolute; font: bold 2.4em sans-serif; color: hsl(100, 100%, 90%); white-space: pre; cursor: default; filter: drop-shadow(1px 1px 2px hsla(0, 100%, 0%, .85)); z-index: 900; }
- #lrc::before { position: absolute; content: attr(data-lrc); width: 0%; height: 100%; color: transparent; overflow: hidden; white-space: pre; background: linear-gradient(180deg, var(--color1), var(--color2)); filter: inherit; background-clip: text; animation: var(--motion) var(--tt) linear forwards var(--state); }
- @keyframes cover1 { from { width: 0%; } to { width: 100%; } }
- @keyframes cover2 { from { width: 0%; } to { width: 100%; } }
- `;
- function LRC(pa, aud, lrcText, skip = 0, average = 0) {
- if (pa instanceof Element === false || aud instanceof Element === false || !lrcText) return;
- let curkey = 0, lrcAr;
- let cssEl = document.getElementById('lrc-only-css');
- if (!cssEl) {
- cssEl = document.createElement('style');
- cssEl.id = 'lrc-only-css';
- cssEl.type = 'text/css';
- cssEl.textContent = cssCode;
- document.head.appendChild(cssEl);
- }
- let lrc = pa.querySelector('#lrc');
- if (!lrc) {
- lrc = document.createElement('div');
- lrc.id = 'lrc';
- lrc.textContent = lrc.dataset.lrc = 'HUACHAO LRC';
- pa.appendChild(lrc);
- }
- const lrcTime = (ar) => {
- let tmpAr = [];
- for (let j = 0; j < ar.length - 1; j ++) {
- if (j !== ar.length - 1) tmpAr[j] = parseFloat((ar[j + 1][0] - ar[j][0]).toFixed(1));
- }
- let aver = parseInt(tmpAr.reduce((a, b) => a + b) / (tmpAr.length - 1)) - average || 0;
- tmpAr.push(aver);
- tmpAr.forEach((item, key) => {
- ar[key][2] = item > aver ? aver : item;
- });
- return ar;
- };
- const getLrcAr = (text) => {
- let lrc_ar = [];
- const ar = text.trim().split('\n').map(line => line.trim());
- ar.sort();
- const reg = /\[(\d+)[.:](\d+)[.:](\d+)\](.*)/;
- ar.forEach(item => {
- let result = item.match(reg);
- let tmsg = parseInt(result[1]) * 60 + parseInt(result[2]) + parseInt(result[3]) / 1000 + skip;
- lrc_ar.push([tmsg, result[4].trim()]);
- });
- return (lrcTime(lrc_ar));
- };
- lrcAr = getLrcAr(lrcText);
-
- const showLrc = (time) => {
- const ani = window.getComputedStyle(lrc,'::before').animationName;
- const name = ani === 'cover1' ? 'cover2' : 'cover1';
- lrc.innerHTML = lrcAr[curkey][1];
- lrc.dataset.lrc = lrcAr[curkey][1].replace(/<br>/, '\n');
- lrc.style.setProperty('--motion', name);
- lrc.style.setProperty('--tt', time + 's');
- curkey += 1;
- lrc.title = lrcAr.length + '/' + curkey;
- };
- const calcKey = () => {
- for (let j = 0; j < lrcAr.length; j++) {
- if (aud.currentTime <= lrcAr[j][0]) {
- curkey = j - 1;
- break;
- }
- }
- if (curkey < 0) curkey = 0;
- if (curkey > lrcAr.length - 1) curkey = lrcAr.length - 1;
- let time = lrcAr[curkey][2] - (aud.currentTime - lrcAr[curkey][0]);
- showLrc(time);
- };
- aud.ontimeupdate = () => {
- for (let j = 0; j < lrcAr.length; j++) {
- if (aud.currentTime >= lrcAr[j][0]) {
- if (curkey === j) showLrc(lrcAr[j][2]);
- else continue;
- }
- }
- };
- aud.onended = () => {
- curkey = 0;
- aud.play();
- }
- aud.onseeked = () => calcKey();
- }
复制代码
|
|