时间轴

2025-12-27

init


在Codepen上看到的,稍微改了一下,效果可以看本站aside的card或nav。

实现思路

主要思路是利用多层叠加,共四层

1
2
3
4
5
6
7
8
<div class="liquidGlass-wrapper">
<div class="liquidGlass-effect"></div> <!-- 模糊 + 扭曲 -->
<div class="liquidGlass-tint"></div> <!-- 半透明白色蒙层 -->
<div class="liquidGlass-shine"></div> <!-- 内发光高光 -->
<div class="liquidGlass-box"> <!-- 实际内容容器 -->
<!-- 内容 -->
</div>
</div>

liquidGlass-wrapper

容器壳

1
2
3
4
5
6
7
8
.liquidGlass-wrapper {
box-shadow: 0 6px 6px rgba(0, 0, 0, 0.2), 0 0 20px rgba(0, 0, 0, 0.1) !important;
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 2.2) !important;
background: none !important;
backdrop-filter: none !important;
-webkit-backdrop-filter: none !important;
border-style: none !important;
}
  • 作用:最外层容器,负责整体阴影和动画过渡。
  • 关键点
    • background: nonebackdrop-filter: none 是为了不让它自己产生模糊,把模糊交给内部 .liquidGlass-effect
    • 阴影模拟玻璃的立体感。
    • transition 让 hover 时的圆角和 padding 变化更流畅。

liquidGlass-effect

核心“液态”模糊层

1
2
3
4
5
6
7
8
9
10
.liquidGlass-effect {
position: absolute;
z-index: 0;
inset: 0;

backdrop-filter: blur(3px);
filter: url(#glass-distortion);
overflow: hidden;
isolation: isolate;
}
  • backdrop-filter: blur(3px)
    对背景内容(如网页其他部分)做模糊处理 → 这是“毛玻璃”的基础。
  • filter: url(#glass-distortion)这是“液态”感的关键!它引用了一个 SVG 滤镜(#glass-distortion),通常定义在页面 <svg> 中,用于对图像施加扭曲、波纹、液态流动等效果。
  • isolation: isolate 确保该层创建新的层叠上下文,避免滤镜影响其他元素。

glass-distortion

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
<svg style="display: none">
<filter
id="glass-distortion"
x="0%"
y="0%"
width="100%"
height="100%"
filterUnits="objectBoundingBox"
>
<feTurbulence
type="fractalNoise"
baseFrequency="0.01 0.01"
numOctaves="1"
seed="5"
result="turbulence"
/>
<!-- Seeds: 14, 17, -->

<feComponentTransfer in="turbulence" result="mapped">
<feFuncR type="gamma" amplitude="1" exponent="10" offset="0.5" />
<feFuncG type="gamma" amplitude="0" exponent="1" offset="0" />
<feFuncB type="gamma" amplitude="0" exponent="1" offset="0.5" />
</feComponentTransfer>

<feGaussianBlur in="turbulence" stdDeviation="3" result="softMap" />

<feSpecularLighting
in="softMap"
surfaceScale="5"
specularConstant="1"
specularExponent="100"
lighting-color="white"
result="specLight"
>
<fePointLight x="-200" y="-200" z="300" />
</feSpecularLighting>

<feComposite
in="specLight"
operator="arithmetic"
k1="0"
k2="1"
k3="1"
k4="0"
result="litImage"
/>

<feDisplacementMap
in="SourceGraphic"
in2="softMap"
scale="150"
xChannelSelector="R"
yChannelSelector="G"
/>
</filter>
</svg>

liquidGlass-tint

色调蒙层

1
2
3
4
5
6
.liquidGlass-tint {
z-index: 1;
position: absolute;
inset: 0;
background: rgba(255, 255, 255, 0.25);
}
  • 在模糊层之上叠加一层半透明白色(也可改为浅蓝、浅灰等)。
  • 模拟真实玻璃的“反光底色”,让文字/内容更易读。
  • z-index: 1 确保它在 effect 之上、shine 之下。

liquidGlass-shine

高光层

1
2
3
4
5
6
7
8
9
10
.liquidGlass-shine {
position: absolute;
inset: 0;
z-index: 2;

overflow: hidden;

box-shadow: inset 2px 2px 1px 0 rgba(255, 255, 255, 0.5),
inset -1px -1px 1px 1px rgba(255, 255, 255, 0.5);
}
  • 使用 内阴影(inset box-shadow) 模拟玻璃边缘的高光和微反光
  • 上右亮(+2px 白光),下左稍暗(-1px 白光)→ 营造光照方向感。
  • z-index: 2 放在最上层(但低于内容)。

liquidGlass-box

内容层

1
2
3
4
5
6
7

.liquidGlass-box {
z-index: 3;
position: relative;
box-shadow: none !important;
}

  • 真正放内容的地方(如头像、文字)。
  • z-index: 3 确保内容在所有玻璃效果层之上,清晰可见。
  • position: relative 使其脱离普通流,但仍在 wrapper 内。

交互效果(Hover 动态)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/* OTHER STYLES */

.liquidGlass-wrapper,
.liquidGlass-wrapper > div:not(.liquidGlass-box) {
padding: 0.4rem !important;
border-radius: var(--glass-border-radius) !important;
}

.liquidGlass-wrapper:hover {
padding: 0.6rem !important;
border-radius: calc(var(--glass-border-radius) + 1em) !important;
}

.liquidGlass-wrapper > div:not(.liquidGlass-box):hover {
border-radius: calc(var(--glass-border-radius) + 1em) !important;
}
  • 悬停时
    • 圆角变大(+1em)→ 模拟“液体膨胀”或“柔软变形”;
    • 内边距增大 → 整体“呼吸感”;
    • 所有非内容层同步圆角变化,保持视觉一致。

应用到butterfly的例子

下面以card_author为例,即显示作者信息的卡片。

card_author.pug

修改themes\butterfly\layout\includes\widget\card_author.pug

1
2
3
4
5
6
7
8
9
10
11
12
13
if theme.aside.card_author.enable
.card-widget.card-info.text-center.liquidGlass-wrapper
.liquidGlass-effect
.liquidGlass-tint
.liquidGlass-shine
.liquidGlass-box
div.card-info-avatar
.avatar-img
img(
src=url_for(theme.avatar.img)
onerror="this.onerror=null;this.src='" + url_for(theme.error_img.flink) + "'"
alt="avatar"
)

liquid_glass.css

创建自定义css文件themes\butterfly\source\css\liquid_glass.css

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
/* LIQUID GLASS STYLES */
:root {
--glass-border-radius: 1.2rem; /* 默认圆角 */
}

.liquidGlass-wrapper {
box-shadow: 0 6px 6px rgba(0, 0, 0, 0.2), 0 0 20px rgba(0, 0, 0, 0.1) !important;
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 2.2) !important;
background: none !important;
backdrop-filter: none !important;
-webkit-backdrop-filter: none !important;
border-style: none !important;
}

.liquidGlass-effect {
position: absolute;
z-index: 0;
inset: 0;

backdrop-filter: blur(3px);
filter: url(#glass-distortion);
overflow: hidden;
isolation: isolate;
}

.liquidGlass-tint {
z-index: 1;
position: absolute;
inset: 0;
background: rgba(255, 255, 255, 0.25);
}

.liquidGlass-shine {
position: absolute;
inset: 0;
z-index: 2;

overflow: hidden;

box-shadow: inset 2px 2px 1px 0 rgba(255, 255, 255, 0.5),
inset -1px -1px 1px 1px rgba(255, 255, 255, 0.5);
}

.liquidGlass-box {
z-index: 3;
position: relative;
box-shadow: none !important;
}

/* OTHER STYLES */

.liquidGlass-wrapper,
.liquidGlass-wrapper > div:not(.liquidGlass-box) {
padding: 0.4rem !important;
border-radius: var(--glass-border-radius) !important;
}

.liquidGlass-wrapper:hover {
padding: 0.6rem !important;
border-radius: calc(var(--glass-border-radius) + 1em) !important;
}

.liquidGlass-wrapper > div:not(.liquidGlass-box):hover {
border-radius: calc(var(--glass-border-radius) + 1em) !important;
}

inject

主题的_config.yaml的inject中引入

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
inject:
head:
- <link rel="stylesheet" href="/css/liquid_glass.css">
bottom:
- |
<svg style="display: none">
<filter
id="glass-distortion"
x="0%"
y="0%"
width="100%"
height="100%"
filterUnits="objectBoundingBox"
>
<feTurbulence
type="fractalNoise"
baseFrequency="0.01 0.01"
numOctaves="1"
seed="5"
result="turbulence"
/>
<!-- Seeds: 14, 17, -->

<feComponentTransfer in="turbulence" result="mapped">
<feFuncR type="gamma" amplitude="1" exponent="10" offset="0.5" />
<feFuncG type="gamma" amplitude="0" exponent="1" offset="0" />
<feFuncB type="gamma" amplitude="0" exponent="1" offset="0.5" />
</feComponentTransfer>

<feGaussianBlur in="turbulence" stdDeviation="3" result="softMap" />

<feSpecularLighting
in="softMap"
surfaceScale="5"
specularConstant="1"
specularExponent="100"
lighting-color="white"
result="specLight"
>
<fePointLight x="-200" y="-200" z="300" />
</feSpecularLighting>

<feComposite
in="specLight"
operator="arithmetic"
k1="0"
k2="1"
k3="1"
k4="0"
result="litImage"
/>

<feDisplacementMap
in="SourceGraphic"
in2="softMap"
scale="150"
xChannelSelector="R"
yChannelSelector="G"
/>
</filter>
</svg>

参考