如何在 README 显示正在听的音乐

以网易云音乐为例,效果如下

P.S. 如果上面是白色的,代表我没有在听歌,不过文章末尾我放了一个静态的可以参考

数据怎么获取

简单说一下几种思路:

  1. 网易云音乐 API,参考这个 Repo Binaryify/NeteaseCloudMusicApi,里面有一个 「获取用户播放记录」 的接口。此接口返回的并不是直接的一个播放历史,而是需要你根据数量过滤。你需要在两次请求间隔中 diff 一下,如果哪首歌播放次数增加了,那么就是最近在听的
  2. 如果你在用第三方客户端,可以直接 fork 改代码进行数据埋点。或者看看客户端里面有没有播放行为相关的日志
  3. 改 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>  

效果如下

  • cxcy 属性定义圆点的 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: 指定要应用的变换类型
  • fromto 用于变换的起始和结束。根据所选的变换类型提供相应的值
  • 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 的时候会报错。效果如下,这个是一个静态嵌入的版本,右键可以查看所有源码。我稍微加了一点外圈的渐变效果,有没有区别不大

無窮プラトニック