时间轴
时间轴
2025-12-27
init
本文介绍了在Hexo butterfly主题中实现MacOS液态玻璃效果的方法。文章详细阐述了实现思路,即通过多层叠加共四层结构来模拟玻璃质感:最外层容器负责阴影与动画过渡,核心模糊层利用SVG滤镜实现背景扭曲与液态流动感,色调蒙层叠加半透明白色以增强可读性,高光层通过内阴影模拟边缘反光。同时说明了内容层的放置方式及悬停时的动态交互效果,包括圆角变化与内边距调整。最后以作者信息卡片为例,展示了在butterfly主题中修改pug文件、创建自定义CSS并注入的具体步骤。
在 CodePen 上看到的,稍微改了一下,效果可以看本站 aside 的 card 或 nav。
实现思路
主要思路是利用多层叠加,共四层
12345678 | <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
容器壳
12345678 | .liquidGlass-wrapper { box-shadow: 0 6px 6px rgba(0, 0, 0, 0.2), 0 0 20px rgba(0, 0, 0, 0.1) ; transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 2.2) ; background: none ; backdrop-filter: none ; -webkit-backdrop-filter: none ; border-style: none ;} |
- 作用:最外层容器,负责整体阴影和动画过渡。
- 关键点:
background: none和backdrop-filter: none是为了不让它自己产生模糊,把模糊交给内部.liquidGlass-effect。- 阴影模拟玻璃的立体感。
transition让 hover 时的圆角和 padding 变化更流畅。
liquidGlass-effect
核心“液态”模糊层
12345678910 | .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
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556 | <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
色调蒙层
123456 | .liquidGlass-tint { z-index: 1; position: absolute; inset: 0; background: rgba(255, 255, 255, 0.25);} |
- 在模糊层之上叠加一层半透明白色(也可改为浅蓝、浅灰等)。
- 模拟真实玻璃的“反光底色”,让文字/内容更易读。
z-index: 1确保它在 effect 之上、shine 之下。
liquidGlass-shine
高光层
12345678910 | .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
内容层
1234567 | .liquidGlass-box { z-index: 3; position: relative; box-shadow: none ;} |
- 真正放内容的地方(如头像、文字)。
z-index: 3确保内容在所有玻璃效果层之上,清晰可见。position: relative使其脱离普通流,但仍在 wrapper 内。
交互效果(Hover 动态)
12345678910111213141516 | /* OTHER STYLES */.liquidGlass-wrapper,.liquidGlass-wrapper > div:not(.liquidGlass-box) { padding: 0.4rem ; border-radius: var(--glass-border-radius) ;}.liquidGlass-wrapper:hover { padding: 0.6rem ; border-radius: calc(var(--glass-border-radius) + 1em) ;}.liquidGlass-wrapper > div:not(.liquidGlass-box):hover { border-radius: calc(var(--glass-border-radius) + 1em) ;} |
- 悬停时:
- 圆角变大(
+1em)→ 模拟“液体膨胀”或“柔软变形”; - 内边距增大 → 整体“呼吸感”;
- 所有非内容层同步圆角变化,保持视觉一致。
- 圆角变大(
应用到butterfly的例子
下面以card_author为例,即显示作者信息的卡片。
card_author.pug
修改themes/butterfly/layout/includes/widget/card_author.pug
12345678910111213 | 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
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465 | /* 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) ; transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 2.2) ; background: none ; backdrop-filter: none ; -webkit-backdrop-filter: none ; border-style: none ;}.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 ;}/* OTHER STYLES */.liquidGlass-wrapper,.liquidGlass-wrapper > div:not(.liquidGlass-box) { padding: 0.4rem ; border-radius: var(--glass-border-radius) ;}.liquidGlass-wrapper:hover { padding: 0.6rem ; border-radius: calc(var(--glass-border-radius) + 1em) ;}.liquidGlass-wrapper > div:not(.liquidGlass-box):hover { border-radius: calc(var(--glass-border-radius) + 1em) ;} |
inject
主题的_config.yml的 inject 中引入
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061 | 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> |