马黑黑 发表于 2022-9-8 06:50

清零

<style>
#papa { left: -214px; width: 1024px; height: 640px; background: darkred url('/data/attachment/forum/202209/08/064641dlkn8iogkhrok2s6.jpg') no-repeat center/cover; display: grid; place-items: center; box-shadow: 3px 3px 20px #000; user-select: none; position: relative; z-index: 1;}
#mplayer { position: absolute;top: 260px; width: 120px; height: 120px; border-radius: 50%; overflow: hidden; z-index: 3; }
#track { stroke: url(#gradient); }
#lrc { position: absolute; top: 180px; display: block; }
#lrctxt { text-anchor: middle; dominant-baseline: text-after-edge; fill: url(#gradient); font: bold 2.2em sans-serif; text-shadow: -2px -2px 0px #fff, 2px 2px 3px #000; letter-spacing: 3px; }
#tmsg { fill: #ccc; font: bold 1em sans-serif; }
#btnwrap { display: block; fill: #ccc; cursor: pointer; }
#btnwrap:hover { fill: orange; }
</style>

<div id="papa">
        <!-- 播放器 -->
        <svg id="mplayer" width="120" height="120" shape-rendering="geometricPrecision">
                <g id="mama" transform="rotate(-90, 60, 60)" style="cursor: pointer;">
                        <circle id="track" cx="60" cy="60" r="50" fill="none" stroke-width="10" stroke="rgba(0,0,0,0.35)" />
                        <circle id="prog" cx="60" cy="60" r="50" fill="none" stroke-width="4" stroke="rgba(255,255,255,0.55)" />
                </g>
                <path id="curPath" d="M 20 70 Q 60 0 100 70" fill="none" stroke="none"/>
                <path id="durPath" d="M 20 55 Q 60 110 100 55" fill="none" stroke="none"/>
                <g id="tmsg">
                        <text x="34" y="0"><textPath id="curMsg" xlink:href="#curPath" dominant-baseline="text-after-edge">00:00</textPath></text>
                        <text x="29" y="0"><textPath id="durMsg" xlink:href="#durPath" dominant-baseline="text-before-edge">00:00</textPath></text>
                </g>
                <g id="btnwrap">
                        <path id="btnplay" d="M 50 50,50 70,70, 60 z"></path>
                        <path id="btnpause" d="M 52 50,52 70,57 70,57 50,52 50 z M 60 50,60 70,65 70,65 50,60 50 z"></path>
                </g>
        </svg>
        <!-- lrc歌词 -->
        <svg id="lrc" width="560" height="150">
                <defs><path id="lrcPath" fill="none" stroke="red" d="M 10 150 Q 305 -10,560 150" /></defs>
                <defs>
                        <linearGradient id="gradient" x1="0" y1="0" x2="0" y2="1">
                                <stop offset="0%" stop-color="blue"/>
                                <stop offset="35%" stop-color="yellow"/>
                                <stop offset="65%" stop-color="gray"/>
                                <stop offset="100%" stop-color="red"/>
                        </linearGradient>
                </defs>
                <textx="54%" y="0"><textPath id="lrctxt" xlink:href="#lrcPath">司南 - 清零</textPath></text>
        </svg>
</div>

<script>
let lrcAr = [
        ['1.42','作词:千年小妖'],
        ['2.84','作曲:黄文文'],
        ['4.66','编曲:曾吴秋杰'],
        ['5.66','OP:深声文化 出品:网易飓风'],
        ['8.43','演唱:司南'],
        ['17.22','风划过黎明 泛白的天际'],
        ['20.79','阴沉的乌云 在聚集'],
        ['24.34','裹挟着雨滴 偏漫无声息'],
        ['27.97','跳跃在我的掌心里'],
        ['31.60','潮湿空气 席卷着旧日回忆'],
        ['35.22','身后的影子一瞬间藏匿'],
        ['38.59','难预测的天气 眼前的风景'],
        ['42.32','连同你 消失彻底'],
        ['44.43','混音:曾吴秋杰'],
        ['46.26','制作人:曾吴秋杰'],
        ['48.59','你在我 生命中全部清零'],
        ['52.64','寻你的踪迹 已下落不明'],
        ['56.16','我手写故事里的残缺可惜'],
        ['59.97','来不及 拥抱就离去'],
        ['63.31','我的爱 在你心里被清零'],
        ['66.86','我们又回到 陌生的关系'],
        ['70.62','许下的承诺你选择忘记'],
        ['74.15','这段爱 被遗憾定义'],
        ['92.49','潮湿空气 席卷着旧日回忆'],
        ['96.06','身后的影子一瞬间藏匿'],
        ['99.62','难预测的天气 眼前的风景'],
        ['103.20','连同你 消失彻底'],
        ['109.94','你在我 生命中全部清零'],
        ['113.58','寻你的踪迹 已下落不明'],
        ['117.22','我手写故事里的残缺可惜'],
        ['121.05','来不及 拥抱就离去'],
        ['124.30','我的爱 在你心里被清零'],
        ['128.07','我们又回到 陌生的关系'],
        ['131.50','许下的承诺你选择忘记'],
        ['135.01','这段爱 被遗憾定义'],
        ['138.61','你在我 生命中全部清零'],
        ['142.23','寻你的踪迹 已下落不明'],
        ['146.10','我手写故事里的残缺可惜'],
        ['149.73','来不及 拥抱就离去'],
        ['152.93','我的爱 在你心里被清零'],
        ['156.52','我们又回到 陌生的关系'],
        ['160.10','许下的承诺你选择忘记'],
        ['163.70','这段爱 被遗憾定义'],
        ['169.36','录音师:乌英剑'],
        ['171.31','和声:熊'],
        ['173.08','统筹:郭凯翌/吴桦/冯港'],
        ['174.83','录音棚:北京好听音乐录音棚']
];
let cc = { //圆环对象
        x: 1*track.getAttribute('cx'),
        y: 1*track.getAttribute('cy'),
        r: 1*track.getAttribute('r'),
        sw: 1*track.getAttribute('stroke-width'),
        len: track.getTotalLength(),
};
let aud = new Audio();
aud.src = 'https://music.163.com/song/media/outer/url?id=1961264465.mp3';
aud.autoplay = true;
aud.loop = true;
prog.style.strokeDasharray = prog.style.strokeDashoffset = cc.len; //进度偏移
btnwrap.onclick = () => aud.paused ? aud.play() : aud.pause(); //按钮单击
mama.onclick = (e) => { //环单击
        let deg = Math.atan2(e.offsetY - cc.y, e.offsetX - cc.x) * 180 / Math.PI;
        deg += (e.offsetX < cc.x && e.offsetY < cc.y) ? 450 : 90;
        aud.currentTime = aud.duration * deg / 360;
};
//audio空间各类监听
aud.addEventListener('pause', () => btnstate());
aud.addEventListener('play',() => btnstate());
aud.addEventListener('timeupdate', () => {
        prog.style.strokeDashoffset = cc.len - cc.len * aud.currentTime / aud.duration + 'px';
        curMsg.textContent = toMin(aud.currentTime);
        durMsg.textContent = toMin(aud.duration);
        for(j=0; j<lrcAr.length; j++) {
                if(aud.currentTime >= lrcAr) lrctxt.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-8 06:53

附:代码
<style>
#papa { left: -214px; width: 1024px; height: 640px; background: darkred url('/data/attachment/forum/202209/08/064641dlkn8iogkhrok2s6.jpg') no-repeat center/cover; display: grid; place-items: center; box-shadow: 3px 3px 20px #000; user-select: none; position: relative; z-index: 1;}
#mplayer { position: absolute;top: 260px; width: 120px; height: 120px; border-radius: 50%; overflow: hidden; z-index: 3; }
#track { stroke: url(#gradient); }
#lrc { position: absolute; top: 180px; display: block; }
#lrctxt { text-anchor: middle; dominant-baseline: text-after-edge; fill: url(#gradient); font: bold 2.2em sans-serif; text-shadow: -2px -2px 0px #fff, 2px 2px 3px #000; letter-spacing: 3px; }
#tmsg { fill: #ccc; font: bold 1em sans-serif; }
#btnwrap { display: block; fill: #ccc; cursor: pointer; }
#btnwrap:hover { fill: orange; }
</style>

<div id="papa">
        <!-- 播放器 -->
        <svg id="mplayer" width="120" height="120" shape-rendering="geometricPrecision">
                <g id="mama" transform="rotate(-90, 60, 60)" style="cursor: pointer;">
                        <circle id="track" cx="60" cy="60" r="50" fill="none" stroke-width="10" stroke="rgba(0,0,0,0.35)" />
                        <circle id="prog" cx="60" cy="60" r="50" fill="none" stroke-width="4" stroke="rgba(255,255,255,0.55)" />
                </g>
                <path id="curPath" d="M 20 70 Q 60 0 100 70" fill="none" stroke="none"/>
                <path id="durPath" d="M 20 55 Q 60 110 100 55" fill="none" stroke="none"/>
                <g id="tmsg">
                        <text x="34" y="0"><textPath id="curMsg" xlink:href="#curPath" dominant-baseline="text-after-edge">00:00</textPath></text>
                        <text x="29" y="0"><textPath id="durMsg" xlink:href="#durPath" dominant-baseline="text-before-edge">00:00</textPath></text>
                </g>
                <g id="btnwrap">
                        <path id="btnplay" d="M 50 50,50 70,70, 60 z"></path>
                        <path id="btnpause" d="M 52 50,52 70,57 70,57 50,52 50 z M 60 50,60 70,65 70,65 50,60 50 z"></path>
                </g>
        </svg>
        <!-- lrc歌词 -->
        <svg id="lrc" width="560" height="150">
                <defs><path id="lrcPath" fill="none" stroke="red" d="M 10 150 Q 305 -10,560 150" /></defs>
                <defs>
                        <linearGradient id="gradient" x1="0" y1="0" x2="0" y2="1">
                                <stop offset="0%" stop-color="blue"/>
                                <stop offset="35%" stop-color="yellow"/>
                                <stop offset="65%" stop-color="gray"/>
                                <stop offset="100%" stop-color="red"/>
                        </linearGradient>
                </defs>
                <textx="54%" y="0"><textPath id="lrctxt" xlink:href="#lrcPath">司南 - 清零</textPath></text>
        </svg>
</div>

<script>
let lrcAr = [
        ['1.42','作词:千年小妖'],
        ['2.84','作曲:黄文文'],
        ['4.66','编曲:曾吴秋杰'],
        ['5.66','OP:深声文化 出品:网易飓风'],
        ['8.43','演唱:司南'],
        ['17.22','风划过黎明 泛白的天际'],
        ['20.79','阴沉的乌云 在聚集'],
        ['24.34','裹挟着雨滴 偏漫无声息'],
        ['27.97','跳跃在我的掌心里'],
        ['31.60','潮湿空气 席卷着旧日回忆'],
        ['35.22','身后的影子一瞬间藏匿'],
        ['38.59','难预测的天气 眼前的风景'],
        ['42.32','连同你 消失彻底'],
        ['44.43','混音:曾吴秋杰'],
        ['46.26','制作人:曾吴秋杰'],
        ['48.59','你在我 生命中全部清零'],
        ['52.64','寻你的踪迹 已下落不明'],
        ['56.16','我手写故事里的残缺可惜'],
        ['59.97','来不及 拥抱就离去'],
        ['63.31','我的爱 在你心里被清零'],
        ['66.86','我们又回到 陌生的关系'],
        ['70.62','许下的承诺你选择忘记'],
        ['74.15','这段爱 被遗憾定义'],
        ['92.49','潮湿空气 席卷着旧日回忆'],
        ['96.06','身后的影子一瞬间藏匿'],
        ['99.62','难预测的天气 眼前的风景'],
        ['103.20','连同你 消失彻底'],
        ['109.94','你在我 生命中全部清零'],
        ['113.58','寻你的踪迹 已下落不明'],
        ['117.22','我手写故事里的残缺可惜'],
        ['121.05','来不及 拥抱就离去'],
        ['124.30','我的爱 在你心里被清零'],
        ['128.07','我们又回到 陌生的关系'],
        ['131.50','许下的承诺你选择忘记'],
        ['135.01','这段爱 被遗憾定义'],
        ['138.61','你在我 生命中全部清零'],
        ['142.23','寻你的踪迹 已下落不明'],
        ['146.10','我手写故事里的残缺可惜'],
        ['149.73','来不及 拥抱就离去'],
        ['152.93','我的爱 在你心里被清零'],
        ['156.52','我们又回到 陌生的关系'],
        ['160.10','许下的承诺你选择忘记'],
        ['163.70','这段爱 被遗憾定义'],
        ['169.36','录音师:乌英剑'],
        ['171.31','和声:熊'],
        ['173.08','统筹:郭凯翌/吴桦/冯港'],
        ['174.83','录音棚:北京好听音乐录音棚']
];
let cc = { //圆环对象
        x: 1*track.getAttribute('cx'),
        y: 1*track.getAttribute('cy'),
        r: 1*track.getAttribute('r'),
        sw: 1*track.getAttribute('stroke-width'),
        len: track.getTotalLength(),
};
let aud = new Audio();
aud.src = 'https://music.163.com/song/media/outer/url?id=1961264465.mp3';
aud.autoplay = true;
aud.loop = true;
prog.style.strokeDasharray = prog.style.strokeDashoffset = cc.len; //进度偏移
btnwrap.onclick = () => aud.paused ? aud.play() : aud.pause(); //按钮单击
mama.onclick = (e) => { //环单击
        let deg = Math.atan2(e.offsetY - cc.y, e.offsetX - cc.x) * 180 / Math.PI;
        deg += (e.offsetX < cc.x && e.offsetY < cc.y) ? 450 : 90;
        aud.currentTime = aud.duration * deg / 360;
};
//audio空间各类监听
aud.addEventListener('pause', () => btnstate());
aud.addEventListener('play',() => btnstate());
aud.addEventListener('timeupdate', () => {
        prog.style.strokeDashoffset = cc.len - cc.len * aud.currentTime / aud.duration + 'px';
        curMsg.textContent = toMin(aud.currentTime);
        durMsg.textContent = toMin(aud.duration);
        for(j=0; j<lrcAr.length; j++) {
                if(aud.currentTime >= lrcAr) lrctxt.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-8 07:23

本帖最后由 马黑黑 于 2022-9-8 07:26 编辑

简单解释:

一、用两个 svg 分别做圆环播放控制器和lrc歌词。移动圆环播放器在CSS选择器 #mplayer 、移动lrc歌词在CSS选择器进行方位操作。

二、圆环播放器和lrc歌词共享 id="gradient" 的渐变设置(34行)。id是元素的唯一标识,只要在同一个页面,即便在不同区域设置,依然可以识别、共享特性。

三、lrc歌词路径设置为往上的弧形,路径设计在32行:

<defs><path id="lrcPath" fill="none" stroke="red" d="M 10 150 Q 305 -10,560 150" /></defs>

头尾的 defs 收尾标签隐藏路径(没必要把路径显示出来),41行文本路径标签绑定此路径,从而文本能按预设路径排列(播放器中的时间信息排列方法与此原理同)。

此处使用的是二次贝塞尔曲线,d="M 10 150 Q 305 -10,560 150" ,简单说一下:

M 10 150,表示移动(M=Move)画笔到(10,150),这也是曲线的开端;Q表示二次贝塞尔曲线,其所带的第一组xy坐标(300,-10)是曲线的线外控制点,这个点能够左右曲线凸面朝哪个方向(基于头尾两端的方向),第二组坐标(560,150)是曲线的末端,注意它的Y坐标和曲线开始点的值一致,它的X坐标值减去起始点X坐标值等于曲线经过的横向距离。

曲线的开始点、控制点、终点的xy坐标,共同构成曲线的最终样式。可以把defs标签去掉查看路径的真实样子。

四、lrc歌词文本如何在路径上居中

① 06行:#lrctxt { text-anchor: middle; dominant-baseline: text-after-edge; ......

② 41行:<textx="54%" y="0"><textPath id="lrctxt" xlink:href="#lrcPath">司南 - 清零</textPath>

这两行的共同作用,能达成目标。CSS中,设定文本居中的语句是 text-anchor: middle; 那句,但此处,svg的文本居中是基于X坐标的,即文字分散在X坐标的两边,所以有 41行 的 x="54%"的设置。为什么不是50%呢?我开始也设为50%,后面根据肉眼观测,微调为54%,这是根据路径样式做的微调,并不科学,或者,路径设计的不理想,不过大致效果可以的。

马黑黑 发表于 2022-9-8 07:29

圆环播放器看着像手镯,估计很多人喜欢,那就算是俺的中秋礼物吧,送给大家。预祝中秋万事如意、过节开森!

加林森 发表于 2022-9-8 07:45

等会来学习,现在在外面做核酸检测!

马黑黑 发表于 2022-9-8 07:48

本帖最后由 马黑黑 于 2022-9-8 07:50 编辑

【附】二次贝塞尔曲线的手工设计方法:

分三个点坐标。点1 用 M 命令,移动画笔到 的意思;点2 和 点3 用 Q 命令带出:点2 是曲线之外的控制点,它是曲线曲率的决定点;点3 是曲线的终点。

例如我们要在区域 300*200 的地方设置一条横向走向、水平距离300的曲线,凸面朝上,则需要将画笔移动到 (0,y1),然后给出一个控制点(x2, y2),这里的y2要尽可能小,可以是负数,最后给出终点(300,y3)。写成 d 语句(各点的数值可以适当调整):

d="M 0,200 Q 100 -20,300 90"

完整的路径语句:

<path d="M 0,200 Q 100 -20,300 90" fill="red" stroke="none" id="myPath" />

路径也是一个实体元素,所以可以拥有自己的着色设定,fill默认黑色、不设计也有,stroke建议永远设置为none,否则可能会出现几何图案(设定stroke为实色时试着在 d 语句的末尾加入小写字母 z)。为确保路径不显示出来,可在路径前后加 <defs> 和 </defs> 标签。

马黑黑 发表于 2022-9-8 07:54

加林森 发表于 2022-9-8 07:45
等会来学习,现在在外面做核酸检测!

早。我们也连续做了三天,小区有两位密接者,封闭了。

梦缘 发表于 2022-9-8 08:08

欣赏精美好帖,感谢老师的礼物!{:4_204:}

马黑黑 发表于 2022-9-8 08:26

梦缘 发表于 2022-9-8 08:08
欣赏精美好帖,感谢老师的礼物!

{:5_108:}

红影 发表于 2022-9-8 08:47

马黑黑 发表于 2022-9-8 07:23
简单解释:

一、用两个 svg 分别做圆环播放控制器和lrc歌词。移动圆环播放器在CSS选择器 #mplayer 、移 ...

我昨天也在本地试了一下向上弯的,对这个305的数字有点不解d="M 10 150 Q 305 -10,560 150"
貌似应该取280,凸起的才是居中吧,后面的54%是不是也是因为这个原因?

当然我现在听不了歌,也看不到效果,只能是闭着眼睛瞎说了{:4_173:}

红影 发表于 2022-9-8 08:49

马黑黑 发表于 2022-9-8 07:48
【附】二次贝塞尔曲线的手工设计方法:

分三个点坐标。点1 用 M 命令,移动画笔到 的意思;点2 和 点3...

“stroke建议永远设置为none,否则可能会出现几何图案(设定stroke为实色时试着在 d 语句的末尾加入小写字母 z)。”
还有这样的事,记下了{:4_173:}

红影 发表于 2022-9-8 08:51

马黑黑 发表于 2022-9-8 07:29
圆环播放器看着像手镯,估计很多人喜欢,那就算是俺的中秋礼物吧,送给大家。预祝中秋万事如意、过节开森!

这个手镯太美了,谢谢黑黑的中秋礼物{:4_187:}

红影 发表于 2022-9-8 08:53

《清零》两字的寓意更好,虽然是讲情感的歌曲,我更愿意是说疫情的,清零了,就可以出去玩了,也不用有诸多限制了{:4_173:}

红影 发表于 2022-9-8 08:55

一进坛子看到清零两字真的好开心{:4_173:}
借黑黑好帖同祝大家中秋快乐,有圆圆的月亮,圆圆的月饼,还有圆圆的代码手镯{:4_199:}

加林森 发表于 2022-9-8 09:25

马黑黑 发表于 2022-9-8 07:54
早。我们也连续做了三天,小区有两位密接者,封闭了。

终于查完了。我们这里基本上是全市都封闭管理了。

马黑黑 发表于 2022-9-8 09:26

加林森 发表于 2022-9-8 09:25
终于查完了。我们这里基本上是全市都封闭管理了。

现在很麻烦了,哪儿都去不了了

马黑黑 发表于 2022-9-8 09:27

红影 发表于 2022-9-8 08:55
一进坛子看到清零两字真的好开心
借黑黑好帖同祝大家中秋快乐,有圆圆的月亮,圆圆的月饼,还有 ...

一切都是团团圆圆

马黑黑 发表于 2022-9-8 09:29

红影 发表于 2022-9-8 08:47
我昨天也在本地试了一下向上弯的,对这个305的数字有点不解d="M 10 150 Q 305 -10,560 150"
貌似应该取 ...

估计是吧。理论上x方向的控制点,应居中,做的时候,歌词显示不完,就随意调整了长度,然后其他数据也随意调整了一下。

加林森 发表于 2022-9-8 09:30

马黑黑 发表于 2022-9-8 09:26
现在很麻烦了,哪儿都去不了了

就是。很麻烦的。

马黑黑 发表于 2022-9-8 09:31

红影 发表于 2022-9-8 08:53
《清零》两字的寓意更好,虽然是讲情感的歌曲,我更愿意是说疫情的,清零了,就可以出去玩了,也不用有诸多 ...

这个难,无法清零了,将是一种与人类为伴的流感一样的东东。等待平民药价出现吧,那时,它和流感是一样的待遇
页: [1] 2 3 4 5
查看完整版本: 清零