上一篇:导航、首页与文章页配置 · 系列总目录 · 下一篇:使用 Inject 添加自定义组件

前言

Butterfly 默认把页首、正文区域和页脚看成三个不同层次。分别设置图片和遮罩很方便,但当我们希望整页呈现为一张连续画面时,三个区域之间就容易出现明显色彩分层。

一图流的核心并不是把同一张图片贴三遍,而是建立一个全局背景层,让所有半透明内容卡片覆盖在它上面。渐变只负责保证文字可读,不能把背景在某个固定高度切断。


一、先设计背景资源

至少准备四张图片:

1
2
3
4
background-light-desktop.webp
background-dark-desktop.webp
background-light-mobile.webp
background-dark-mobile.webp

桌面背景可以使用 2560×1440 或更高分辨率,手机背景应使用竖屏构图。仅靠 background-size: cover 把横图裁成手机尺寸,通常会丢失人物或视觉中心。

图片选择还要考虑:

  • 标题所在区域不能过亮或过于复杂。
  • 正文背后应保留较平静的色块。
  • 暗色背景不能只有纯黑,需要保留层次。
  • 文件大小应控制在合理范围。
  • 优先使用 WebP 或 AVIF。

二、关闭容易造成分层的原生遮罩

_config.butterfly.yml 中:

1
2
3
mask:
header: false
footer: false

同时明确设置背景:

1
2
3
default_top_img: /img/background-light-desktop.webp
footer_img: /img/background-light-desktop.webp
background: /img/background-light-desktop.webp

这一步只能关闭原生遮罩。如果自定义 CSS 中还存在覆盖下半区的伪元素或线性渐变,也需要一起检查。

常见分层来源包括:

1
2
3
4
#page-header::before
#footer::before
#web_bg::after
body::before

以及从某个固定百分比突然改变透明度的渐变。


三、建立独立的全局背景层

Butterfly 页面中存在 #web_bg,可以把它作为固定背景:

1
2
3
4
5
6
7
8
9
10
11
12
#web_bg {
position: fixed;
inset: 0;
z-index: -10;
width: 100%;
height: 100%;
background-image: url('/img/background-light-desktop.webp');
background-position: center top;
background-size: cover;
background-repeat: no-repeat;
transition: opacity 0.45s ease, filter 0.45s ease;
}

关键点是:

  • 使用 fixed,滚动时背景不会在正文开始处重新贴一次。
  • 使用 inset: 0 覆盖整个视口。
  • 背景层保持在内容之后。
  • 正文、页脚本身不要再各自绘制不一致的背景图。

正文卡片可以保留轻微半透明:

1
2
3
4
5
6
7
#recent-posts > .recent-post-item,
#aside-content .card-widget,
.layout > div:first-child:not(.recent-posts),
#post {
background: rgba(255, 255, 255, 0.82);
backdrop-filter: blur(10px);
}

透明度越低,画面越明显,但文字可读性也越差。不要为了“一图流”把正文卡片做成完全透明。


四、连续淡化应该怎样实现

错误的做法通常是:

1
2
3
background:
linear-gradient(transparent 40%, white 50%),
url(...);

当渐变在很短的区间完成变化时,会形成明显横线;如果把白色透明度推到 100%,页面底部又会完全看不到背景。

更自然的方式,是在全局背景上加入范围更长、强度更低的光照层:

1
2
3
4
5
6
7
8
9
10
11
12
13
body::before {
position: fixed;
inset: 0;
z-index: -9;
content: '';
pointer-events: none;
background: linear-gradient(
to bottom,
rgba(255, 255, 255, 0.02) 0%,
rgba(255, 255, 255, 0.10) 48%,
rgba(255, 255, 255, 0.18) 100%
);
}

它不会把背景覆盖成白色,只会随着页面向下增加少量柔和亮度。

也可以根据滚动位置动态调整一个 CSS 变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function updateBackgroundFade() {
var max = Math.max(
document.documentElement.scrollHeight - window.innerHeight,
1
);
var progress = Math.min(window.scrollY / max, 1);
document.documentElement.style.setProperty(
'--page-scroll-progress',
progress.toFixed(3)
);
}

window.addEventListener('scroll', updateBackgroundFade, { passive: true });
updateBackgroundFade();

CSS 中使用:

1
2
3
4
5
#web_bg {
filter:
brightness(calc(1 - var(--page-scroll-progress, 0) * 0.08))
saturate(calc(1 - var(--page-scroll-progress, 0) * 0.06));
}

变化幅度应保持很小。目标是让背景安静下来,而不是滚到底时完全消失。


五、亮色和暗色背景切换

Butterfly 会在根元素上设置:

1
<html data-theme="dark">

因此可以通过属性选择器切换背景:

1
2
3
4
5
6
7
#web_bg {
background-image: url('/img/background-light-desktop.webp');
}

[data-theme='dark'] #web_bg {
background-image: url('/img/background-dark-desktop.webp');
}

不过 background-image 本身不能像透明度一样自然插值。直接替换图片时仍可能闪烁。更平滑的方案是准备两个固定层,通过透明度交叉淡入:

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
body::before,
body::after {
position: fixed;
inset: 0;
z-index: -10;
content: '';
background-position: center top;
background-size: cover;
transition: opacity 0.55s ease;
}

body::before {
background-image: url('/img/background-light-desktop.webp');
opacity: 1;
}

body::after {
background-image: url('/img/background-dark-desktop.webp');
opacity: 0;
}

[data-theme='dark'] body::before {
opacity: 0;
}

[data-theme='dark'] body::after {
opacity: 1;
}

实际项目中要注意伪元素是否已经被其他组件占用。本站将背景切换和氛围动画集中在独立样式及脚本中,避免多个文件同时操作同一伪元素。


六、手机端使用独立图片

1
2
3
4
5
6
7
8
9
10
11
@media (max-width: 768px) {
body::before {
background-image: url('/img/background-light-mobile.webp');
background-position: center top;
}

body::after {
background-image: url('/img/background-dark-mobile.webp');
background-position: center top;
}
}

手机端还应该:

  • 减少模糊半径,降低 GPU 压力。
  • 减少大面积粒子数量。
  • 关闭跟随鼠标的效果。
  • 确保卡片与屏幕边缘留有间距。
  • 避免背景人物被侧栏或标题完全遮挡。

Chrome 开发者工具中的手机预览只能做初步检查,最后仍应在真实手机上测试。


七、加入克制的氛围动画

亮色主题可以使用:

  • 缓慢移动的柔光。
  • 非循环突变的海面反射。
  • 很低透明度的漂浮光点。

暗色主题可以使用:

  • 星点闪烁。
  • 偶尔出现的流星。
  • 标题和个人信息的轻微霓虹。

动画出现跳跃通常是因为 @keyframes 的首尾状态不一致:

1
2
3
4
5
6
7
8
9
10
11
@keyframes glow-drift {
0%,
100% {
transform: translate3d(-2%, 0, 0);
opacity: 0.25;
}
50% {
transform: translate3d(2%, -1%, 0);
opacity: 0.42;
}
}

首尾使用相同状态,循环连接才不会突然跳回。

还应尊重系统设置:

1
2
3
4
5
6
7
8
9
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
scroll-behavior: auto !important;
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
}
}

不要让动态效果成为阅读的前景。背景动画应该在停止观察时几乎感觉不到,在主动留意时又能发现细节。


八、保持页脚内容清晰

一图流经常会误把整个 footer 设置透明度:

1
2
3
#footer {
opacity: 0.5;
}

这会连带降低文字、链接和图标透明度。正确做法是只调整背景层:

1
2
3
4
5
6
7
8
9
10
11
12
#footer {
position: relative;
background: transparent;
}

#footer::before {
position: absolute;
inset: 0;
z-index: -1;
content: '';
background: rgba(10, 20, 35, 0.14);
}

页脚内容本身保持 opacity: 1


九、本篇检查清单

  • 从页首滚动到正文时没有横向色带
  • 页面底部仍能看到背景
  • 页脚文字没有随背景一起淡化
  • 明暗主题切换没有明显白闪或黑闪
  • 手机使用竖屏背景
  • 背景不会盖住正文
  • 动画首尾连续
  • 减少动态效果设置生效

下一篇将不再停留在外观配置,而是建立一套可以反复复用的 Butterfly 自定义组件加载方式。


参考资料与声明

本文结合本站一图流背景的多轮调整过程整理。部分结构与文字由 AI 协助完成。


上一篇:导航、首页与文章页配置 · 返回系列总目录 · 下一篇:使用 Inject 添加自定义组件