马黑黑 发表于 2022-9-4 07:40

她是水

<style>
#papa { left: -214px; width: 1024px; height: 640px; background: gray url('https://638183.freep.cn/638183/Pic/81/100s.jpg') no-repeat center/cover; box-shadow: 3px 3px 20px #000; overflow: hidden; user-select: none; display: grid; place-items: center; position: relative; }
/* svg按钮↓*/
#mplayer { position: absolute; bottom: 0; }
#btnpause { display: none; }
#btnwrap, #btnplay, #btnpause { cursor: pointer; }
#btnplay:hover + btnwrap, #btnpause:hover + btnwrap { fill: gold; }
#btnwrap:hover { fill: gold; }
#btnwrap:hover + #btnplay + #btnpause { fill: red;}
#tmsg { dominant-baseline: middle; fill: snow; }
#track { shape-rendering: crispEdges; cursor: pointer; }
/* svg 歌词及动态效果 ↓ */
#lrc { position: absolute; width: 1000px; height: 80px; top: 10px; }
.text { font: bold 3em sans-serif; text-anchor: middle; dominant-baseline: middle; letter-spacing: 4px; fill: none; stroke-width: 1.5px; stroke-dasharray: 90 310; animation: stroke 6s infinite linear; }
.text-1{ stroke: snow; text-shadow: 0 0 5px red; animation-delay: -1.5s; }
.text-2{ stroke: orange; text-shadow: 0 0 5px green; animation-delay: -3s; }
.text-3{ stroke: gold; text-shadow: 0 0 5px blue; animation-delay: -4.5s; }
.text-4{ stroke: tomato; text-shadow: 0 0 5px purple; animation-delay: -6s; }
@keyframes stroke { to { stroke-dashoffset: -400; } }
</style>

<div id="papa">
        <svg id="lrc">
                <text x="50%" y="50%" class="text text-1">lrc歌词</text>
                <text x="50%" y="50%" class="text text-2">lrc歌词</text>
                <text x="50%" y="50%" class="text text-3">lrc歌词</text>
                <text x="50%" y="50%" class="text text-4">lrc歌词</text>
        </svg>
        <svg id="mplayer" width="400" height="60">
                <circle id="btnwrap" cx="20" cy="30" r="15" fill="olive" />
                <polygon id="btnplay" points="15 20, 15 40, 30 30" fill="snow" />
                <g id="btnpause" fill="snow">
                       <rect x= "15" y="20" width="3" height="20" />
                        <rect x="22" y="20" width="3" height="20" />
                </g>
                <g id="track"stroke-width="2">
                        <line x1="45" y1="30" x2="245" y2="30" stroke="transparent" stroke-width = "20" />
                        <line x1="45" y1="30" x2="245" y2="30" stroke="snow" />
                        <line id="prog" x1="45" y1="30" x2="245" y2="30" stroke="red" stroke-dasharray="200" stroke-dashoffset="200" />
                </g>
                <text id="tmsg" x="260" y="30">00:00 | 00:00</text>
        </svg>
</div>

<script>
let lrcAr = [
        ['0.00','她是水 - 黄龄'],
        ['21.07','都说水中月不比那镜中花'],
        ['28.07','伸手捧一掬空掌心无他不见月牙'],
        ['34.02','量 一寸 一尺 一丈 无极无量'],
        ['38.02','我叹 我想 我情不自禁观想观想'],
        ['42.08','舞起霓裳合十手掌 她在身旁'],
        ['50.08','她说爱在水中 一笑一场梦'],
        ['54.08','我与谁相拥'],
        ['57.08','淌过身旁的人 呢喃自称'],
        ['61.06','吝啬这湿吻'],
        ['64.06','太美我不敢看 窥探这一半'],
        ['68.04','多情又如何'],
        ['71.04','低声唱啊唱啊 尝不尽这牵挂'],
        ['76.09','涉险为你蒸发'],
        ['93.07','她似水中月唤我去温柔乡'],
        ['100.06','我似梦一场唏嘘这光年只去不往'],
        ['106.02','春 了夏 了秋 冬雪悄悄落下'],
        ['110.02','冻结 埋藏 冰下的誓言是真吗'],
        ['114.03','是假的吗'],
        ['116.03','那些话 融化只不过是刹那'],
        ['121.03','她忘了回答'],
        ['122.09','她说爱在水中 一笑一场梦'],
        ['126.08','我与谁相拥'],
        ['129.08','淌过身旁的人 呢喃自称'],
        ['133.06','吝啬这湿吻'],
        ['136.07','太美我不敢看 窥探这一半'],
        ['140.04','多情又如何'],
        ['143.04','低声唱啊唱啊 尝不尽这牵挂'],
        ['148.09','涉险为你蒸发'],
        ['151.04','嘿呀 嘿呀 怎么让她留下'],
        ['158.05','嘿呀 嘿呀 怎么把她遗忘'],
        ['163.09','她在水中看江南繁华落尽'],
        ['166.00','曲中叹 伊人魂影若泣 淡然从容'],
        ['168.06','在夜的朦胧里赏月色落寂'],
        ['171.01','她说相濡以沫 分秒必争'],
        ['173.00','承君此诺 必守一生'],
        ['174.06','在烟雨笼罩水中月'],
        ['176.05','涟漪荡漾落松叶'],
        ['181.02','她说爱在水中 一笑一场梦'],
        ['185.01','我与谁相拥'],
        ['188.00','淌过身旁的人 呢喃自称'],
        ['191.08','吝啬这湿吻'],
        ['194.08','太美我不敢看 窥探这一半'],
        ['198.07','多情又如何'],
        ['201.07','低声唱啊唱啊 尝不尽这牵挂'],
        ['207.01','涉险为你蒸发']
];
let aud = new Audio(), lw = prog.getTotalLength(), txtAr = document.querySelectorAll('.text');
aud.src = 'https://music.163.com/song/media/outer/url?id=1975659253.mp3';
aud.autoplay = true;
aud.loop = true;

track.onclick = (e) => aud.currentTime = aud.duration * (e.offsetX - prog.getAttribute('x1')) / lw;
btnwrap.onclick = btnpause.onclick = btnplay.onclick = () => aud.paused ? aud.play() : aud.pause();

aud.addEventListener('playing', ()=> btnstate());
aud.addEventListener('pause', ()=> btnstate());

aud.addEventListener('timeupdate', () => {
        prog.style.strokeDashoffset = lw - lw * aud.currentTime / aud.duration + 'px';
        tmsg.textContent = toMin(aud.currentTime) + ' | ' + toMin(aud.duration);
        for(j = 0; j < lrcAr.length;j ++) {
                if(aud.currentTime >= lrcAr) {
                        for(ele of txtAr) ele.textContent = lrcAr;
                }
        }
});

let btnstate = () => aud.paused ? (btnplay.style.display = 'block', btnpause.style.display = 'none') : (btnplay.style.display = 'none', btnpause.style.display = 'block');

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>

马黑黑 发表于 2022-9-4 07:41

源码(全)
<style>
#papa { left: -214px; width: 1024px; height: 640px; background: gray url('https://638183.freep.cn/638183/Pic/81/100s.jpg') no-repeat center/cover; box-shadow: 3px 3px 20px #000; overflow: hidden; user-select: none; display: grid; place-items: center; position: relative; }
/* svg按钮↓*/
#mplayer { position: absolute; bottom: 0; }
#btnpause { display: none; }
#btnwrap, #btnplay, #btnpause { cursor: pointer; }
#btnplay:hover + btnwrap, #btnpause:hover + btnwrap { fill: gold; }
#btnwrap:hover { fill: gold; }
#btnwrap:hover + #btnplay + #btnpause { fill: red;}
#tmsg { dominant-baseline: middle; fill: snow; }
#track { shape-rendering: crispEdges; cursor: pointer; }
/* svg 歌词及动态效果 ↓ */
#lrc { position: absolute; width: 1000px; height: 80px; top: 10px; }
.text { font: bold 3em sans-serif; text-anchor: middle; dominant-baseline: middle; letter-spacing: 4px; fill: none; stroke-width: 1.5px; stroke-dasharray: 90 310; animation: stroke 6s infinite linear; }
.text-1{ stroke: snow; text-shadow: 0 0 5px red; animation-delay: -1.5s; }
.text-2{ stroke: orange; text-shadow: 0 0 5px green; animation-delay: -3s; }
.text-3{ stroke: gold; text-shadow: 0 0 5px blue; animation-delay: -4.5s; }
.text-4{ stroke: tomato; text-shadow: 0 0 5px purple; animation-delay: -6s; }
@keyframes stroke { to { stroke-dashoffset: -400; } }
</style>

<div id="papa">
        <svg id="lrc">
                <text x="50%" y="50%" class="text text-1">lrc歌词</text>
                <text x="50%" y="50%" class="text text-2">lrc歌词</text>
                <text x="50%" y="50%" class="text text-3">lrc歌词</text>
                <text x="50%" y="50%" class="text text-4">lrc歌词</text>
        </svg>
        <svg id="mplayer" width="400" height="60">
                <circle id="btnwrap" cx="20" cy="30" r="15" fill="olive" />
                <polygon id="btnplay" points="15 20, 15 40, 30 30" fill="snow" />
                <g id="btnpause" fill="snow">
                       <rect x= "15" y="20" width="3" height="20" />
                        <rect x="22" y="20" width="3" height="20" />
                </g>
                <g id="track"stroke-width="2">
                        <line x1="45" y1="30" x2="245" y2="30" stroke="transparent" stroke-width = "20" />
                        <line x1="45" y1="30" x2="245" y2="30" stroke="snow" />
                        <line id="prog" x1="45" y1="30" x2="245" y2="30" stroke="red" stroke-dasharray="200" stroke-dashoffset="200" />
                </g>
                <text id="tmsg" x="260" y="30">00:00 | 00:00</text>
        </svg>
</div>

<script>
let lrcAr = [
        ['0.00','她是水 - 黄龄'],
        ['21.07','都说水中月不比那镜中花'],
        ['28.07','伸手捧一掬空掌心无他不见月牙'],
        ['34.02','量 一寸 一尺 一丈 无极无量'],
        ['38.02','我叹 我想 我情不自禁观想观想'],
        ['42.08','舞起霓裳合十手掌 她在身旁'],
        ['50.08','她说爱在水中 一笑一场梦'],
        ['54.08','我与谁相拥'],
        ['57.08','淌过身旁的人 呢喃自称'],
        ['61.06','吝啬这湿吻'],
        ['64.06','太美我不敢看 窥探这一半'],
        ['68.04','多情又如何'],
        ['71.04','低声唱啊唱啊 尝不尽这牵挂'],
        ['76.09','涉险为你蒸发'],
        ['93.07','她似水中月唤我去温柔乡'],
        ['100.06','我似梦一场唏嘘这光年只去不往'],
        ['106.02','春 了夏 了秋 冬雪悄悄落下'],
        ['110.02','冻结 埋藏 冰下的誓言是真吗'],
        ['114.03','是假的吗'],
        ['116.03','那些话 融化只不过是刹那'],
        ['121.03','她忘了回答'],
        ['122.09','她说爱在水中 一笑一场梦'],
        ['126.08','我与谁相拥'],
        ['129.08','淌过身旁的人 呢喃自称'],
        ['133.06','吝啬这湿吻'],
        ['136.07','太美我不敢看 窥探这一半'],
        ['140.04','多情又如何'],
        ['143.04','低声唱啊唱啊 尝不尽这牵挂'],
        ['148.09','涉险为你蒸发'],
        ['151.04','嘿呀 嘿呀 怎么让她留下'],
        ['158.05','嘿呀 嘿呀 怎么把她遗忘'],
        ['163.09','她在水中看江南繁华落尽'],
        ['166.00','曲中叹 伊人魂影若泣 淡然从容'],
        ['168.06','在夜的朦胧里赏月色落寂'],
        ['171.01','她说相濡以沫 分秒必争'],
        ['173.00','承君此诺 必守一生'],
        ['174.06','在烟雨笼罩水中月'],
        ['176.05','涟漪荡漾落松叶'],
        ['181.02','她说爱在水中 一笑一场梦'],
        ['185.01','我与谁相拥'],
        ['188.00','淌过身旁的人 呢喃自称'],
        ['191.08','吝啬这湿吻'],
        ['194.08','太美我不敢看 窥探这一半'],
        ['198.07','多情又如何'],
        ['201.07','低声唱啊唱啊 尝不尽这牵挂'],
        ['207.01','涉险为你蒸发']
];
let aud = new Audio(), lw = prog.getTotalLength(), txtAr = document.querySelectorAll('.text');
aud.src = 'https://music.163.com/song/media/outer/url?id=1975659253.mp3';
aud.autoplay = true;
aud.loop = true;

track.onclick = (e) => aud.currentTime = aud.duration * (e.offsetX - prog.getAttribute('x1')) / lw;
btnwrap.onclick = btnpause.onclick = btnplay.onclick = () => aud.paused ? aud.play() : aud.pause();

aud.addEventListener('playing', ()=> btnstate());
aud.addEventListener('pause', ()=> btnstate());

aud.addEventListener('timeupdate', () => {
        prog.style.strokeDashoffset = lw - lw * aud.currentTime / aud.duration + 'px';
        tmsg.textContent = toMin(aud.currentTime) + ' | ' + toMin(aud.duration);
        for(j = 0; j < lrcAr.length;j ++) {
                if(aud.currentTime >= lrcAr) {
                        for(ele of txtAr) ele.textContent = lrcAr;
                }
        }
});

let btnstate = () => aud.paused ? (btnplay.style.display = 'block', btnpause.style.display = 'none') : (btnplay.style.display = 'none', btnpause.style.display = 'block');

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>

马黑黑 发表于 2022-9-4 07:55

首次使用 svg 开发播放器。播放器采用控制器与lrc歌词分离的实现形式,各用一个 svg 标签做成。

CSS可以有限设定 svg 内部元素,结合 svg 丰富的属性,做按钮、进度条、文本显示绰绰有余。

进度条的实现,歌词的动态描边效果,都是利用 svg 的虚线线段偏移属性完成,该属性名称为 stroke-dashoffset,JS表示为 strokeDashoffset,它会将线段(stroke-dasharray)偏移从而隐藏,通过设置 offset 值,恢复部分乃至全部显现,达到进度条与文本描边的动态效果。进度条使用JS控制,文本描边用CSS关键帧动画实现。

svg 对多数人来说是个陌生的领域,它是一种图形文件格式,英文全称为 Scalable Vector Graphics,意为可缩放的矢量图形。svg 可以直接当成像 div 一样的 HTML 标签在 web 页中使用,不同的是,它有自己一整套规范,使用者需要熟知相关知识才能应用。

加林森 发表于 2022-9-4 08:11

等会来学习。

红影 发表于 2022-9-4 09:17

全部用SVG实现的播放器和歌词同步,这个太赞了,黑黑好棒{:4_199:}

红影 发表于 2022-9-4 09:19

好奇妙的歌词效果。这个是stroke文字,不知道fill的效果是怎样的{:4_173:}

红影 发表于 2022-9-4 09:25

.text { font: bold 3em sans-serif; text-anchor: middle; dominant-baseline: middle; letter-spacing: 4px; fill: none; stroke-width: 1.5px; stroke-dasharray: 90 310; animation: stroke 6s infinite linear; }

这句里面有好多没见过的命令。

马黑黑 发表于 2022-9-4 10:37

红影 发表于 2022-9-4 09:25
.text { font: bold 3em sans-serif; text-anchor: middle; dominant-baseline: middle; letter-spacing: 4 ...

svg 文本水平居中:text-anchor: middle;
svg 文本垂直居中:dominant-baseline: middle;

svg虚线线段(数组):stroke-dasharray: 90 310; 表示:90为一节线段,310为线段间距

马黑黑 发表于 2022-9-4 10:38

红影 发表于 2022-9-4 09:17
全部用SVG实现的播放器和歌词同步,这个太赞了,黑黑好棒

原理在那里,只要了解了相关指令,组织一下代码就行

马黑黑 发表于 2022-9-4 10:41

红影 发表于 2022-9-4 09:19
好奇妙的歌词效果。这个是stroke文字,不知道fill的效果是怎样的

fill 没有相应的 dash 指令

绿叶清舟 发表于 2022-9-4 11:03

这个字体效果漂亮

梦油 发表于 2022-9-4 11:06

黑黑先生真是才华横溢啊!

梦缘 发表于 2022-9-4 11:16

学习代码,感谢老师的精彩分享!{:4_187:}

醉美水芙蓉 发表于 2022-9-4 11:39

小辣椒 发表于 2022-9-4 11:49

马黑黑 发表于 2022-9-4 07:55
首次使用 svg 开发播放器。播放器采用控制器与lrc歌词分离的实现形式,各用一个 svg 标签做成。

CSS可以 ...

感觉有点深奥了

小辣椒 发表于 2022-9-4 11:50

黑黑精彩不断{:4_178:}

加林森 发表于 2022-9-4 12:19

来学习!

马黑黑 发表于 2022-9-4 13:12

绿叶清舟 发表于 2022-9-4 11:03
这个字体效果漂亮

还行

马黑黑 发表于 2022-9-4 13:12

小辣椒 发表于 2022-9-4 11:50
黑黑精彩不断

谢顶

马黑黑 发表于 2022-9-4 13:12

小辣椒 发表于 2022-9-4 11:49
感觉有点深奥了

你可能一点都木有接触过 svg
页: [1] 2 3 4 5 6
查看完整版本: 她是水