alist美化

alist作为一个优秀的开源网盘程序,其用户基数是非常庞大的,所以这其中也就不乏有我这样的美化小王子(无敌大闲逼)的存在。因此,现在网上已经有了很多文章贴出了alist自定义头部与内容的美化代码,例如Alist V3 美化教程(4)(这哥们貌似是alist美化的鼻祖),这种美化的代码大概如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
<style>
/*readme白天模式透明*/
.hope-c-PJLV.hope-c-PJLV-ikSuVsl-css {
background-color: rgba(255, 255, 255, 0.2) !important;
}
/*readme夜间模式透明*/
.hope-c-PJLV.hope-c-PJLV-iiuDLME-css {
background-color:rgb(0 0 0 / 20%) !important;
}

/* ...其他样式 */
</style>

提出问题

这种美化方式有一个明显的问题。alist作为一个webapp,页面的内容是动态生成的,并且不像静态网站,其元素名是不保证其相对稳定的。因此,使用这种方式美化alist的代码也是不可靠的,可能在alist更新之后,类名发生更改,部分美化就失效了。更何况,alist每次更新都会带来一些新的特性,虽然其大部分都是功能性的,但有时也会有一些新的前段元素与样式变动出现,因此,使用这种方式美化alist的代码需要不断地维护。

我随便找了个一个月前的美化代码(来自Alist美化教程),发现其已经过时。

应有效果:

应有效果

实际效果:

实际效果

显然,地址栏、功能栏与展示方式切换按钮的美化都失效了。事实上,原版截图的搜索栏美化应该已经失效了。

解决问题

我反正是懒得每次都去维护这些代码,所以我就想到了个一劳永逸的美化方案。这个方案的核心思想是:关注alist元素中的一些特征,使用javascript捕获它们,然后动态替换样式。这样一来,就不需要担心alist更新后,类名发生变化导致美化失效的问题了。找了一圈,发现没有已有的方案,遂决定自己动手撸一个。

alist js美化方案

代码我已贴在了我的github存储库上,你也可以直接在下方查看。

代码

注意!要想使用这段代码,你必须将代码最尾部的themeColor变量设置为你alist的主题色(如果没有设置主题色,可以不填),而且需要写成rgb(r, g, b)的标准格式。

推荐一起使用我的自定义头部代码,这样效果更佳(不然没有背景)。

自定义内容

不过好像放自定义头部也能跑…

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
<!-- v1 -->

<!-- 修正部分区域的透明 -->
<style>
.hope-ui-light,
.hope-ui-dark {
--hope-colors-background: transparent;
}
</style>

<script type="module">
// 提供用来监听代码控制的 url 变化的事件
(() => {
const wrapHistoryMethod = (type) => {
const orig = history[type];
return function (...args) {
const rv = orig.apply(this, args);
const event = new CustomEvent(type, { detail: args });
window.dispatchEvent(event);
return rv;
};
};
history.pushState = wrapHistoryMethod('pushState');
history.replaceState = wrapHistoryMethod('replaceState');
})();

class Beautifier {
/**
* Beautifier 类用于美化页面背景色
*
* 其提供了3个方法:
* - observe: 开始监听页面变化并美化背景色
* - disconnect: 停止监听页面变化
* - undo: 恢复页面背景色到默认状态
*
* 可以通过window.beautifier访问实例对象
*
*/
static themeColor = 'rgb(24, 144, 255)'; // 这是默认的主题色
static lightBgColor = 'rgba(255, 255, 255, 0.8)';
static darkBgColor = 'rgb(32, 36, 37)';

static lightSelector = '.hope-ui-light :not(.hope-tooltip):not(.hope-close-button):not(a)';
static darkSelector = '.hope-ui-dark :not(.hope-tooltip):not(.hope-close-button):not(a)';
static ignoredColors = [
'rgba(0, 0, 0, 0)',
'rgba(0, 0, 0, 0.65)',
'rgba(0, 0, 0, 0.09)'
];

constructor(themeColor = Beautifier.themeColor, lightBgColor = Beautifier.lightBgColor, darkBgColor = Beautifier.darkBgColor) {
this.themeColor = themeColor;
this.lightBgColor = lightBgColor;
this.darkBgColor = darkBgColor;

this.ignoredColors = [...Beautifier.ignoredColors, this.themeColor];

this.observer = null;
}

/**
* @param {'light'|'dark'} theme
*/
#rewriteBgColor(theme) {
let selector = theme === 'light' ? Beautifier.lightSelector : Beautifier.darkSelector;
let bgColor = theme === 'light' ? this.lightBgColor : this.darkBgColor;

document.querySelectorAll(selector).forEach(element => {
const computedStyle = getComputedStyle(element);

if (computedStyle.backgroundImage !== 'none') {
return;
}

if (!this.ignoredColors.includes(computedStyle.backgroundColor)) {
element.style.backgroundColor = bgColor;
element.setAttribute('data-beautified', 'true');
}
});
}

#beautify() {
if (!location.pathname.startsWith('/@manage') && !location.pathname.startsWith('/@login')) {
this.#rewriteBgColor('light');
this.#rewriteBgColor('dark');
}
}

observe() {
this.observer = new MutationObserver(this.#beautify.bind(this));
this.observer.observe(document.body, {
childList: true,
subtree: true
});

this.#beautify();
}

disconnect() {
if (this.observer) {
this.observer.disconnect();
}
}

undo() {
this.disconnect();

document.body.querySelectorAll('[data-beautified]').forEach(element => {
element.style.backgroundColor = '';
element.removeAttribute('data-beautified');
});
}
}

const beautifier = new Beautifier('rgb(240, 128, 128)'); // 务必在这里填入你设置的主题色,没有设置可以不填
window.beautifier = beautifier;
beautifier.observe();

// 一个愚蠢到有点无敌的修复机制,不过工作良好
(() => {
function fixLogin(pathname) {
if (pathname.startsWith('/@login')) {
beautifier.undo();
}
else {
beautifier.disconnect();
beautifier.observe();
}
}

['popstate', 'pushState', 'replaceState'].forEach(eventType => {
addEventListener(eventType, () => {
fixLogin(location.pathname);
});
});
})();
</script>

自定义头部

建议一起使用,效果更佳。

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
<!-- v1 -->

<!-- 引入自定义字体 -->
<link rel="stylesheet" href="https://s4.zstatic.net/ajax/libs/lxgw-wenkai-webfont/1.7.0/lxgwwenkai-regular.min.css">

<style>
/* 移除原生视频控件 */
video::-webkit-media-controls {
display: none;
}

/* 设置背景图片样式 */
body {
background-repeat: no-repeat;
background-size: cover;
background-attachment: fixed;
background-position: center;
}

/* 在此处的url()里修改背景图片地址 */
/* 加了个深色遮罩,爱护你的眼睛 */
body.hope-ui-dark {
background-color: rgb(32, 36, 37);
background-image: linear-gradient(rgba(32, 36, 37, 0.8), rgba(32, 36, 37, 0.8)), url('https://t.alcy.cc/moez');
}

/* 在此处的url()里修改背景图片地址 */
body.hope-ui-light {
background-image: url('https://t.alcy.cc/moez');
}

/* 统一站点公告的样式 */
.hope-ui-light .hope-c-PJLV-ikJQsXT-css {
background: rgba(255, 255, 255, 0.8) !important;
backdrop-filter: blur(0) !important;
}

.hope-ui-dark .hope-c-PJLV-ikJQsXT-css {
background: rgb(32, 36, 37) !important;
backdrop-filter: blur(0) !important;
}

/* 自定义字体 */
* {
font-family: "LXGW WenKai", sans-serif;
}
</style>

<!-- 看板娘 -->
<script>if (localStorage.getItem('modelId') === null) {
localStorage.setItem('modelId', '9'); // 设置默认模型ID
localStorage.setItem('modelTexturesId', '0'); // 设置默认皮肤ID
}
</script>
<script src="https://cdn.mmoe.work/live2d-widget/autoload.js"></script>

<script src="https://s4.zstatic.net/ajax/libs/js-polyfills/0.1.43/polyfill.min.js?features=String.prototype.replaceAll"></script>

从cdn引入

如果你只需要美化alist背景色,而不需要自定义头部,可以直接从CDN引入这段代码(如果有自定义主题色,记得fork一份,填入主题色后,再改成你自己的仓库)(itdog了一下速度竟出奇的可观):

1
<script type="module" src="https://fastly.jsdelivr.net/gh/adogecheems/alist-beautification@latest/src/beautifier.js"></script>

代码说明

这段代码提供了一个Beautifier类,用于自动修改alist的背景色,其创建的实例会监听DOM的变化,并在页面变动时自动对有背景色的元素应用样式(当然是有选择的)。在控制台中,你可以通过window.beautifier访问这个实例。

它暴露了以下方法:

  • observe(): 开始监听页面变化并应用美化
  • disconnect(): 停止监听页面变化
  • undo(): 恢复元素背景色到默认状态

还有一个愚蠢到有点无敌的修复机制,用于修复登录页面的背景色。

这些方法工作的效果:

工作效果

效果

使用这段代码后,你的alist元素将会有一个统一的背景色。并且在alist版本更新之后,美化器也会工作得相当良好。

不说了上图:

最终效果

最后

如果你有任何问题与建议,欢迎在评论区与Github issues中提出(去github给我点一个star是再好不过的了)。希望这个美化方案能给你一个良好而统一的视觉体验,如要转载,也请希望务必注明出处。