如何在 README 显示正在听的音乐
以网易云音乐为例,效果如下
P.S. 如果上面是白色的,代表我没有在听歌,不过文章末尾我放了一个静态的可以参考
数据怎么获取
简单说一下几种思路:
- 网易云音乐 API,参考这个 Repo Binaryify/NeteaseCloudMusicApi,里面有一个 「获取用户播放记录」 的接口。此接口返回的并不是直接的一个播放历史,而是需要你根据数量过滤。你需要在两次请求间隔中 diff 一下,如果哪首歌播放次数增加了,那么就是最近在听的
- 如果你在用第三方客户端,可以直接 fork 改代码进行数据埋点。或者看看客户端里面有没有播放行为相关的日志
- 改 hosts 文件,然后起一个 tunnel 来劫持 packet,还原一下流量
需要注意的是,触发播放的时候有可能不会发请求 mp3 流的,取决与本地是否有缓存。只要你拿到一个 song id,你就可以通过 API 拿专辑封面之类的信息
绘制黑胶唱片 SVG
因为要在 README 中显示动态的结果,那么最好就是嵌入多媒体资源,我们可以通过后端服务对于同一个 URL 返回不同的内容。接下来我们来时使用 SVG 绘制一个黑胶唱片的图片。关于 SVG,资料可以直接在 MDN 中找到
绘制唱片外边框
第一步我们来画圆。首先我们先创建一个 SVG 元素
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 200 200">
</svg>
在这里我们通过 width="200" height="200"
定义了一个 SVG 图形的大小。viewBox="0 0 200 200"
用于截取 SVG 指定的区域然后缩放到整个 SVG 大小,这里不关键。SVG 中有一些预定义的形状:矩形 <rect>
,圆形 <circle>
,椭圆 <ellipse>
等等
我们来向 SVG 中添加一个圆形
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 200 200">
<circle cx="100" cy="100" r="86" fill="#1F1F1F" />
</svg>
效果如下
cx
和cy
属性定义圆点的 x 和 y 坐标r
属性定义圆的半径
为了模拟唱片中的纹路,我们可以多添加几个不同半径的同心圆形,让其叠加显示
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 200 200">
<circle cx="100" cy="100" r="86" fill="#1F1F1F" />
<circle cx="100" cy="100" r="79" fill="#3A3A3A" />
<circle cx="100" cy="100" r="78" fill="#1F1F1F" />
<circle cx="100" cy="100" r="74" fill="#3A3A3A" />
<circle cx="100" cy="100" r="73" fill="#1F1F1F" />
<circle cx="100" cy="100" r="69" fill="#3A3A3A" />
<circle cx="100" cy="100" r="68" fill="#1F1F1F" />
</svg>
效果如下
添加专辑封面
网易云音乐的专辑封面的 URL 类似下面这个
https://p2.music.126.net/sZWouKjMg7eFCsWC5l8IYQ==/109951165714669526.jpg?param=130y130
这里这个 param=130y130
是返回图片的大小。如果要在 GitHub SVG svg,我们不能直接嵌入 URL,而是要嵌入 base64 编码后的图片内容,否则会有域问题。GitHub 中所有嵌入的图片都是会被 camo.github 这个代理的,你可以点开你的 README 中的图片看一下,并不是原始的 URL。这里我们为了演示方便,先使用 URL 作为示例,后面再改
另外图片都是矩形的,我们需要进行剪裁
<clipPath id="circle-clip">
<circle cx="100" cy="100" r="61.8" />
</clipPath>
<image href="https://p2.music.126.net/sZWouKjMg7eFCsWC5l8IYQ==/109951165714669526.jpg?param=140y140" x="30" y="30" width="140" height="140" />
在 <clipPath>
元素内部,我们使用 <circle>
元素来定义一个裁剪路径,半径为 61.8,它将用于将图片裁剪成圆形。然后,在 <image>
元素中,我们添加了一个 clip-path
属性,它的值为 url(#circle-clip)
,指向之前定义的裁剪路径。这样,图片将会根据裁剪路径进行裁剪并填充到黑胶唱片的区域中。详情参考文档 https://developer.mozilla.org/zh-CN/docs/Web/SVG/Element/clipPath
x="30" y="30"
指定了起始位置,即图片左上顶点的座标。因为我们指定的 SVG 是 200 * 200 的,所以图片的宽度需要为 140,以满足
(200 / 2) - 30 == (140 - 30) / 2
的式子,这样会让图片居中。效果类似下图
旋转动画
<animateTransform>
元素是 SVG 中用于创建动画的元素,参考文档 https://developer.mozilla.org/zh-CN/docs/Web/SVG/Element/animateTransform
<animateTransform
attributeName="transform"
type="rotate"
from="0 0 0"
to="360 0 0"
dur="10s"
repeatCount="indefinite"
/>
type
: 指定要应用的变换类型from
和to
用于变换的起始和结束。根据所选的变换类型提供相应的值dur
: 完成一轮变换需要的时间,用于控制速度repeatCount
: 指定动画的重复次数
Base64 图片编码
通过 xlink
可以添加 base64 编码支持。${albumData}
这里是一个 JavaScript 里的变量,你替换掉就好了
<image
xlink:href="data:image/gif;base64,${albumData}"
x="30"
y="30"
width="140"
height="140"
clip-path="url(#circle-clip)"
/>
使用 xlink
的时候不要忘了添加 xmlns:xlink="http://www.w3.org/1999/xlink"
,否则显示 SVG 的时候会报错。效果如下,这个是一个静态嵌入的版本,右键可以查看所有源码。我稍微加了一点外圈的渐变效果,有没有区别不大