书接上文,因为上次还有未能实现的想法,所以接着鼓捣。

唱片架

稍稍修改了结构和样式,加上了“添至列表”按钮,点击后就会把这张专里的内容加进左下角播放器的播放列表。

篇幅所限,结构和样式就先省略了。

添至列表

主要加上了点击“添至列表”的逻辑。

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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
/**
* 配合标签外挂music食用~
*/

import useSnackbar from "../Tools/useSnackbar.js"
import addCoverColor from "./addCoverColor.js"

// 播放事件委托(
window.addEventListener("click", async (event) => {
const target = event.target
const tagName = target.tagName

const dataset = target.dataset

// 参数有误
if (dataset.server == "undefined" || dataset.type == "undefined" || dataset.id == "undefined") {
return
}

// 点击操作按钮
if (tagName.toUpperCase() == "BUTTON" && target.classList.contains("music-play-btn")) {
// 获得音乐列表
const res = await getMusicList({
server: dataset.server,
type: dataset.type,
id: dataset.id
})

const ap = window.fixedap

if (ap) {
if (target.classList.contains("play-album")) {
// 清空并添加
ap.list.clear()
ap.list.add(res)
ap.setMode("normal")
ap.play()
useSnackbar("让你见识见识我珍藏已久的好曲子(〃` 3′〃)")
} else if (target.classList.contains("add-album")) {
// 目前的播放列表
const nowList = ap.list.audios
// 去重
const res2 = res.filter(r => {
return !nowList.some(n => {
return n.url === r.url
})
})

// 全是重复
if (res2.length === 0) {
useSnackbar("添加过的就不要再加啦w(゚Д゚)w")
return
}

// 添加
ap.list.add(res2)
ap.setMode("normal")

// 滚动音乐列表
const apDOM = ap.options.container
const apolDOM = apDOM.querySelector("ol")
const scrollHeight = apolDOM.scrollHeight
apolDOM.scrollTo({ top: scrollHeight, behavior: "smooth" })

useSnackbar("加到播放列表里啦,慢慢听ᕕ( ᐛ )ᕗ")
}
} else {
return
}
}

// 点击标题
else if (target.classList.contains("music-to-site")) {
let href

switch(dataset.server) {
case("netease"):
href = `https://music.163.com/#/${dataset.type}?id=${dataset.id}`
break
case("tencent"):
let type
if(dataset.type === "song") {
type = "songDetail"
} else if(dataset.type === "album") {
type = "albumDetail"
} else type = "playlist"

href = `https://y.qq.com/n/ryqq/${type}/${dataset.id}`
break
}

console.log(href)

if(href) {
window.open(href)
}
}
}, true)

// 获得音乐列表
const getMusicList = async (options) => {
if (typeof (options) !== "object") {
return
}

let api
let result

//初始化
init()
await parse()

function init() {
api =
options.api ||
window.meting_api ||
'https://api.i-meto.com/meting/api?server=:server&type=:type&id=:id&r=:r'

if (options.auto) _parse_link()

function _parse_link() {
let rules = [
['music.163.com.*song.*id=(\\d+)', 'netease', 'song'],
['music.163.com.*album.*id=(\\d+)', 'netease', 'album'],
['music.163.com.*artist.*id=(\\d+)', 'netease', 'artist'],
['music.163.com.*playlist.*id=(\\d+)', 'netease', 'playlist'],
['music.163.com.*discover/toplist.*id=(\\d+)', 'netease', 'playlist'],
['y.qq.com.*song/(\\w+).html', 'tencent', 'song'],
['y.qq.com.*album/(\\w+).html', 'tencent', 'album'],
['y.qq.com.*singer/(\\w+).html', 'tencent', 'artist'],
['y.qq.com.*playsquare/(\\w+).html', 'tencent', 'playlist'],
['y.qq.com.*playlist/(\\w+).html', 'tencent', 'playlist'],
['xiami.com.*song/(\\w+)', 'xiami', 'song'],
['xiami.com.*album/(\\w+)', 'xiami', 'album'],
['xiami.com.*artist/(\\w+)', 'xiami', 'artist'],
['xiami.com.*collect/(\\w+)', 'xiami', 'playlist'],
]

for (let rule of rules) {
// 返回匹配
// eg: "https://y.qq.com/n/yqq/song/001RGrEX3ija5X.html"
// ["y.qq.com/n/yqq/song/001RGrEX3ija5X.html", "001RGrEX3ija5X"]
let patt = new RegExp(rule[0])
let res = patt.exec(options.auto)

if (res !== null) {
options.server = rule[1]
options.type = rule[2]
options.id = res[1]
return
}
}
}
}

async function parse() {
if (options.url) {
// 直接构建 APlayer 配置并加载 APlayer
let res = {
name: options.name || options.title || 'Audio name',
artist: options.artist || options.author || 'Audio artist',
url: options.url,
cover: options.cover || options.pic,
lrc: options.lrc || options.lyric || '',
type: options.type || 'auto',
}

try {
result = await addCoverColor(res)
} catch {

}

return
}

// // 1. 通过 meta 拼凑接口参数获得完整接口 (_init 中存放的默认 api)
// // 2. 请求接口,得到播放列表数据
// // 3. 加载 APlayer
let url = api
.replace(':server', options.server)
.replace(':type', options.type)
.replace(':id', options.id)
.replace(':auth', options.auth)
.replace(':r', Math.random())

const r = await fetch(url)
const res = await r.json()

result = res
}

try {
await addCoverColor(result)
} catch {

}

return result
}

提取封面颜色值

为播放器样式自适应准备的东西。

根据图片获取平均色值

使用canvas计算图片的rgb色值

获取网络图片应该是个异步的操作,所以返回一个Promise。

QQ音乐源的封面出现了跨域问题,如果有大佬知道怎么解决请务必教导我(网易云源一切正常)。

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
/**
* 计算专辑图片的平均色值(rgb)
*/

// js创建一个canvas元素
const canvas = document.createElement('canvas')

// 表示绘制一个2d图
const context = canvas.getContext("2d",{willReadFrequently:true})

export default (src) => {
// 创建图片元素用来加载图片地址
const img = new Image()

// 将图片地址传给nimg
img.src = src
img.crossOrigin = ""

try {
return new Promise((resolve, reject) => {
img.onload = function () {

// 获取图片的大小
const width = img.width
const height = img.height

// 设置canvas的大小
canvas.width = width
canvas.height = height

// rgba值
let r = 0
let g = 0
let b = 0
let a = 0
const fxs = width * height

// 设置要绘制的图片
context.drawImage(img, 0, 0)

// 获取图片的像素信息,并.data获得数组
const data = context.getImageData(0, 0, width, height).data

// 获取所有的rgba的和
for (let i = 0; i < data.length / 4; i++) {
r += data[i * 4]
g += data[i * 4 + 1]
b += data[i * 4 + 2]
a += data[i * 4 + 3]
}

// 获得平均值
const rgba = [parseInt(r / fxs), parseInt(g / fxs), parseInt(b / fxs), parseInt(a / fxs)]
resolve(rgba)
}
})
} catch(e) {
throw e
}
}

rgb颜色转hsl颜色

转换成hsl颜色而不适用rgb是因为hsl颜色的计算更符合人类的大脑(

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* 将rgb颜色转换成hsl颜色(方便人类计算)
*/

export default (r, g, b) => {
r /= 255, g /= 255, b /= 255
let max = Math.max(r, g, b), min = Math.min(r, g, b)
let h, s, l = (max + min) / 2

if (max == min) {
h = s = 0 // achromatic
} else {
var d = max - min
s = l > 0.5 ? d / (2 - max - min) : d / (max + min)
switch (max) {
case r: h = (g - b) / d + (g < b ? 6 : 0); break
case g: h = (b - r) / d + 2; break
case b: h = (r - g) / d + 4; break
}
h /= 6;
}

return [h, s, l]
}

添加颜色

在获取曲目列表时就顺便给每一个曲目加上一个颜色值。

加了缓存机制,避免重复计算(如果是网络获取音乐,不缓存的话,还要发很多请求)。

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
/**
* 给歌曲加颜色值
*/

import getColor from "../Tools/getColor.js"
import rgbToShl from "../Tools/rgbToHsl.js"

// 缓存封面的颜色值
window.coverCacheMap = new Map()

export default async (audios) => {
for (let i = 0; i < audios.length; i++) {
const cover = audios[i].cover || audios[i].pic
if (cover && !audios[i].theme) {
const cache = window.coverCacheMap.get(cover)
if (cache) {
audios[i].theme = cache
} else {
const rgba = await getColor(cover)
const hsl = rgbToShl(rgba[0], rgba[1], rgba[2])
audios[i].theme = hsl
// 缓存颜色值
window.coverCacheMap.set(cover, hsl)
}
}
}
}

白天/夜晚模式

添加了可供白天/夜晚模式样式切换的方法。

一个小技巧,:root中定义的css变量可以通过js修改从而实现改变样式。这样就不用操作DOM的行内样式啦。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* musicBlock标签外挂 按钮的夜晚/白天样式
*/

export default ()=> {
const htmlEl = document.documentElement

// let darkChange = mode==="dark"

// 判断夜晚模式
if (htmlEl.dataset.theme !== "dark") {
htmlEl.style.setProperty("--music-item-bg", "white")
htmlEl.style.setProperty("--music-item-words", "black")
htmlEl.style.setProperty("--music-item-shadow", "rgba(0,0,0,0.1)")
htmlEl.style.setProperty("--music-item-hover-shadow-1", "rgba(50,50,93,0.25)")
htmlEl.style.setProperty("--music-item-hover-shadow-2", "rgba(255,255,255,0.4)")
} else {
htmlEl.style.setProperty("--music-item-bg", "#444444")
htmlEl.style.setProperty("--music-item-words", "white")
htmlEl.style.setProperty("--music-item-shadow", "rgba(255,255,255,0.1)")
htmlEl.style.setProperty("--music-item-hover-shadow-1", "rgba(50,50,93,0.25)")
htmlEl.style.setProperty("--music-item-hover-shadow-2", "rgba(255,255,255,0.4)")
}
}

随时调用提示框

观察butterfly主题的源码后写的,所以应该只适用于butterfly(悲)。

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
/**
* 随时使用snackbar
*/

export default (str) => {
// window.onload = () => {
// 使用提示框(如果有进行配置的话)
// window.useSnackbar = (str) => {
if (GLOBAL_CONFIG.Snackbar) {
// snackbar配置对象
const snackbar = GLOBAL_CONFIG.Snackbar
// 页面的亮度模式
const dark = document.documentElement.getAttribute('data-theme')
let bgc
if (dark === "light") {
bgc = snackbar.bgLight
} else if (dark === "dark") {
bgc = snackbar.bgDark
} else {
return
}

window.Snackbar.show({
backgroundColor: bgc,
text: str,
actionText: null,
pos: snackbar.position
})
}
}

播放器配色自适应

感觉Aplayer播放器的默认样式颜色太素了,不是很好看。

于是我查阅了APlayer文档,配置项可以配置主题,那我试试看。

但是配置了后啥也没变啊?是我瞎了还是播放器坏了?

观察了114514次后才发现,原来配置后改变颜色的是这玩意……

那我自己去修改样式改变它的配色吧,要做就要做好点,直接按照封面自适应配色(

样式

我就光写css不写styl了吧,styl实在写不习惯(不知道这里能不能写scss(写完编译一下不就行了x)。

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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
:root {
--bg-color: hsl(0,100%,100%);
--bg-color-choose: hsl(0, 0%, 91%);
--scroll-bar-bg-color: hsl(0, 0%, 93%);
--scroll-bar-bg-color-hover: hsl(0, 0%, 50%);
--music-list-cur-color: hsl(204, 64%, 44%);
--music-words-color: hsl(15, 3%, 29%);
--music-words-color-lighter: hsl(0, 0%, 40%);
--music-words-color-lightest: hsl(0, 0%, 60%);
--music-icon-color-hover: hsl(120, 1%, 20%);
--music-player-bar: hsl(0, 0%, 80%);
--music-player-bar-load: hsl(0, 0%, 67%);
--music-player-bar-played: hsl(204, 64%, 44%);
--music-border-color: hsl(0, 0%, 93%);
--music-console-border-color: hsl(0, 0%, 91%);
}

/* 播放列表 */
.aplayer {
background-color: var(--bg-color) !important;
}

/* 播放列表 */
.aplayer.aplayer-fixed .aplayer-list {
border: 1px solid var(--music-border-color) !important;
}

/* 滚动条本体 */
.aplayer .aplayer-list ol::-webkit-scrollbar-thumb {
background-color: var(--scroll-bar-bg-color) !important;
transition: 0.2s all ease;
}

/* 滚动条悬停 */
.aplayer .aplayer-list ol::-webkit-scrollbar-thumb:hover {
background-color: var(--scroll-bar-bg-color-hover) !important;
}

/* 播放列表项 */
.aplayer .aplayer-list ol li {
border-top: 1px solid var(--music-border-color) !important;
}
/* 播放列表项首项 */
.aplayer .aplayer-list ol li:first-child {
border-top: none !important;
}

/* 播放列表项悬停 */
.aplayer .aplayer-list ol li:hover {
background-color: var(--bg-color-choose) !important;
}

/* 正在播放 */
.aplayer .aplayer-list ol li.aplayer-list-light {
background-color: var(--bg-color-choose) !important;
}

/* 正在播放指针 */
.aplayer .aplayer-list ol li.aplayer-list-light .aplayer-list-cur {
background-color: var(--music-list-cur-color) !important;
}

/* 播放列表序号/艺人 */
.aplayer .aplayer-list ol li .aplayer-list-index,
.aplayer .aplayer-list ol li .aplayer-list-author {
color: var(--music-words-color-lighter) !important;
}

/* 播放列表曲目标题 */
.aplayer .aplayer-list ol li .aplayer-list-title {
color: var(--music-words-color) !important;
}

/* 操作台 */
.aplayer.aplayer-fixed .aplayer-body {
background-color: var(--bg-color) !important;
}

/* 操作台边框 */
.aplayer.aplayer-fixed .aplayer-info {
border-top: 1px solid var(--music-console-border-color) !important;
}

/* 操作台曲目标题 */
.aplayer .aplayer-info .aplayer-music .aplayer-title {
color: var(--music-words-color) !important;
}

/* 操作台艺人 */
.aplayer .aplayer-info .aplayer-music .aplayer-author {
color: var(--music-words-color-lighter) !important;
}

/* 操作台图标 */
.aplayer .aplayer-info .aplayer-controller .aplayer-time .aplayer-icon path {
fill: var(--music-words-color-lighter) !important;
}

/* 操作台图标悬停 */
.aplayer .aplayer-info .aplayer-controller .aplayer-time .aplayer-icon:hover path {
fill: var(--music-icon-color-hover) !important;
}

/* 操作台曲目时长 */
.aplayer .aplayer-info .aplayer-controller .aplayer-time .aplayer-time-inner {
color: var(--music-words-color-lightest) !important;
}

/* 操作台进度条 */
.aplayer .aplayer-info .aplayer-controller .aplayer-bar-wrap .aplayer-bar {
background-color: var(--music-player-bar);
}

/* 操作台已加载进度条 */
.aplayer .aplayer-info .aplayer-controller .aplayer-bar-wrap .aplayer-bar .aplayer-loaded {
background-color: var(--music-player-bar-load);
}

/* 操作台已播放进度条 */
.aplayer .aplayer-info .aplayer-controller .aplayer-bar-wrap .aplayer-bar .aplayer-played {
background-color: var(--music-player-bar-played);
}

/* 操作台进度条控制点 */
.aplayer .aplayer-info .aplayer-controller .aplayer-bar-wrap .aplayer-bar .aplayer-played .aplayer-thumb {
background-color: var(--music-player-bar-played);
}

/* 操作台音量条 */
.aplayer .aplayer-info .aplayer-controller .aplayer-volume-wrap .aplayer-volume-bar-wrap .aplayer-volume-bar {
background-color: var(--music-player-bar-load);
}

/* 操作台音量高低条 */
.aplayer .aplayer-info .aplayer-controller .aplayer-volume-wrap .aplayer-volume-bar-wrap .aplayer-volume-bar .aplayer-volume {
background-color: var(--music-player-bar-load);
}

/* 操作台迷你模式开关 */
.aplayer.aplayer-fixed .aplayer-miniswitcher {
background-color: var(--scroll-bar-bg-color) !important;
}

/* 操作台图标 */
.aplayer .aplayer-miniswitcher .aplayer-icon path {
fill: var(--music-words-color-lighter) !important;
}

/* 操作台图标悬停 */
.aplayer .aplayer-miniswitcher:hover .aplayer-icon path {
fill: var(--music-icon-color-hover) !important;
}

配色方案生成

添加一个根据色值自动生成配色的方法吧,兼容白天/夜晚模式。

使用缓存和步进的方式减少运算量(太细致的颜色差别一般人也看不出来x)。

其实我觉得步进值还可以高一点,因为这样理论上最多需要缓存200个配色(虽然实际上应该刷不出这么多)。

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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
import useSnackbar from "../Tools/useSnackbar.js"

/**
* 播放器颜色自适应
*/

// 缓存当前的颜色
window.hueColors = new Map()
// 当前使用的缓存key
window.nowHue

export default () => {
const ap = window.fixedap
if (ap) {
// 获取当前播放的样式
const playList = ap.list
const playIndex = playList.index
const nowStyle = playList.audios[playIndex].theme
try {
changeStyle(nowStyle)
} catch {
new Promise((resolve)=> {
setTimeout(() => {
useSnackbar("因不可抗力因素没得到封面颜色w(゚Д゚)w,但至少不影响你收听啦.....((/- -)/")
resolve()
}, 3000)
})
}
}
}

const hslToStr = (hsl) => {
return `hsl(${hsl[0] * 360},${hsl[1] * 100}%,${hsl[2] * 100}%)`
}

const changeStyle = (hsl) => {
const htmlEl = document.documentElement
const mode = htmlEl.dataset.theme

const hue = hsl[0]

// 每10为一个取色点,如果太黑了就直接gray
const hueKey = Math.round(hue * 100) / 100

let keyName
if (hsl[1] < 0.05) {
keyName = "gray"
} else {
keyName = hueKey
}

let darkChange = mode === "dark"

// 判断夜晚模式
if (darkChange) {
htmlEl.style.setProperty("--music-border-color", "hsl(0, 0%, 20%)")
htmlEl.style.setProperty("--music-console-border-color", "hsl(0, 0%, 40%)")

keyName = mode + keyName
} else {
htmlEl.style.setProperty("--music-border-color", "hsl(0, 0%, 93%)")
htmlEl.style.setProperty("--music-console-border-color", "hsl(0, 0%, 91%)")
}

// 切换的key和上一首的key一样,那就不调整样式了
if (window.nowHue === keyName) {
return
} else {
// 从缓存中获取样式
let colors = window.hueColors.get(keyName)
// 缓存中没有样式,生成
if (!colors) {
// 夜晚模式
if (darkChange) {
// 灰色配色
if (keyName === "gray" || keyName === "darkgray") {
colors = {
bgColor: hslToStr([0, 0, 0.1]),
bgColorChoose: hslToStr([0, 0, 0.2]),
scrollBarBgColor: hslToStr([0, 0, 0.4]),
scrollBarBgColorHover: hslToStr([0, 0, 0.5]),
musicListCurColor: hslToStr([0, 0, 0.6]),
musicWordsColor: hslToStr([0, 0, 1]),
musicWordsColorLighter: hslToStr([0, 0, 0.8]),
musicWordsColorLightest: hslToStr([0, 0, 0.7]),
musicIconColorHover: hslToStr([0, 0, 1]),
musicPlayerBar: hslToStr([0, 0, 0.75]),
musicPlayerBarLoad: hslToStr([0, 0, 0.9]),
musicPlayerBarPlayed: hslToStr([0, 0, 0.1])
}

// 彩色配色
} else {
colors = {
bgColor: hslToStr([hueKey, 0.3, 0.1]),
bgColorChoose: hslToStr([hueKey, 0.5, 0.2]),
scrollBarBgColor: hslToStr([hueKey, 0.25, 0.4]),
scrollBarBgColorHover: hslToStr([hueKey, 0.4, 0.5]),
musicListCurColor: hslToStr([hueKey, 0.9, 0.6]),
musicWordsColor: hslToStr([hueKey, 0, 1]),
musicWordsColorLighter: hslToStr([hueKey, 0.05, 0.8]),
musicWordsColorLightest: hslToStr([hueKey, 0.05, 0.7]),
musicIconColorHover: hslToStr([hueKey, 0.01, 1]),
musicPlayerBar: hslToStr([hueKey, 0.05, 0.75]),
musicPlayerBarLoad: hslToStr([hueKey, 0.05, 0.9]),
musicPlayerBarPlayed: hslToStr([hueKey, 1, 0.44])
}
}
// 白天模式
} else {
// 灰色配色
if (keyName === "gray") {
colors = {
bgColor: hslToStr([0, 0, 0.99]),
bgColorChoose: hslToStr([0, 0, 0.95]),
scrollBarBgColor: hslToStr([0, 0, 0.9]),
scrollBarBgColorHover: hslToStr([0, 0, 0.8]),
musicListCurColor: hslToStr([0, 0, 0.6]),
musicWordsColor: hslToStr([0, 0., 0.29]),
musicWordsColorLighter: hslToStr([0, 0, 0.4]),
musicWordsColorLightest: hslToStr([0, 0, 0.6]),
musicIconColorHover: hslToStr([0, 0, 0.2]),
musicPlayerBar: hslToStr([0, 0, 0.8]),
musicPlayerBarLoad: hslToStr([0, 0, 0.67]),
musicPlayerBarPlayed: hslToStr([0, 0, 0.44])
}

// 彩色配色
} else {
colors = {
bgColor: hslToStr([hueKey, 1, 0.99]),
bgColorChoose: hslToStr([hueKey, 0.5, 0.95]),
scrollBarBgColor: hslToStr([hueKey, 0.4, 0.9]),
scrollBarBgColorHover: hslToStr([hueKey, 0.25, 0.8]),
musicListCurColor: hslToStr([hueKey, 0.9, 0.6]),
musicWordsColor: hslToStr([hueKey, 0.03, 0.29]),
musicWordsColorLighter: hslToStr([0, 0, 0.4]),
musicWordsColorLightest: hslToStr([0, 0, 0.6]),
musicIconColorHover: hslToStr([hueKey, 0.01, 0.2]),
musicPlayerBar: hslToStr([0, 0, 0.8]),
musicPlayerBarLoad: hslToStr([0, 0, 0.67]),
musicPlayerBarPlayed: hslToStr([hueKey, 0.64, 0.44])
}
}
}
// 把新颜色值存入缓存
window.hueColors.set(keyName, colors)
}

// 更改css变量从而改变配色
htmlEl.style.setProperty("--bg-color", colors.bgColor)
htmlEl.style.setProperty("--bg-color-choose", colors.bgColorChoose)
htmlEl.style.setProperty("--scroll-bar-bg-color", colors.scrollBarBgColor)
htmlEl.style.setProperty("--scroll-bar-bg-color-hover", colors.scrollBarBgColorHover)
htmlEl.style.setProperty("--music-list-cur-color", colors.musicListCurColor)
htmlEl.style.setProperty("--music-words-color", colors.musicWordsColor)
htmlEl.style.setProperty("--music-words-color-lighter", colors.musicWordsColorLighter)
htmlEl.style.setProperty("--music-words-color-lightest", colors.musicWordsColorLightest)
htmlEl.style.setProperty("--music-icon-color-hover", colors.musicIconColorHover)
htmlEl.style.setProperty("--music-player-bar", colors.musicPlayerBar)
htmlEl.style.setProperty("--music-player-bar-load", colors.musicPlayerBarLoad)
htmlEl.style.setProperty("--music-player-bar-played", colors.musicPlayerBarPlayed)

// 某几个DOM被设了行内样式,所以这么改
const apDOM = window.fixedap.container
const dot = apDOM.querySelector(".aplayer-thumb")
const bar = apDOM.querySelector(".aplayer-played")
const vol = apDOM.querySelector(".aplayer-volume")
dot.style.backgroundColor = colors.musicPlayerBarPlayed
bar.style.backgroundColor = colors.musicPlayerBarPlayed
vol.style.backgroundColor = colors.musicPlayerBarPlayed

// 保存当前使用key
window.nowHue = keyName
}
}

添加事件

调戏播放器的操作台好像也会触发canplay事件,其实我只希望切换播放时才触发,有没有更好的事件名?

另外感觉夜晚模式的那个按钮有点诡异,我点击后到底是先执行代码逻辑还是先改变data-theme啊。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* 音乐切换时发起事件~
*/

import changeBlockStyle from "./changeBlockStyle.js"
import changePlayerStyle from "./changePlayerStyle.js"

export default () => {
const ap = window.fixedap
// 更改播放时刷新样式
ap.on("canplay", () => {
changePlayerStyle()
})

// 点击夜晚模式按钮时刷新样式
document.addEventListener("click", (e)=> {
const target = e.target
const btn = document.getElementById("darkmode")
if(target === btn || target.parentNode === btn) {
changePlayerStyle()
changeBlockStyle()
}
})
}

播放器实例问题

找到了解决方法,直接在开启应用(Window.onload)时就读取播放器实例然后存在全局window中。

亲测除了刷新否则不会丢,这样就可以随时操作该实例,不用每次都创建了(创建新实例还容易出bug)。

顺便把其他需要初始化的东西也丢进去。

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
import changePlay from "./changePlay.js"
import addCoverColor from "./addCoverColor.js"
import changePlayerStyle from "./changePlayerStyle.js"
import changeBlockStyle from "./changeBlockStyle.js"

// 保存所有aplayer实例到全局上(butterfly本身为什么切换页面就失去window.aplayers了?)
window.onload = async () => {
const aps = window.aplayers
// 获得全局吸底aplayer(有fixed配置的那个)
aps.some(a => {
if (a.options.fixed) {
window.fixedap = a
return true
}
return false
})

// 我不知道aplayer播放器有没有列表生成完毕的事件,所以先干脆直接上轮询(
let timer = setInterval(async () => {
// 为初始播放列表增加配色
if(window.fixedap.list?.audios?.length>0) {
await addCoverColor(window.fixedap.list.audios)

// 初始化播放器样式
changePlayerStyle()

// 绑定使播放器样式变化的事件
changePlay()

clearInterval(timer)
}
},500)

// 初始化音乐卡样式
changeBlockStyle()
}

碎碎念

能保存播放器实例后玩得舒服多了。

但是好像因为服务端的设置,我无法获取QQ源封面的平均颜色,如果哪位大佬有解决方法请狠狠地教导我orz。

有时间的话想把获取音乐的接口换一下,要不用网易云音乐API/QQ音乐API自己造一个接口?

如果可以的话我甚至还想在网站背景加个音乐可视化(错乱)