简述

  • 结构简单,功能简单,支持播放、暂停、播放进度控制。
  • 每个音频单独播放,互不影响,且与控制中心的 Aplayer 相互独立。
  • 如果喜欢该音频,可以点击右侧加号按钮添加至 Aplayer 歌单列表,实现全局播放,同时可使用 Aplayer 的桌面歌词功能。

格式

1
{% music 标题,作者,链接,封面,歌词 %}

【注】参数两端不可使用引号;各参数间以英文逗号为分割,故参数不可携带英文逗号。

预览

cover
贫道 刘心 --:-- / --:--

音源

我主要使用网易云的音乐资源,在音乐单曲界面按F12可获取封面链接,页面网址则包含了该歌曲的id,而音频链接格式如下:

1
`https://music.163.com/song/media/outer/url?id=${id}.mp3`

除此之外也可以通过 Meting API 获取相关资源。

实现

新建 themes\butterfly\scripts\tag\music.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
'use strict'
function music(args) {
args = args.join(' ').split(',')
let title = (args[0] || '未知').trim()
let author = (args[1] || '未知').trim()
let url = (args[2] || '').trim()
let pic = (args[3] || '').trim()
let lrc = (args[4] || '').trim()
return `<center><figure id="mc-${title}" class="music-card">
<div class="player">
<audio class="audio" preload="metadata"></audio>
</div>
<div class="content">
<div class="mc-progress" onclick="setProgress(event, 'mc-${title}')">
<div class="mc-progressbar"></div>
</div>
<div class="cover">
<img src="${pic}">
<div class="play" onclick="ctrl.mcToggleMusic('mc-${title}', '${url}')">
<i class="blogfont icon-play-circle"></i>
</div>
</div>
<div class="text-info">
<span class="title">${title}</span>
<span class="author">${author}</span>
<span class="time">--:--&nbsp;/&nbsp;--:--</span>
</div>
<div class="add" onclick="ctrl.addMusicToList('${title}','${author}','${url}','${pic}','${lrc}')">
<i class="blogfont icon-add"></i>
</div>
</div>
<script>
function mcAddListener(id) {
const mc = document.getElementById(id)
if (mc) {
const mc_audio = mc.querySelector('.audio')
const mc_play = mc.querySelector(".play i")
let intervalId;
mc_audio.addEventListener('play', function() {
mc_play.classList.remove("icon-play-circle")
mc_play.classList.add("icon-pause-circle")
intervalId = setInterval(function() {
ctrl.mcRefreshTime(id)
}, 500);
})
mc_audio.addEventListener('pause', function() {
mc_play.classList.remove("icon-pause-circle")
mc_play.classList.add("icon-play-circle")
clearInterval(intervalId)
})
}
}
function setProgress(e, id) {
const mc = document.getElementById(id)
if (mc && mc.querySelector(".content").classList.contains("canplay")) {
const progressBar = mc.querySelector('.mc-progressbar')
const audio = mc.querySelector('.audio')
const clickX = e.clientX - mc.getBoundingClientRect().left
const width = clickX / mc.offsetWidth
progressBar.style.transform = 'translateX(-' + (1 - width) * 100 + '%)'
audio.currentTime = width * audio.duration
ctrl.mcRefreshTime(id)
}
}
mcAddListener('mc-${title}')
</script>
</figure></center>`
}

hexo.extend.tag.register('music', music, { ends: false })

新建 themes\butterfly\source\css\/_tags\music.styl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
.music-card
border-radius: 21px
border: 1px solid var(--gavin-border-color)
background-color: var(--gavin-widget-bg1)
box-shadow: var(--gavin-shadow-1)
height: 80px
width: 100%
max-width: 350px
margin: 1em 0
overflow: hidden

.content
position: relative
display: flex
align-items: center
width: 100%
height: 100%

.mc-progress
width: 100%
height: 100%
z-index: 10
display: flex
justify-content: left

.mc-progressbar
height: 100%
width: 100%
transform: translateX(-100%)
background-color: var(--gavin-highlight-op-1)

.cover
position: absolute
left: 10px
top: 10px
width: 60px
height: 60px
background-color: var(--gavin-secondbg)
border: 1px solid var(--gavin-border-color)
border-radius: 10px
overflow: hidden
z-index: 11

.play
position: absolute
top: 0
width: 100%
height: 100%
display: flex
align-items: center
justify-content: center
cursor: pointer
opacity: 0

i
display: flex
align-items: center
justify-content: center
font-size: 35px
color: rgba(255,255,255,.8)

&:hover
.play
opacity: 1

&.canplay
a
img
filter: brightness(0.8) !important

.play
opacity: 1

.text-info
position: absolute
left: 80px
max-width: calc(100% - 150px)
display: flex
flex-direction: column
text-align: left
z-index: 9

.title
font-size: 16px
line-height: 1.3
display: -webkit-box
overflow: hidden
-webkit-box-orient: vertical
-webkit-line-clamp: 1

.author
font-size: 13px
line-height: 1.3
opacity: .8
display: -webkit-box
overflow: hidden
-webkit-box-orient: vertical
-webkit-line-clamp: 1

.time
font-size: 10px
line-height: 1.4
display: -webkit-box
overflow: hidden
-webkit-box-orient: vertical
-webkit-line-clamp: 1
opacity: .6

.add
position: absolute
right: 20px
top: 20px
width: 40px
height: 40px
border-radius: 50%
background-color: var(--gavin-op-dis-05)
display: flex
align-items: center
justify-content: center
cursor: pointer
z-index: 11

&:hover
background-color: var(--gavin-op-dis-1)

i
display: flex
align-items: center
justify-content: center
font-size: 21px

自定义 custom.js 中加入以下代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
var ctrl = {
clearConsoleMusicList() {
document.getElementById("console-music-list").innerHTML = ''
},

addMusicToList(title, author, url, pic, lrc) {
var ap = document.querySelector("meting-js.global-music").aplayer;
ctrl.clearConsoleMusicList();
// ap.list.clear();
ap.list.add([{"title": title, "author": author, "url": url, "pic": pic, "lrc": lrc}]);
ap.list.switch(ap.list.audios.length - 1)
},

mcToggleMusic(id, url) {
const mc = document.getElementById(id)
if (mc) {
const c = mc.querySelector(".content")
if (!c.classList.contains("canplay")) {
const a = mc.querySelector(".audio")
a.src = url
a.addEventListener('loadeddata', function f() {
c.classList.add("canplay")
a.removeEventListener('loadeddata', f)
});
}
const mc_audio = mc.querySelector(".audio")
if (mc_audio.paused) {
mc_audio.play()
} else {
mc_audio.pause()
}
}
},

mcRefreshTime(id) {
var mc = document.getElementById(id)
if (mc) {
var mc_audio = mc.querySelector(".audio")
var t0 = mc_audio.currentTime + 0.5
var t1 = mc_audio.duration
var _t0 = tools.secToTime(t0)
var _t1 = tools.secToTime(t1)
mc.querySelector(".time").innerHTML = `${_t0}&nbsp;/&nbsp;${_t1}`
mc.querySelector(".mc-progressbar").style.transform = "translateX(-" + ((1 - (t0 / t1)) * 100) + "%)"
}
}
}

var tools = {
secToTime(s) {
if (isNaN(s)) s = 0;
var min = Math.floor(s / 60);
var sec = Math.floor(s % 60);
var t = min.toString().padStart(2, '0') + ":" + sec.toString().padStart(2, '0');
return t;
}
}