本帖最后由 花简静 于 2026-1-22 11:04 编辑 <br /><br /><style>
/*
名称 :花潮LRC在线增强版
制作 :马黑黑
更新 :2022年9月23日
简述 :用于制作加餐格式的花潮LRC,兼容以往帖子模板
声明 :转载与转用请注明作者与出处
*/
.outer { margin: auto; width: fit-content; height: fit-content; }
.outer h2 { margin: 10px; padding: 0; font: bold 1.5em sans-serif; text-align: center; }
.outer p { margin: 10px; }
.outer input { margin: 2px; font: normal 16px sans-serif; outline: none; }
.menu { margin: 0 0 -1px 8px; width: fit-content; height: fit-content; display: flex; position: relative; }
.item { width: 60px; height: 30px; font: normal 1em / 30px sans-serif; text-align: center; background: #fff; border: 1px solid; border-bottom: none; cursor: pointer; }
.item1 { border: none; border-bottom: 1px solid; background: none; }
#slip { width: 50px; text-align: center; }
#lrcText { padding: 10px; width: 720px; height: 400px; font: normal 16px / 26px sans-serif; resize: both; border: 1px solid; outline: none; }
#mUrl { padding: 4px; width: 450px; font-size: 14px; }
#aud, #up { display: none; outline: none; }
#audMsg { display: inline-block; margin-left: 12px; height: 54px; font: normal 1em / 54px sans-serif; }
</style>
</head>
<body>
<div class="outer">
<h2>花潮LRC在线增强版</h2>
<div class="menu">
<div class="item">制作</div>
<div class="item item1">转换</div>
<div class="item item1">微调</div>
</div>
<textarea id="lrcText" rows ="18" cols="60" placeholder="原始歌词"></textarea>
<p>
<input id="begin" type="button" value=" 制作 " />
<input id="up" type="button" value=" ↑↑ " />
<input id="copy" type="button" value=" 复制 " disabled />
<input id="mUrl" type="text" placeholder="音频地址" value="" />
<label>误差值 : </label>
<input id="slip" type="number" placeholder="0" value="0.2" min="0" max="1" step="0.1" />
</p>
<p style="display:flex">
<audio id="aud" controls="controls"></audio>
<span id="audMsg"></span>
</p>
</div>
<script>
let items = document.querySelectorAll('.item');
let lrcArr = [], slipNum = , btnName = ['制作','转换','微调'], modeName = ['原始歌词','mm:(.)ss.(:)ms → ss.ms','花潮LRC歌词'], mode = 0, yp, currentIdx, mkidx;
//模式切换
Array.from(items).forEach((ele,key) => {
ele.onclick = () => {
ele.className = 'item';
slip.value = slipNum;
mode = key;
begin.value = btnName;
lrcText.placeholder = modeName;
lrcText.focus();
begin.style.display = 'inline-block';
up.style.display = 'none';
aud.style.display = audMsg.style.display = 'none';
if(mode === 0) { //制作模式
lrcText.value = '';
items.className = items.className = 'item item1';
} else if(mode === 1){ //转换模式
lrcText.value = '';
items.className = items.className = 'item item1';
} else { //微调模式
items.className = items.className = 'item item1';
}
}
});
//主按钮事件
begin.onclick =() => {
if(lrcText.value === '' || mUrl.value === '') return false;
getUrl(mUrl.value);
aud.style.display = audMsg.style.display = 'inline-block';
if(mode === 0) { //制作模式
lrcArr.length = 0;
lrcArr = (lrcText.value).trim().split('\n'); //从文本框中获取数组
currentIdx = mkidx = 0;
lrcText.value = 'let lrcAr = [\n';
mUrl.value = lrcArr;
begin.style.display = 'none';
up.style.display = 'inline-block';
up.disabled = true;
} else if(mode === 1) { //转换模式
if(lrcText.value === '' || mUrl.value === '') return false;
getUrl(mUrl.value);
let str = 'let lrcAr = [\n';
lrcArr.length = 0;
lrcArr = getLrcMsg(lrcText.value);
for(x of lrcArr) str += "\t[" + x + ",'" + x + "'],\n";
str = str.substring(0, str.lastIndexOf(','));
lrcText.value = str + '\n];';
lrcText.value = getAr(lrcText.value); //转加餐格式
begin.style.display = 'none';
} else { //微调模式
lrcText.value = getAr(lrcText.value); //转加餐格式
currentIdx = 0;
lrcText.scrollTop = 0;
begin.style.display = 'none';
up.style.display = 'inline-block';
}
}
//lrc上屏
up.onclick = () => {
if(mode === 0) { //制作模式
if(currentIdx === lrcArr.length - 1) {//最后一句
lrcText.value += '\t' + mUrl.value.slice(0,-1) + '\n];';
lrcText.value = getAr(lrcText.value); //转加餐格式
up.disabled = true;
mUrl.value = yp;
} else { //其它句
lrcText.value += '\t' + mUrl.value + '\n';
lrcText.scrollTop = lrcText.scrollHeight;
if(currentIdx < lrcArr.length - 1) currentIdx ++;
mUrl.value = lrcArr;
}
mkidx ++;
audMsg.innerText = audMsg.innerText.replace(/:\s\d+/, ': ' + mkidx);
} else { //微调模式
if(lrcText.value === '' || mUrl.value === '') return false;
let txtAr = mUrl.value.split('"');
lrcAr = txtAr.trim();
lrcAr = txtAr.trim();
replaceText(currentIdx + 1, lrcText);
}
up.disabled = true;
}
//复制按钮事件
copy.onclick = () => {
lrcText.select();
document.execCommand('copy');
mUrl.value = '内容已复制到剪切板';
}
lrcText.onchange = () => copy.disabled = lrcText.value === '' ? true : false; //大文本框内容变更:复制按钮状态
slip.onchange = () => { //误差值改变
slipNum = slip.value;
if(slipNum > 1) slipNum = 1;
if(slipNum < -1) slipNum = -1;
}
//播放器暂停监听
aud.addEventListener('pause', () =>{
if(mode === 0) { //制作模式
let time = (aud.currentTime - slipNum).toFixed(2);
mUrl.value = `[${time},"${lrcArr}"],`;
}
up.disabled = mode > 0 ? false : (mkidx >= lrcArr.length ? true : false);
});
//播放监听
aud.addEventListener('timeupdate', () =>{
let done = mode === 0 ? (' [ 完成量: ' + mkidx + ' / ' + lrcArr.length + ' ]') : (mode === 2 ? (' [ 当前操作: ' + (currentIdx + 1) + ' / ' + lrcAr.length + ' ] ') : '');
audMsg.innerText = aud.currentTime.toFixed(2) + ' | ' + (aud.duration || 0).toFixed(2) +done ;
if(mode === 2){ //微调模式
for(j=0; j<lrcAr.length; j++){
if(aud.currentTime >= lrcAr - slipNum){
currentIdx = j;
let time = (aud.currentTime - lrcAr).toFixed(1);
mUrl.value = lrcAr + ' ' + lrcAr + ' ' + time;
}
}
}
});
//转换函数 mm:(.)ss:(.)ms → ss.ms
let getLrcMsg = (text) => {
let lrcAr = []; //返回的数组
let calcRule = ; //不同数位的分钟运算规则
for(x of text.split('\n')) {
let ar = [];
let re = /\d+[\.:]\d+([\.:]\d+)?/g; //匹配 mm:(.)ss:(.)ms
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;
sec -= slipNum;
if(sec <0) sec = 0.00;
ar = ;
lrcAr.push(ar);
}
}
}
}
lrcAr.sort((a,b)=> a - b); //排序
return lrcAr;
}
//获取音频地址
let getUrl = (url) => {
//let reg = /\.?:wav|mp3|wma|ogg|aac|ape|flac$/;
//if(reg.test(url.toLowerCase())) yp = url.trim();
yp = url.trim();
aud.src = yp;
}
//函数:转文本框数组为加餐格式
let getAr = (text) => {
let re = /.*lrcAr/;
let jsStr = text.replace(re, 'lrcAr');
(new Function(jsStr))();
if(typeof(lrcAr) === 'undefined') {
alert('数据非法!请确保 lrcAr 数组完整出现在文本框中');
throw new Error('lrcAr数组加载失败');
}
for(key in lrcAr) {
let gap = parseInt(key) === lrcAr.length - 1 ? 6 : (lrcAr - lrcAr).toFixed(1);
lrcAr = '"' + lrcAr+'"';
lrcAr = gap;
}
return 'let lrcAr = [' + lrcAr.map((item) => '\n\t[' + item + ']') + '\n];';
}
//函数:选中textarea一行文本
let replaceText = (line,ele) => {
ele.value ='let lrcAr = [' + lrcAr.map((item) => `\n\t[${item},${item},${item || 'x'}]`) + '\n];';
let arr = ele.value.split('\n');
let pos1 = 0, pos2 = 0;
for(j = 0; j < line; j ++) {
pos1 += arr.length + 1;
}
pos1 += 1;
pos2 = pos1 + arr.length;
ele.setSelectionRange(pos1,pos2);
ele.scrollTop = line * 26 - 26;
ele.focus();
}
</script>
本帖最后由 花简静 于 2026-1-26 17:21 编辑
歌词可以自己做,上一楼就是黑师编写的【花潮格式歌词】制作编辑软件。
方法:
1.框内放歌词,下方填歌曲(要带.mp3)的地址
2.边听边控制歌词进行每句的时间确认。(每句开始时点击)
3.全部歌词走完了之后。
4.点微调,重走一遍(每句结束时点击)。
5.因为每句开始和结尾都有控制时间,所以走动比较精确。
6.以下附三首制好的,可试用(所有的中括号都要复制进去)
本帖最后由 花简静 于 2026-1-26 17:21 编辑
单曲:潮汐 网易地址:404784312
[
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
]
本帖最后由 花简静 于 2026-1-26 17:22 编辑
单曲:渐无书 网易地址:1363490517
[
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
]
本帖最后由 花简静 于 2026-1-26 17:22 编辑
单曲:无氧之地 网易地址:2043179016
[
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
]
{:1_150:}
{:1_150:}
{:1_150:}
{:1_150:}
{:1_150:}