Skip to content

Commit

Permalink
Upload v1.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
YanJi314 committed Aug 5, 2024
1 parent 3decbf6 commit 6b89509
Show file tree
Hide file tree
Showing 7 changed files with 219 additions and 2 deletions.
2 changes: 0 additions & 2 deletions .gitattributes

This file was deleted.

17 changes: 17 additions & 0 deletions demo/demo.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@

@font-face{font-family:'icon';src:url('//s4.zstatic.net/ajax/libs/remixicon/4.2.0/remixicon.woff2');}
html{background:#F9F9FB;}
body{margin:0;overflow:hidden;}
font{font-family:icon;}
*{scrollbar-width:none;box-sizing:border-box;user-select:none;outline:none;}
::-webkit-scrollbar{display:none;}

button{background:#0292FE;color:white;border:0;border-radius:5px;padding:5px 20px;font-size:1rem;transition:filter .2s;font-family:inherit;height:40px;}
button:hover{filter:brightness(.95);}
button:active{filter:brightness(.9);}
audio{margin-bottom:10px;width:100%;}
textarea{background:white;border-radius:5px;border:0;margin-bottom:10px;height:100%;resize:none;padding:5px 10px;white-space:nowrap;}

#input{position:fixed;left:10px;top:10px;height:calc(100% - 20px);width:calc(50% - 15px);display:flex;flex-direction:column;}
#render{position:fixed;right:10px;top:10px;height:calc(100% - 20px);width:calc(50% - 15px);background:white;}
#container{width:100%;height:100%;}
10 changes: 10 additions & 0 deletions demo/demo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

let lrcInstance;

function render() {
let options = {callback: text => {document.title = text + " - 风吹故里";}};
try { options = JSON.parse(document.getElementById("confInput").value); }
catch (_ignore) {}
lrcInstance = new SimLRC(document.getElementById("lrcInput").value);
lrcInstance.render(document.getElementById("container"), document.getElementById("audio"), options);
}
Binary file added demo/demo.webm
Binary file not shown.
47 changes: 47 additions & 0 deletions demo/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
<link rel="stylesheet" href="demo.css">
<link rel="stylesheet" href="../simlrc.css">
<title>SimLRC Demo</title>
</head>
<body>

<div id="input">
<textarea id="lrcInput" placeholder="歌词文本(LRC 格式)...">[00:00.00]风吹故里
[00:16.41]风声静谧 秋雨依依
[00:22.53]时光又锁住一年的雨季
[00:31.11]潮湿空气 几分熟悉
[00:37.56]像那时相遇 你身上的气息
[00:44.83][01:40.17]黑白记忆 如同电影
[00:49.64][01:44.89]遗憾的剧情用离别布景
[00:53.09][01:48.39]漫长流年 独你缺席
[00:56.30][01:51.53]残缺的结局 是我一个人的独角戏
[00:59.73][01:54.91][02:24.42]我把思念写尽风中 吹过故里
[01:03.99][01:59.22][02:28.73]多想能落在你怀里 片刻光景
[01:07.67][02:02.91][02:32.61]可数不清的四季 已抹去爱的痕迹
[01:14.31][02:09.69][02:39.18]我把回忆轻轻拾起 放在掌心
[01:18.69][02:13.96][02:43.56]可惜你模糊的身影 无法靠近
[01:22.39][02:17.77][02:47.30]无数个失眠夜里 伤痛又会被唤醒
这是一行没啥用的文本,不会被渲染在歌词中
你可以尝试编辑或打乱歌词后再次点击「渲染」按钮查看效果
默认配置下,时间标签相同的不同歌词将作为多语言翻译渲染
</textarea>
<textarea id="confInput" placeholder="配置信息(JSON 格式),详情查看文档 ..."></textarea>
<audio src="demo.webm" controls id="audio"></audio>
<button onclick="render()">渲染 <font>&#xEA6C;</font></button>
</div>

<div id="render">
<div id="container">
<div style="position:absolute;opacity:.5;inset:0;margin:auto;width:fit-content;height:fit-content;">按「渲染」以开始</div>
</div>
</div>



<script src="../simlrc.js"></script>
<script src="demo.js"></script>
</body>
</html>
8 changes: 8 additions & 0 deletions simlrc.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

.SimLRC{position:relative;overflow-x:hidden;overflow-y:scroll;text-align:var(--align);}
.SimLRC::before,.SimLRC::after{content:"";display:block;height:50%;}
.SimLRC>div{color:var(--normalColor);margin:calc(var(--lineSpace) * var(--inactiveZoom)) .2em;font-size:1.5em;transform:scale(var(--inactiveZoom));transform-origin:center var(--align);transition:all .3s;}
.SimLRC.scrolling>div{filter:none!important;}
.SimLRC>div.active{color:var(--activeColor);transform:scale(1);margin:var(--lineSpace) .2em;}
.SimLRC>div:hover{color:var(--hoverColor);}
.SimLRC>div>span,.SimLRC>div>small{display:block;}
137 changes: 137 additions & 0 deletions simlrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@

class SimLRC {
constructor(lrc, container = null) {
// 解析歌词
const lrcSpilitted = lrc.split("\n");
this.lrcParsed = {};
for (let lineNum in lrcSpilitted) {
const line = lrcSpilitted[lineNum];
const regex = /\[\d+\:\d+\.\d+\]/g;
const tags = (line.match(regex) || []).map(match => match.slice(1, -1));
const text = line.replace(regex, "").trim();
if (!tags || !text) continue;
tags.forEach(tag => {
const [minutes, seconds] = tag.split(':').map(Number);
const msTime = minutes * 60000 + seconds * 1000;
if (msTime || msTime === 0) {
if (!this.lrcParsed[msTime]) this.lrcParsed[msTime] = [];
this.lrcParsed[msTime].push(text);
}
});
}
if (!Object.keys(this.lrcParsed).length) this.lrcParsed = {0: ["暂无歌词"]};
}

render(container, audio, options = {}) {
if (!container || !audio) return;
// 初始化配置项
const defaultOptions = {
blurStep: 1,
blurMin: 2,
blurMax: 5,
normalColor: "#00000088",
activeColor: "#000000",
clickUpdate: true,
multiLangSupport: true,
align: "center",
inactiveZoom: .8,
lineSpace: .8,
scrollTimeout: 3000,
};
options = Object.assign(defaultOptions, options);
console.log(options)
// 渲染歌词HTML
container.innerHTML = "";
for (let timestamp in this.lrcParsed) {
const currentLrc = this.lrcParsed[timestamp];
if (options.multiLangSupport) {
// 启用多语言支持,则同时间戳不同歌词在同一个div渲染
const lrcDiv = document.createElement("div");
lrcDiv.dataset.stamp = timestamp;
currentLrc.forEach((text, index) => {
const textElement = document.createElement(index ? "small" : "span");
textElement.innerText = text;
lrcDiv.appendChild(textElement);
});
container.appendChild(lrcDiv);
} else {
// 禁用多语言支持,则同时间戳不同歌词分开渲染
currentLrc.forEach(text => {
const lrcDiv = document.createElement("div");
lrcDiv.dataset.stamp = timestamp;
lrcDiv.innerText = text;
container.appendChild(lrcDiv);
});
}
}
// 设置样式
container.classList.add("SimLRC");
container.style.setProperty("--align", options.align);
container.style.setProperty("--normalColor", options.normalColor);
container.style.setProperty("--activeColor", options.activeColor);
container.style.setProperty("--hoverColor", options.clickUpdate ? options.activeColor : options.normalColor);
container.style.setProperty("--inactiveZoom", options.inactiveZoom);
container.style.setProperty("--lineSpace", options.lineSpace + "em");
// 监听事件
const refreshLrcProgress = forceScroll => {
const currentTime = audio.currentTime * 1000;
let lrcEles = Array.from(container.getElementsByTagName("div"));
for (let index in lrcEles) {
let div = lrcEles[index];
if (div.dataset.stamp <= currentTime && (!div.nextElementSibling || div.nextElementSibling.dataset.stamp > currentTime)) {
// 执行回调
if (!div.classList.contains("active") && options.callback) options.callback(div.innerText);
if (!div.classList.contains("active") || forceScroll) {
// 取消用户滚动模式
if (forceScroll) container.classList.remove("scrolling");
// 设置为当前歌词并滚动
div.classList.add("active");
if (!container.classList.contains("scrolling")) div.scrollIntoView({ behavior: "smooth", block: "center" });
// 渲染歌词模糊效果
if (options.blurStep && options.blurMax) {
div.style.filter = "none";
const prevSiblings = [];
let prev = div.previousElementSibling;
while (prev) {
prevSiblings.push(prev);
prev = prev.previousElementSibling;
}
let next = div.nextElementSibling;
const nextSiblings = [];
while (next) {
nextSiblings.push(next);
next = next.nextElementSibling;
}
for (let index = 0; index <= Math.max(prevSiblings.length, nextSiblings.length); index++) {
const blurPixel = Math.min(options.blurMin + options.blurStep * index, options.blurMax);
if (prevSiblings[index]) prevSiblings[index].style.filter = `blur(${blurPixel}px)`;
if (nextSiblings[index]) nextSiblings[index].style.filter = `blur(${blurPixel}px)`;
}
}
}
} else div.classList.remove("active");
}
};
audio.addEventListener("timeupdate", () => { refreshLrcProgress(); });
if (options.clickUpdate) {
Array.from(container.getElementsByTagName("div")).forEach(div => {
div.onclick = () => { audio.currentTime = div.dataset.stamp / 1000; refreshLrcProgress(true); };
});
}
refreshLrcProgress();
// 处理用户滚动
const handleUserScroll = () => {
clearTimeout(this.scrollTimeoutId);
this.scrollTimeoutId = setTimeout(() => {
container.classList.remove("scrolling");
refreshLrcProgress(true);
}, options.scrollTimeout);
container.classList.add("scrolling");
}
container.onwheel = handleUserScroll;
container.onpointerdown = () => { this.pointerDown = true; };
container.onpointerup = () => { this.pointerDown = false; };
container.onpointerout = () => { this.pointerDown = false; };
container.onpointermove = () => { if (this.pointerDown) handleUserScroll(); };
}
}

0 comments on commit 6b89509

Please sign in to comment.