热情沙漠
<style>#papa { left: -214px; width: 1024px; height: 640px; background: rgba(0,0,0,.45) url('https://638183.freep.cn/638183/t22/51/desert.webp') no-repeat center/cover; box-shadow: 3px 3px 20px #000; display: grid; place-items: center; overflow: hidden; position: relative; z-index: 1; }
#mplayer { position: absolute; bottom: 0; width: 300px; height: 80px; user-select: none; display: grid; place-items: center; cursor: pointer; }
#mplayer:hover #btnwrap, #mplayer:hover #prog { transform: translateY(var(--yy)); }
#mplayer:hover #btnwrap { background: linear-gradient(to top right, purple, orange); border-radius: 50%; opacity: .75; }
#btnwrap, #prog { position: absolute; display: grid; place-items: center; transition: .5s; }
#btnwrap { --yy: -15px; width: 40px; height: 40px; transform: rotate(45deg); border: 1px solid tan; border-radius: 6px; opacity: 0; }
#btnplay { width: 20px; height: 20px; transform: translateX(3px); background: #eee; clip-path: polygon(0 0, 0% 100%, 100% 50%); }
#btnpause { width: 2px; height: 20px; border-style: solid; border-width: 0px 4px; border-color: transparent #eee; display: none; }
#prog { --yy: 20px; width: 300px; height: 16px; border-radius: 10px; background: linear-gradient(90deg, orange, purple 100%, transparent 0); border: 1px solid tan; font: normal 14px / 16px sans-serif; color: #fff; text-shadow: 1px 1px 0 #000; opacity: .8; }
#lrcwrap { position: absolute; top: 20px; font: bold 2.4em 'KaiTi',sans-serif; }
#lrc1, #lrc2 { width: 100%; height: 100%; color: rgba(0,0,0,.3); white-space: nowrap; }
#lrc2 { position: absolute; top: -1px; left: -1px; color: purple; overflow: hidden; }
#mpic { position: absolute; transform: rotateY(180deg); top: 30px; left: 0; width: 100px; offset-distance: 0%; offset-path: path("m482,185c749,286.31216 450,-494.37752 450,-494.37752c0,0 -1199,208.06536 -450,494.37752z"); cursor: pointer; }
@keyframes fly { from { offset-distance: 0%; } to { offset-distance: 100%; } }
@keyframes mov1 { from { width: 0%;} to { width: 100%; } }
@keyframes mov2 { from { width: 0%;} to { width: 100%; } }
</style>
<div id="papa" data-lr="no">
<img id="mpic" alt="" src="https://638183.freep.cn/638183/t22/51/plane.gif" />
<div id="lrcwrap">
<span id="lrc1">花潮lrc在线</span>
<span id="lrc2">花潮lrc在线</span>
</div>
<div id="mplayer">
<div id="btnwrap"><span id="btnplay"></span><span id="btnpause"></span></div>
<div id="prog">00:00 | 00:00</div>
</div>
</div>
<script>
let mKey = 0, mFlag = true, aud = new Audio();
let lrcAr = [,,,,,,,,,,,,,,,,,じて",3.1],,,,,,,,,,,];
aud.src = 'https://music.163.com/song/media/outer/url?id=280815.mp3';
aud.autoplay = true;
aud.loop = true;
mpic.onmouseover = () => {
mpic.style.animation = 'fly 16s linear forwards';
setTimeout(() => { mpic.style.animation = ''; },16000);
}
btnwrap.onclick = () => aud.paused ? aud.play() : aud.pause();
prog.onclick = (e) => aud.currentTime = aud.duration * e.offsetX / prog.offsetWidth;
aud.addEventListener('pause', () => mState());
aud.addEventListener('play', () => mState());
aud.addEventListener('seeked', () => calcKey());
aud.addEventListener('timeupdate', () => {
prog.style.background = 'linear-gradient(90deg, orange, purple ' + aud.currentTime / aud.duration * 100 + '%, snow 0)';
prog.innerText = toMin(aud.currentTime) + ' | ' + toMin(aud.duration);
for (j = 0; j < lrcAr.length; j++) {
if (aud.currentTime >= lrcAr) {
if (mKey === j) showLrc(lrcAr);
else continue;
}
}
});
let mState = () => aud.paused ? (btnplay.style.display = 'block', btnpause.style.display = 'none', lrc2.style.animationPlayState = 'paused') : (btnplay.style.display = 'none', btnpause.style.display = 'block', lrc2.style.animationPlayState = 'running');
let showLrc = (time) => {
lrc2.style.animation = (mFlag ? 'mov1 ' : 'mov2 ') + time + 's linear forwards';
lrc1.innerHTML = lrc2.innerHTML = lrcAr;
mKey += 1;
mFlag = !mFlag;
};
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 mtime = lrcAr - (aud.currentTime - lrcAr);
showLrc(mtime);
};
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;
};
</script>
代码
<style>
#papa { margin: auto; width: 1024px; height: 640px; background: rgba(0,0,0,.45) url('https://638183.freep.cn/638183/t22/51/desert.webp') no-repeat center/cover; box-shadow: 3px 3px 20px #000; display: grid; place-items: center; overflow: hidden; position: relative; z-index: 1; }
#mplayer { position: absolute; bottom: 0; width: 300px; height: 80px; user-select: none; display: grid; place-items: center; cursor: pointer; }
#mplayer:hover #btnwrap, #mplayer:hover #prog { transform: translateY(var(--yy)); }
#mplayer:hover #btnwrap { background: linear-gradient(to top right, purple, orange); border-radius: 50%; opacity: .75; }
#btnwrap, #prog { position: absolute; display: grid; place-items: center; transition: .5s; }
#btnwrap { --yy: -15px; width: 40px; height: 40px; transform: rotate(45deg); border: 1px solid tan; border-radius: 6px; opacity: 0; }
#btnplay { width: 20px; height: 20px; transform: translateX(3px); background: #eee; clip-path: polygon(0 0, 0% 100%, 100% 50%); }
#btnpause { width: 2px; height: 20px; border-style: solid; border-width: 0px 4px; border-color: transparent #eee; display: none; }
#prog { --yy: 20px; width: 300px; height: 16px; border-radius: 10px; background: linear-gradient(90deg, orange, purple 100%, transparent 0); border: 1px solid tan; font: normal 14px / 16px sans-serif; color: #fff; text-shadow: 1px 1px 0 #000; opacity: .8; }
#lrcwrap { position: absolute; top: 20px; font: bold 2.4em 'KaiTi',sans-serif; }
#lrc1, #lrc2 { width: 100%; height: 100%; color: rgba(0,0,0,.3); white-space: nowrap; }
#lrc2 { position: absolute; top: -1px; left: -1px; color: purple; overflow: hidden; }
#mpic { position: absolute; transform: rotateY(180deg); top: 30px; left: 0; width: 100px; offset-distance: 0%; offset-path: path("m482,185c749,286.31216 450,-494.37752 450,-494.37752c0,0 -1199,208.06536 -450,494.37752z"); cursor: pointer; }
@keyframes fly { from { offset-distance: 0%; } to { offset-distance: 100%; } }
@keyframes mov1 { from { width: 0%;} to { width: 100%; } }
@keyframes mov2 { from { width: 0%;} to { width: 100%; } }
</style>
<div id="papa" data-lr="no">
<img id="mpic" alt="" src="https://638183.freep.cn/638183/t22/51/plane.gif" />
<div id="lrcwrap">
<span id="lrc1">花潮lrc在线</span>
<span id="lrc2">花潮lrc在线</span>
</div>
<div id="mplayer">
<div id="btnwrap"><span id="btnplay"></span><span id="btnpause"></span></div>
<div id="prog">00:00 | 00:00</div>
</div>
</div>
<script>
let mKey = 0, mFlag = true, aud = new Audio();
let lrcAr = [,,,,,,,,,,,,,,,,,じて",3.1],,,,,,,,,,,];
aud.src = 'https://music.163.com/song/media/outer/url?id=280815.mp3';
aud.autoplay = true;
aud.loop = true;
mpic.onmouseover = () => {
mpic.style.animation = 'fly 16s linear forwards';
setTimeout(() => { mpic.style.animation = ''; },16000);
}
btnwrap.onclick = () => aud.paused ? aud.play() : aud.pause();
prog.onclick = (e) => aud.currentTime = aud.duration * e.offsetX / prog.offsetWidth;
aud.addEventListener('pause', () => mState());
aud.addEventListener('play', () => mState());
aud.addEventListener('seeked', () => calcKey());
aud.addEventListener('timeupdate', () => {
prog.style.background = 'linear-gradient(90deg, orange, purple ' + aud.currentTime / aud.duration * 100 + '%, snow 0)';
prog.innerText = toMin(aud.currentTime) + ' | ' + toMin(aud.duration);
for (j = 0; j < lrcAr.length; j++) {
if (aud.currentTime >= lrcAr) {
if (mKey === j) showLrc(lrcAr);
else continue;
}
}
});
let mState = () => aud.paused ? (btnplay.style.display = 'block', btnpause.style.display = 'none', lrc2.style.animationPlayState = 'paused') : (btnplay.style.display = 'none', btnpause.style.display = 'block', lrc2.style.animationPlayState = 'running');
let showLrc = (time) => {
lrc2.style.animation = (mFlag ? 'mov1 ' : 'mov2 ') + time + 's linear forwards';
lrc1.innerHTML = lrc2.innerHTML = lrcAr;
mKey += 1;
mFlag = !mFlag;
};
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 mtime = lrcAr - (aud.currentTime - lrcAr);
showLrc(mtime);
};
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;
};
</script>
此帖尝试:以双元素叠加方式模拟歌词逐字同步效果。
这是昨天上午的一个想法,本来是想使用伪元素加以实现,因JS对伪元素的操控能力有限,改用两个元素完成。这需要额外增加第三个元素,作为叠加元素的父元素,以便归拢歌词与在帖子上进行物理定位。
HTML歌词代码:
<div id="lrcwrap">
<span id="lrc1">花潮lrc在线</span>
<span id="lrc2">花潮lrc在线</span>
</div>
可从DOM节点查看对应元素的CSS设置。
与之前用背景着色的实现方式相比,JS的变动不大,仅两三行,源码中的第 59、60 这两行以及第 57 行。
双元素叠加的实现方式兼容性更好,但没有以渐变背景呈现文本的效果华丽。
此帖以日语歌做测试,目的在于检验一下花潮模式的歌词逐字模拟功能的适用性。
歌曲由湾湾歌手欧阳菲菲演唱。原唱属日本的一对双胞胎,那是1973年的事情了,随后欧阳菲菲分别用多种语言演绎过这首歌,一时火爆。上世纪末,此歌有多名华语歌手也演唱过,都受到热捧。 本帖最后由 马黑黑 于 2022-9-26 07:27 编辑
关于飞机的特技表演:鼠标移动到沙漠斜坡上的飞机,会触发飞机滑翔后起飞。飞机会以高难度动作做一个高级特技,期间会消失一小阵子,然后回位。可反复触发飞机的特技表演。
特技的飞行路线使用 path 路径设计,利用在线工具做成,飞机垂直升空后的那一部分路径我没有关注,仅关心它在帖子区域的路线。
顺便提一下,飞机属于J系列,性能优越,已经可以量产。 醉美水芙蓉 发表于 2022-9-26 06:58
老师早上好!欣赏黑黑老师精彩音画!
早啊 小飞机有意思,原来是用JS控制点,需要鼠标触控,开始有还以为是不会动的呢{:4_173:} 双元素叠加方式模拟歌词逐字同步效果也很棒的,黑黑辛苦了,给黑黑点赞{:4_187:} 红影 发表于 2022-9-26 09:41
双元素叠加方式模拟歌词逐字同步效果也很棒的,黑黑辛苦了,给黑黑点赞
谢谢 红影 发表于 2022-9-26 09:35
小飞机有意思,原来是用JS控制点,需要鼠标触控,开始有还以为是不会动的呢
它静止时就是有动作的,发动机全开,单等号令。 黒黑,我现在有个问题没有弄懂,歌词逐字因为音乐有过门,你可以逐字的同时过门音乐歌词前面显示完 但我逐字会跟着过门音乐一起走完,这个是不是在微调中完成,我试了音乐逐字停止,后面的过门音乐会显没有歌词,变空的了。 今天又有新的制作,现在歌词同步更加完美了{:4_178:} 小辣椒 发表于 2022-9-26 13:02
今天又有新的制作,现在歌词同步更加完美了
中午好 小辣椒 发表于 2022-9-26 13:01
黒黑,我现在有个问题没有弄懂,歌词逐字因为音乐有过门,你可以逐字的同时过门音乐歌词前面显示完 但我逐 ...
过门是指开头的部分,还没有开始唱的时候。真唱以前的“歌词”信息,可以不用微调,就不存在显示不完的情况。
至于歌词与歌词间的“过门”,原理是这样:第一句开始唱第一个字到唱完最后一个字所用的时间,和数组元素
6]
红色部分的时间相同,也就是说,上面的 6 记录的是唱词所用的时间。那么,下一句歌词
[41.40,'歌词一',6.5]
红色部分必须 大于等于 27 + 6 ,否则就出现你所说的情况。 马黑黑 发表于 2022-9-26 13:12
过门是指开头的部分,还没有开始唱的时候。真唱以前的“歌词”信息,可以不用微调,就不存在显示不完的情 ...
好的,谢谢黑黑 晚上我试一下,这个问题感觉对DJ音乐快节奏没有关系,对抒情歌曲有会有影响,我捣鼓了好久不行,还是问问黑神了{:4_189:} 小辣椒 发表于 2022-9-26 13:15
好的,谢谢黑黑 晚上我试一下,这个问题感觉对DJ音乐快节奏没有关系,对抒情歌曲有会有影响,我捣鼓了好 ...
你要理解歌词数组的含义,每一句歌词数组,有三个项目:
起唱时间,歌词,唱词用时 马黑黑 发表于 2022-9-26 12:15
谢谢
其实这个和前一个,从效果上看,差不多,代码核心确是不一样了呢{:4_187:} 马黑黑 发表于 2022-9-26 12:16
它静止时就是有动作的,发动机全开,单等号令。
黑黑的每个效果都那么有趣{:4_204:} 红影 发表于 2022-9-26 16:24
黑黑的每个效果都那么有趣
还行的吧