对频谱视频加字幕
本帖最后由 亚伦影音工作室 于 2025-9-17 12:12 编辑 <br /><br /><style>#bj {position: relative;
width: 1286px;
height: 720px;
margin-left: -300px;
margin-top: 120px;
overflow: hidden;z-index:12345;
background: ;
}
.intro {margin: 0px0px;z-index:1;
width: 100%;
height:100%;
position: absolute;
background:url(https://pic1.imgdb.cn/item/68be8b6558cb8da5c8883639.jpg),linear-gradient(145deg, #e56420, #c22525, #3d9c31, #000078);
background-size: cover;
animation: hue-rotate 1s linear infinite;
}
.stop.intro{animation-play-state: paused;}
@keyframes hue-rotate {
from {
filter: hue-rotate(0);
}
to {
filter: hue-rotate(360deg);
}
}
video::-webkit-media-controls-enclosure{ display: none;}
.lrc {z-index:10;
position: absolute;
width: 100%;
height: 60px; /* 只显示一行歌词高度 */
top: 88%;
left: 50%;
transform: translate(-50%, -50%); /* 容器整体居中 */
overflow: hidden;
text-align: center;
}
.lrc #ul {filter:drop-shadow(#FFFFFF 1px 0 0)drop-shadow(#FFFFFF 0 1px 0)drop-shadow(#FFFFFF -1px 0 0) drop-shadow(#FFFFFF 0 -1px0);
width: 100%;
padding: 0;
list-style: none;
transition: 0.1s all ease;
margin: 0;
display: flex;
flex-direction: column;
align-items: center;
}
.lrc #ul li {
font-size: 44px;
color: #000;
font-family: "华文隶书", "Microsoft YaHei", sans-serif;
font-weight:500;
transition: 0.1s all ease;
list-style-type: none;
text-align: center;
padding: 0 10px;
height: 60px;
line-height: 60px;
cursor: pointer;
white-space: nowrap; /* 防止歌词换行 */
}
.lrc #ul li span {
margin: 0 5px;
transform-origin: center center; /* x轴和y轴均以中心为原点 */
transition: all 0.1s cubic-bezier(0.34, 1.56, 0.64, 1); /* 缓动函数让动画更自然 */
display: inline-block; /* 确保transform-origin生效 */
}
.lrc #ul li span.active {
font-family: "华文隶书", "Microsoft YaHei", sans-serif;
font-weight: 500;
font-size: 50px;
color: red;
transform: scale(1.2)rotate(-360deg); /* 缩放效果 */
}
#bfqbnt{ width: 300px;height: 160px;position: absolute; top:90%; left: 5%;overflow: hidden;z-index: 13;}
.start{color: #fff;position: absolute; top:14px; left: 26px;z-index: 3;}
.end{color: #fff;position: absolute;top:14px; left: 150px;z-index: 3;}
#btn{background: url(https://pic.imgdb.cn/item/675813f6d0e0a243d4e14a48.png) no-repeat 0px 0px/cover;width:50px;height: 50px;left: 80px; top: 2px;position: absolute;z-index: 20;}
#prog {position: absolute;z-index: 13;
width: 210px;
height: 2px;background:#ccc;
cursor: pointer;
top:54px;
left:0%;
border-radius: 1px;}
#prog-bar {
height: 100%;
background: #FF0000;
width: 0%;
}
</style>
<divid="bj">
<video id="audio" src="https://file.uhsea.com/2509/ff02232d96efc3fef8a3256700f0922fOM.mp4" autoplay loop controls style="max-width: 60%; height: auto;z-index:2;position: absolute; mix-blend-mode: lighten;left: 50%;"></video>
<div class='intro'></div>
<div id='bfqbnt'>
<span class="start">00:00</span><p id="btn"></p><span class="end">00:00</span>
<div id="prog" title="播放进度条"><div id="prog-bar"></div></div>
</div>
<div class="lrc">
<ul id="ul"></ul>
</div>
</div>
<script>
var lrc = `karaoke.add('00:04.627', '00:09.307', '《对你的爱无怨无悔》', '480,432,424,376,376,360,400,384,464,984');
karaoke.add('00:09.307', '00:15.546', '词曲/刘习军 演唱/小芳', '448,471,440,472,544,496,480,456,440,400,512,1080');
karaoke.add('00:20.361', '00:24.881', '昨日的情还那么美', '471,312,281,1103,681,280,312,1080');
karaoke.add('00:25.425', '00:29.097', '痴情的爱人却已不归', '224,240,272,376,560,344,240,336,1080');
karaoke.add('00:29.657', '00:33.153', '浸湿眼圈装满疲惫', '240,248,360,584,328,304,352,1080');
karaoke.add('00:33.459', '00:36.011', '是否还在坚持无怨无悔', '304,248,240,224,216,368,264,232,248,208');
karaoke.add('00:39.185', '00:43.193', '伤透的心伤透的肺', '240,224,152,1584,224,264,240,1080');
karaoke.add('00:43.705', '00:47.392', '流去的时光怎么追回', '255,256,376,296,496,296,272,360,1080');
karaoke.add('00:47.976', '00:51.361', '心在滴血人在憔悴', '248,241,239,713,280,312,272,1080');
karaoke.add('00:51.969', '00:55.552', '已把执着变得如此狼狈', '272,368,400,280,248,256,232,232,215,1080');
karaoke.add('00:57.180', '01:00.836', '真心的爱已然成灰', '200,240,256,1048,304,248,280,1080');
karaoke.add('01:00.860', '01:04.620', '许下的承诺已随风飞', '272,264,312,296,624,376,264,272,1080');
karaoke.add('01:05.076', '01:08.900', '走过的路为何千折百回', '256,264,280,520,376,256,248,256,288,1080');
karaoke.add('01:09.532', '01:13.428', '迷茫之中爱你对不对', '623,272,280,312,616,233,207,273,1080');
karaoke.add('01:15.268', '01:18.884', '对你的爱无怨无悔', '280,312,280,984,224,240,216,1080');
karaoke.add('01:19.124', '01:22.524', '付出的真情付诸流水', '304,272,288,392,408,240,216,200,1080');
karaoke.add('01:23.428', '01:27.604', '孤独的我只能面对回忆', '240,272,264,616,488,248,296,232,440,1080');
karaoke.add('01:27.859', '01:32.092', '数着不该属于我的破碎', '305,335,312,320,336,328,312,321,584,1080');
karaoke.add('01:34.004', '01:39.732', '字幕制作/亚伦影音', '344,312,448,640,560,600,560,648,536,1080');
karaoke.add('01:52.420', '01:56.364', '伤透的心伤透的肺', '208,240,192,1512,216,232,264,1080');
karaoke.add('01:56.884', '02:00.484', '流去的时光怎么追回', '232,288,232,296,536,336,296,304,1080');
karaoke.add('02:00.964', '02:04.595', '心在滴血人在憔悴', '296,344,272,768,375,241,255,1080');
karaoke.add('02:04.820', '02:08.524', '已把执着变得如此狼狈', '320,264,280,440,272,303,257,271,217,1080');
karaoke.add('02:10.068', '02:13.804', '真心的爱已然成灰', '320,240,296,1104,224,256,216,1080');
karaoke.add('02:14.012', '02:17.724', '许下的承诺已随风飞', '296,264,312,424,480,344,288,224,1080');
karaoke.add('02:18.220', '02:22.275', '走过的路为何千折百回', '264,232,288,448,223,345,599,312,264,1080');
karaoke.add('02:23.148', '02:26.972', '迷茫之中爱你对不对', '319,265,320,663,241,256,272,408,1080');
karaoke.add('02:28.372', '02:32.052', '对你的爱无怨无悔', '280,304,304,944,256,248,264,1080');
karaoke.add('02:32.300', '02:35.740', '付出的真情付诸流水', '296,280,304,440,304,232,256,248,1080');
karaoke.add('02:36.524', '02:40.483', '孤独的我只能面对回忆', '256,248,256,312,311,544,344,296,312,1080');
karaoke.add('02:41.068', '02:45.292', '数着不该属于我的破碎', '416,320,288,312,327,361,296,304,520,1080');
karaoke.add('02:46.596', '02:50.444', '真心的爱已然成灰', '376,288,288,1088,240,248,240,1080');
karaoke.add('02:50.556', '02:54.284', '许下的承诺已随风飞', '280,248,376,408,464,272,336,264,1080');
karaoke.add('02:54.804', '02:58.788', '走过的路为何千折百回', '247,273,280,423,240,409,471,297,264,1080');
karaoke.add('02:59.436', '03:03.508', '迷茫之中爱你对不对', '376,280,312,776,248,264,400,336,1080');
karaoke.add('03:04.892', '03:08.540', '对你的爱无怨无悔', '312,296,343,808,305,240,264,1080');
karaoke.add('03:08.844', '03:12.396', '付出的真情付诸流水', '320,312,312,455,265,239,273,296,1080');
karaoke.add('03:13.107', '03:16.979', '孤独的我只能面对回忆', '257,288,281,279,231,552,281,335,288,1080');
karaoke.add('03:17.956', '03:22.060', '数着不该属于我的破碎', '336,280,272,312,296,312,304,592,320,1080');
karaoke.add('03:33.436', '03:40.084', '谢谢欣赏', '1584,1768,2216,1080');
`;
let lrcArr = lrc.split('\n');
let result = [];
var audio = document.querySelector("#audio"),
ul = document.querySelector("#ul"),
container = document.querySelector(".lrc");
const lrcRegex = /karaoke\.add\('([\d:/.]+)',\s*'([\d:/.]+)',\s*'([^']*)'/;
for (let i = 0; i < lrcArr.length; i++) {
const line = lrcArr.trim();
if (!line) continue;
const match = line.match(lrcRegex);
if (!match) continue;
const startTimeStr = match;
const endTimeStr = match;
const lineText = match.trim();
if (!lineText) continue;
const startTime = parseTime(startTimeStr);
const endTime = parseTime(endTimeStr);
const lineDuration = endTime - startTime;
const words = lineText.split('');
const wordCount = words.length;
if (wordCount === 0) continue;
const wordDuration = lineDuration / wordCount;
const lineWords = [];
words.forEach((word, index) => {
lineWords.push({
time: startTime + index * wordDuration,
text: word
});
});
result.push(lineWords);
}
function parseTime(timeStr) {
const = timeStr.split(':');
return parseInt(minutes, 10) * 60 + parseFloat(seconds);
}
function getCurrentLineIndex() {
const currentTime = audio.currentTime;
for (let i = 0; i < result.length; i++) {
const line = result;
if (currentTime >= line.time &&
(i === result.length - 1 || currentTime < result.time)) {
return i;
}
}
return -1;
}
function createLyricElements() {
const frag = document.createDocumentFragment();
result.forEach((lineWords, lineIndex) => {
const li = document.createElement("li");
lineWords.forEach(wordObj => {
const span = document.createElement("span");
span.textContent = wordObj.text;
span.dataset.time = wordObj.time;
li.appendChild(span);
});
li.addEventListener('click', () => {
audio.currentTime = lineWords.time;
});
frag.appendChild(li);
});
ul.appendChild(frag);
}
createLyricElements();
const liHeight = 60;
const visibleLines = 1;
let prevLineIndex = -1;
function syncLyric() {
const currentLineIndex = getCurrentLineIndex();
if (currentLineIndex === -1) return;
const scrollOffset = currentLineIndex * liHeight;
ul.style.transform = `translateY(-${scrollOffset}px)`;
if (prevLineIndex!== -1 && prevLineIndex!== currentLineIndex) {
const prevLi = ul.children;
if (prevLi) {
prevLi.querySelectorAll('span.active').forEach(span => {
span.classList.remove('active');
});
}
}
const currentLi = ul.children;
const currentLine = result;
const currentTime = audio.currentTime;
currentLine.forEach((wordObj, wordIndex) => {
const span = currentLi.children;
const isActive = currentTime >= wordObj.time &&
(wordIndex === currentLine.length - 1 ||
currentTime < currentLine.time);
if (isActive) {
span.classList.add('active');
} else {
span.classList.remove('active');
}
});
prevLineIndex = currentLineIndex;
}
audio.addEventListener("timeupdate", syncLyric);
</script>
<script>
const intro= document.querySelector('.intro');
let mState = () => audio.paused ? (intro.classList.add('stop')):(intro.classList.remove('stop'));
audio.addEventListener('play', () => mState());
audio.addEventListener('pause', () => mState());
btn.onclick = () => audio.paused ? (audio.play(), btn.style.background = 'url(https://pic.imgdb.cn/item/675813f6d0e0a243d4e14a48.png) no-repeat 0px 0px/cover') : (audio.pause(), btn.style.background = 'url(https://pic.imgdb.cn/item/675813e7d0e0a243d4e14a18.png) no-repeat 0px 0px/cover');
const start = document.querySelector('.start')
const end= document.querySelector('.end')
function conversion (value) {
let minute = Math.floor(value / 60)
minute = minute.toString().length === 1 ? ('0' + minute) : minute
let second = Math.round(value % 60)
second = second.toString().length === 1 ? ('0' + second) : second
return `${minute}:${second}`
}
audio.onloadedmetadata = function () {
end.innerHTML = conversion(audio.duration)
start.innerHTML = conversion(audio.currentTime)
}
setInterval(() => {
start.innerHTML = conversion(audio.currentTime)
}, 1000)
prog.onclick = (e) => { audio.currentTime = audio.duration * e.offsetX / prog.offsetWidth; }
var progBar = document.getElementById('prog-bar');
audio.addEventListener('timeupdate', () => {
const percent = (audio.currentTime / audio.duration) * 100;
progBar.style.width = percent + '%';
});
</script>
这个播放器怎么在中间? 亚纶还在修改吧。。。 这个应该是播放器控制视频的效果 欣赏佳作,问候亚伦。 小辣椒 发表于 2025-9-16 21:13
这个应该是播放器控制视频的效果
点击频谱视频控制所有画面! 亚伦影音工作室 发表于 2025-9-16 21:25
点击频谱视频控制所有画面!
那是视频控制音乐? 小辣椒 发表于 2025-9-16 21:36
那是视频控制音乐?
用视频的音乐关联画面和歌词! 亚伦影音工作室 发表于 2025-9-16 21:42
用视频的音乐关联画面和歌词!
看见了,你现在播放器不在画面上了 漂亮!谢谢亚伦老师精彩分享{:4_191:} 原来这频谱是视频,前面还在奇怪为什么35秒的地方频谱不动了,去看视频也是如此呢。{:4_204:} 这个是用视频音乐对应歌词的,太神奇了{:4_199:}
页:
[1]