- 桌面应用
- 音视频
【免费下载链接】Mineradio-paused
一款以电影镜头、粒子视觉和歌词舞台为核心的沉浸式音乐播放器。
导读
本文以 Mineradio 仓库中的 docs/GLASS_SVG_TEXTURE.md 为骨架,系统讲解这款以电影镜头、粒子视觉和歌词舞台为核心的沉浸式音乐播放器如何用 SVG Filter 实现"带色散扭曲的玻璃质感"——而非普通毛玻璃。你将掌握核心 CSS 变量与基线参数、feDisplacementMap三通道 RGB 色差滤镜的构造原理、displacement map 生成器的实现细节、搜索栏与底部控制台"带质感一起出现"的揭示时序,以及 Wallpaper Engine 接入时的单表面采样约束,最后落到一整套可执行的验收与安全修改规则。
一、基线定位:这是一份被"冻结"的视觉契约
GLASS_SVG_TEXTURE.md开篇即说明其性质:这是用户明确要求保存的播放器 SVG 玻璃质感基线。后续可以修补偏移、缺角、套用范围和响应式问题,但不得随意重写核心质感。也就是说,仓库中存在一份"用户验收通过"的视觉基准,所有后续改动都必须以它为约束。
用户已确认当前播放器控制台质感要保存,并计划逐步套用到搜索栏、小按钮、部分面板和 3D 歌单架。同时用户明确不接受以下五种结果,这组"负向验收清单"直接决定了实现的底线:
- 普通毛玻璃,没有扭曲质感;
- 中间糊成一团的毛玻璃;
- 大面积白色渐变扫过去的廉价质感;
- 右侧缺一块、整体右偏、上下错位明显;
- 因套用 SVG 导致性能明显下降。
从仓库证据看,这套质感的落地位置集中在 public/index.html 顶部:root变量区、#bottom-bar样式、<svg id="control-glass-svg">的 filter 定义,以及 public/js/modules/05-playback/15-control-glass-animations.js 中的运行时生成与动画逻辑。
二、核心 CSS 变量:一套变量驱动多处玻璃面
CSS 变量定义在public/index.html顶部的:root中,分为"面板玻璃"与"按钮玻璃"两套,分别对应大面积容器与小尺寸控件:
:root{ --saved-panel-glass-bg:rgba(0,0,0,.10); --saved-panel-glass-filter:blur(12px) saturate(1.8) brightness(1.16); --saved-panel-glass-svg-filter:url(#mineradio-control-glass-filter) saturate(1); --saved-panel-glass-shadow:inset 0 0 2px 1px rgba(255,255,255,.35),inset 0 0 10px 4px rgba(255,255,255,.15),0 4px 16px rgba(17,17,26,.05),0 8px 24px rgba(17,17,26,.05),0 16px 56px rgba(17,17,26,.05),inset 0 4px 16px rgba(17,17,26,.05),inset 0 8px 24px rgba(17,17,26,.05),inset 0 16px 56px rgba(17,17,26,.05); --saved-panel-glass-radius:50px; --saved-button-glass-bg:rgba(0,0,0,.10); --saved-button-glass-filter:blur(12px) saturate(1.8) brightness(1.16); --saved-button-glass-svg-filter:url(#mineradio-control-glass-filter) saturate(1); --saved-button-glass-shadow:inset 0 0 2px 1px rgba(255,255,255,.34),inset 0 0 10px 4px rgba(255,255,255,.13),0 10px 30px rgba(0,0,0,.18); --saved-button-glass-hover-bg:rgba(255,255,255,.055); --saved-button-glass-hover-shadow:inset 0 0 2px 1px rgba(255,255,255,.42),inset 0 0 12px 5px rgba(255,255,255,.17),0 12px 34px rgba(0,0,0,.22),0 0 18px rgba(255,255,255,.06); }这些变量的设计体现了"渐进增强":普通环境使用--saved-panel-glass-filter(blur + saturate + brightness 的常规毛玻璃),而在支持 SVG backdrop-filter 的环境下,html.control-glass-svg-ok类会切换为--saved-panel-glass-svg-filter(引用#mineradio-control-glass-filter并叠加saturate(1))。control-glass-svg-ok这个根类由运行时检测后添加——见supportsControlGlassSvgFilter()的实现(public/js/modules/05-playback/15-control-glass-animations.js),它通过尝试给一个测试元素设置backdrop-filter: url(#mineradio-control-glass-filter)并检查样式是否被保留来判断浏览器是否支持 SVG 引用滤镜,同时主动排除 Safari 与 Firefox。
从 public/css/index.css 可以看出这套变量的实际套用范围很广:html.control-glass-svg-ok下包括.glass-saved-panel、.glass-saved-button、#bottom-bar、#play-btn、搜索相关面板、弹窗、popover、播放列表面板、home 卡片、封面裁剪等几十个选择器都切到了 SVG 滤镜面,而--saved-panel-glass-filter这类降级值则保证不支持 SVG 滤镜时仍有可用的视觉基础。
三、底部播放器控制台:SVG 玻璃的"母版"
3.1 基线 CSS
#bottom-bar是整套质感的基准载体,默认状态下(无 SVG 支持)如下:
#bottom-bar{ border-radius:50px; background:rgba(0,0,0,.10); border:0; backdrop-filter:blur(12px) saturate(1.8) brightness(1.16); -webkit-backdrop-filter:blur(12px) saturate(1.8) brightness(1.16); box-shadow:inset 0 0 2px 1px rgba(255,255,255,.35),inset 0 0 10px 4px rgba(255,255,255,.15),0 4px 16px rgba(17,17,26,.05),0 8px 24px rgba(17,17,26,.05),0 16px 56px rgba(17,17,26,.05),inset 0 4px 16px rgba(17,17,26,.05),inset 0 8px 24px rgba(17,17,26,.05),inset 0 16px 56px rgba(17,17,26,.05); }而在 SVG 可用的环境中,backdrop-filter 整体替换为对 SVG filter 的引用:
html.control-glass-svg-ok #bottom-bar{ background:rgba(0,0,0,.10); backdrop-filter:url(#mineradio-control-glass-filter) saturate(1); -webkit-backdrop-filter:url(#mineradio-control-glass-filter) saturate(1); } #bottom-bar::before{content:none} #bottom-bar::after{content:none}值得注意的细节:::before/::after被强制置为content:none,即不允许在控制台本体上叠加伪元素图层——这是为了避免额外的覆盖层破坏单表面质感,这个约束在搜索栏部分会再次出现。
3.2 SVG Filter 的关键参数
主滤镜#mineradio-control-glass-filter定义于 public/index.html 的<svg id="control-glass-svg">内,其冻结基线参数为:
color-interpolation-filters="sRGB":色彩空间强制 sRGB,保证三通道色差混合符合预期;- 主 filter id:
mineradio-control-glass-filter; - filter 区域:
x="-12%" y="-28%" width="124%" height="156%"——故意向外扩张,给 displacement 位移留出余量,避免玻璃边缘被裁切; - RGB 三通道 displacement scale:Red
180、Green170、Blue160; - RGB 共同对齐中心:
dx="-90" dy="0"; - 三通道分离后使用
screen混合(feBlend mode="screen"),最后feGaussianBlur stdDeviation="0.5"做极轻的柔化。
从实际 filter 结构看(public/index.html),每一通道的处理链是:feImage(displacement map)→feDisplacementMap(按 R/B 通道取样做扭曲,scale 分别为 180/170/160)→feOffset dx="-90"(对齐中心)→ 与SourceGraphic做feMerge→feColorMatrix只保留对应通道颜色 → 最后三段红绿蓝结果两两feBlend mode="screen"合成。
文档对偏移语义做了严格规定:
controlGlassChromaticOffset只能围绕共同中心-90做 Red/Green/Blue 的相对分离,不要再整体平移所有通道;- 该值的 UI 下限为
30,这是保留最低可见色差的底线;默认值90表示保持共同中心-90并增加适中 RGB 边缘分离;最大值140只扩大三通道相对距离,不移动整块玻璃; - 禁止把 RGB 偏移改成正向、禁止删三通道色差、禁止把
stdDeviation拉大。
运行时实现印证了这套语义(public/js/modules/05-playback/15-control-glass-animations.js):
var CONTROL_GLASS_BASE_SHIFT_X = -90; var CONTROL_GLASS_CHROMA_MAX_SPREAD = 22; function applyControlGlassChromaticOffsetToFilter(filter, baseShiftX, maxSpread, verticalFactor) { var chroma = fx.controlGlassChromaticOffset / 140; var spread = maxSpread * chroma; var verticalSpread = spread * (verticalFactor == null ? 0.08 : verticalFactor); setControlGlassChannelOffset(filter, 'dispRedShifted', baseShiftX - spread, -verticalSpread); setControlGlassChannelOffset(filter, 'dispGreenShifted', baseShiftX, 0); setControlGlassChannelOffset(filter, 'dispBlueShifted', baseShiftX + spread, verticalSpread); }即:Green 始终钉在-90中心,Red 向左(-90 - spread)、Blue 向右(-90 + spread),并各带微小的纵向分量(垂直因子 0.08),形成围绕中心的径向色散而非整体平移。normalizeControlGlassChromaticOffset则把任意输入钳制到[30, 140]。
该参数的来源与持久化链路也很完整:
- 默认值在 public/js/modules/00-state/04-fx-defaults.js 中为
controlGlassChromaticOffset: 50; - 持久化层 public/js/modules/02-visual/04-visual-settings-persistence.js 与 public/js/modules/07-fx/00-preset-archive-data.js 均以
layoutNumber(..., 30, 140)钳制读取; - FX 面板滑块
fx-glassaberration直接绑定该值(public/js/modules/07-fx/05-fx-panel-performance.js),改动即触发applyControlGlassChromaticOffset()并同步落盘。
四、Displacement Map 生成器:运行时按尺寸生成扭曲贴图
feDisplacementMap需要一个输入贴图(feImage引用的 data URI SVG)。由于播放器尺寸和圆角会随窗口变化,贴图不能静态写死,必须在运行时按元素实际尺寸生成。核心函数generateControlGlassDisplacementMap(width, height, radius)位于 public/js/modules/05-playback/15-control-glass-animations.js:
function generateControlGlassDisplacementMap(width, height, radius) { width = Math.max(240, Math.round(width || 400)); height = Math.max(48, Math.round(height || 92)); radius = Math.max(12, Math.round(radius || 50)); var borderWidth = 0.07; var edge = Math.min(width, height) * (borderWidth * 0.5); var innerW = Math.max(1, width - edge * 2); var innerH = Math.max(1, height - edge * 2); var svg = '<svg viewBox="0 0 ' + width + ' ' + height + '" xmlns="http://www.w3.org/2000/svg">' + '<defs>' + '<linearGradient id="glass-red" x1="100%" y1="0%" x2="0%" y2="0%"><stop offset="0%" stop-color="#0000"/><stop offset="100%" stop-color="red"/></linearGradient>' + '<linearGradient id="glass-blue" x1="0%" y1="0%" x2="0%" y2="100%"><stop offset="0%" stop-color="#0000"/><stop offset="100%" stop-color="blue"/></linearGradient>' + '</defs>' + '<rect x="0" y="0" width="' + width + '" height="' + height + '" fill="black"/>' + '<rect x="0" y="0" width="' + width + '" height="' + height + '" rx="' + radius + '" fill="url(#glass-red)"/>' + '<rect x="0" y="0" width="' + width + '" height="' + height + '" rx="' + radius + '" fill="url(#glass-blue)" style="mix-blend-mode:difference"/>' + '<rect x="' + edge.toFixed(2) + '" y="' + edge.toFixed(2) + '" width="' + innerW.toFixed(2) + '" height="' + innerH.toFixed(2) + '" rx="' + radius + '" fill="hsl(0 0% 50% / 1)" style="filter:blur(11px)"/>' + '</svg>'; return 'data:image/svg+xml,' + encodeURIComponent(svg); }参数语义与生成原理:
width = Math.max(240, ...)、height = Math.max(48, ...)、radius = Math.max(12, ...):设置最小尺寸与最小圆角下限,避免贴图过小导致扭曲异常;borderWidth = 0.07:边框宽度系数,edge = min(width, height) * 0.035,用于缩进内层矩形,制造玻璃"内沿";- 贴图内容:黑色底 → 红色水平渐变(x 方向从右到左,即 X 通道编码位移量)→ 蓝色垂直渐变(y 方向从上到下,Y 通道编码位移量)以
mix-blend-mode:difference叠加,再用一个带blur(11px)的 50% 灰度内层矩形来压平中心、强化边缘。
在feDisplacementMap中xChannelSelector="R" yChannelSelector="B"(见 public/index.html),因此红渐变驱动水平扭曲、蓝渐变驱动垂直扭曲,中心灰块对应无位移的"平"区域,边缘渐变产生水波般的折射扭曲——这正是"普通毛玻璃没有的扭曲质感"的来源。
生成与缓存逻辑在updateGlassDisplacementMapForElement(public/js/modules/05-playback/15-control-glass-animations.js):读取元素getBoundingClientRect()与borderRadius,组成宽x高:圆角的缓存 key;只有 key 变化才重新生成 data URI 并写入<img id="control-glass-map">的href(同时写xlink:href兼容旧环境)。ResizeObserver与resize事件监听保证窗口变化后贴图随之更新。
除主控制台外,仓库还针对小尺寸控件生成了专用贴图,避免共享大尺寸 map 造成的边缘裁切:
generateSearchBoxGlassDisplacementMap与generateSearchPillGlassDisplacementMap直接复用控制台生成器(同文件 L105-L110),但各自拥有独立的<feImage>与缓存 key;generateAccountPillGlassDisplacementMap(同文件 L79-L104)为顶部账号 VIP 胶囊生成专用贴图,并配套独立的#mineradio-account-pill-glass-filter(scale 28、stdDeviation 0.16 的轻折射滤镜)。
五、搜索栏揭示时序:让 SVG 质感"随面板一起出现"
这是文档中最精细的工程约束。核心要求是:搜索栏必须像底部播放器控制台一样"带着 SVG 质感一起出现",不能先显示透明玻璃框,再等动画结束才加载色散——否则会出现首帧无质感的闪烁,破坏观感。
5.1 时序协议:priming → ready / fallback
运行时以三个根类状态机管理搜索栏玻璃的预热:
search-glass-priming:正在等待 displacement map 生效(预热帧阶段);search-glass-ready:map 已就绪,可以带 SVG 质感揭示;search-glass-fallback:SVG 不可用或生成失败,走降级路径。
对应的实现见 public/js/modules/05-playback/15-control-glass-animations.js:setSearchGlassPriming/setSearchGlassReady/setSearchGlassFallback三者在<html>与#search-area上同步切换类;queueSearchGlassReadyAfterPaint在search-box-glass-map有href后等待 3 帧(requestAnimationFrame计数)再切到 ready,确保 SVG 纹理真正上屏后才允许面板揭示。
5.2 与 peek 面板系统的协作
搜索面板的弹出由setPeek(el, on, key)驱动(public/js/modules/10-shell/02-peek-panels-upload.js)。对key === 'search'且面板未展开时,会先调用prepareSearchGlassBeforePeek()预热;若返回 false(尚未 ready),则走scheduleSearchPeekAfterGlassReady:通过requestAnimationFrame轮询isSearchGlassReadyForReveal()(接受 ready 或 fallback),最多兜底 140ms(同文件 L46-L63),超时后也必须加peek,保证不因玻璃加载而卡死交互。
prepareSearchGlassBeforePeek()的完整流程(public/js/modules/05-playback/15-control-glass-animations.js):
function prepareSearchGlassBeforePeek() { if (!document.documentElement.classList.contains('control-glass-svg-ok')) return true; setSearchGlassFallback(false); var ready = updateSearchBoxGlassDisplacementMap(); updateSearchPillGlassDisplacementMap(); if (!ready || !glassImageHasHref(document.getElementById('search-box-glass-map'))) { setSearchGlassPriming(false); setSearchGlassFallback(true); return true; } if (document.documentElement.classList.contains('search-glass-ready')) return true; return syncSearchGlassReadyState(true, false); }启动时(initControlGlassSurface,同文件 L377-L434)会同步执行一次prepareSearchGlassBeforePeek(),再用requestAnimationFrame与setTimeout(..., 140)双保险预热,并在 ResizeObserver 回调中持续刷新 search box / search pill 贴图。
5.3 表面规则:禁止伪元素叠加
文档明确了两条表面规则:
- 隐藏的
#search-box保持透明(backdrop-filter:none);只有可见的#search-area.peek #search-box才携带已保存的面板背景/阴影,并在 SVG 可用时直接引用url(#mineradio-search-box-glass-filter) saturate(1); - 禁止把搜索框 SVG 玻璃放在
#search-box::before上——那会与揭示状态分离成独立图层,让弹层看起来像"SVG 迟到了"。
这条规则在 quick-check 中有对应的硬性断言(scripts/quick-check.js 的Search glass entrance guard):校验#search-box::before { content:none !important }、校验.peek揭示时背景/阴影/滤镜必须来自--saved-panel-glass-*变量、校验 SVG 滤镜必须直接作用于#search-area.peek #search-box而非伪元素,同时校验.search-mode-tabs容器保持透明(background:transparent !important、backdrop-filter:none !important),SVG 质感只落在每个 tab 胶囊按钮上(url(#mineradio-search-pill-glass-filter) saturate(1) !important)。此外搜索历史/结果列表在无 SVG 时使用blur(34px) saturate(1.34) brightness(1.08)的线性渐变毛玻璃降级,且不得退回rgba(0,0,0,.90)的黑底。
六、Wallpaper Engine 单表面规则:拒绝"双重玻璃"
当 Mineradio 以壁纸形态运行(Wallpaper Engine)时,文档记录了一个 2026-07-18 的用户决策:明确否定了额外的"可直接透明"玻璃层。旧方案Mineradio WE Glass Refraction、第二个圆角 DWM 面、DwmQueryThumbnailSourceSize局部取样与1.105放大/错位方案全部失效并禁止恢复。
当前唯一允许的实现如下:
- 原生层只有一个全窗
Mineradio WE DWM Surface;精确 WE 源窗仍在它下面,保持真实 Windows 鼠标视差; #wallpaper-engine-glass-sampler只裁切真实#bottom-bar;内部视频保持完整视口 1:1 对齐,不做缩放镜片或二次折射;- 采样源 HWND 必须与唯一基础 DWM HWND 相同。运行时兼容字段
dwmGlassSurfaceWindowId只是该 HWND 的别名,不代表第二个窗口; - 采样流必须在 DWM 缩略图激活前对普通基础 HWND 建立;捕获首帧后再激活同一 HWND 的 DWM 缩略图,并在预热黑帧变成真实壁纸像素后才添加
wallpaper-engine-glass-sampler-ready; - 最终扭曲和 RGB 色散只由既有
#mineradio-control-glass-filter产生。Red180、Green170、Blue160、共同中心-90、screen混合、stdDeviation=0.5等冻结参数不因 WE 接入改变; - 采样 HWND 本身没有光标画面;不得用读取/隐藏/替换系统光标或 DOM 假光标处理捕获。Scene 停止、切换、隐藏或退出时必须停止该 MediaStream。
源码侧由syncWallpaperEngineControlGlassSurface与syncWallpaperEngineGlassSamplerGeometry落实(public/js/modules/05-playback/15-control-glass-animations.js):采样器按#bottom-bar的真实矩形(含圆角半径)定位,视频元素以负偏移覆盖整个视口实现 1:1 裁切;几何数据通过updateWallpaperEngineGlassSurface送入原生层,且带 30ms 节流与dwmGeometryKey变更检测,最大化/还原时由animateWallpaperEngineControlGlassSurface(520)做 520ms 动画跟随。quick-check 中也有对应断言(scripts/quick-check.js):要求采样器存在、cursor-free、1:1 对齐并"通过既有 SVG 控制台玻璃"输出。
文档给出的验收要点:
Mineradio WE Glass Refraction窗口数为0;- 基础 HWND 与采样源 HWND 相等;
- 采样视频轨为
live且无音频; - 采样器矩形与控制台矩形一致;
- 最大化/还原后仍对齐。
七、Safe Change Rules:什么能改、什么不能改
文档将改动分为三档,这是维护这套视觉系统最重要的边界:
可以改:
- 修复右侧缺块、尺寸缓存、ResizeObserver 更新、特定面板的 SVG map 尺寸;
- 给搜索栏、小按钮等新增独立 filter/map,避免共享同一个大尺寸 map(仓库中 search-box、search-pill、account-pill 三套独立 filter/map 正是这一策略的落地)。
谨慎改:
- 调整 filter 区域、offset、scale。改前必须截图对比,防止边缘裁切或色差过度。
禁止改:
- 把
controlGlassChromaticOffset改回同时设置所有feOffset dx的整体左右偏移; - 整体替换为普通
blur()毛玻璃; - 为了性能删除色差或扭曲质感——性能问题必须通过节流、缓存、减少实时重算来解决,而不是删效果。
从实现看,"减少实时重算"确实有据可依:贴图按宽x高:圆角缓存 key 命中即跳过重生成(updateGlassDisplacementMapForElement);DWM 几何上报带 30ms 节流与 key 比对;所有尺寸变更都包在requestAnimationFrame中,避免在布局抖动周期内重复计算。
八、验收清单:黑底与亮底双重检查
文档要求最终验收时在黑底和亮底背景下各看一遍,检查点如下:
- 控制台右侧没有缺块;
- 搜索栏没有右侧缺失、整体右偏或白色廉价渐变;
- 播放器中间不是糊的,仍有水波扭曲和 RGB 色差;
- 鼠标 hover、播放暂停、切歌后没有明显卡顿。
这四条与开篇的"用户不接受"清单一一对应(缺块/右偏对应右侧缺陷,白色渐变对应廉价质感,中间糊对应无扭曲,卡顿对应性能问题),形成闭环。仓库中的 scripts/quick-check.js 提供自动化守护(如Search glass entrance guard、控制台玻璃源切换器 guard、账号胶囊专用 map guard 等),把其中大部分结构性约束固化为可重复执行的断言,防止后续改动无意中破坏基线。
九、小结与扩展阅读
Mineradio 的 SVG 玻璃质感是一套"冻结基线 + 渐进增强 + 状态机预热 + 严格边界"的完整工程实践:CSS 变量保证降级可用,SVG Filter 提供 RGB 三通道色散扭曲,displacement map 按元素尺寸运行时生成并缓存,搜索栏通过 priming/ready/fallback 状态机确保"质感与面板同帧出现",Wallpaper Engine 场景则强制单 DWM 表面 + 1:1 采样,避免二次玻璃叠加。对想复现或改造类似"扭曲玻璃"效果的开发者而言,本文覆盖了从视觉参数、滤镜结构、生成器代码到验收标准的全链路。
进一步阅读建议:
- 基线文档:docs/GLASS_SVG_TEXTURE.md
- 滤镜定义与 SVG 容器:public/index.html
- 运行时生成、色差应用与预热状态机:public/js/modules/05-playback/15-control-glass-animations.js
- 搜索栏 peek 揭示与 140ms 兜底:public/js/modules/10-shell/02-peek-panels-upload.js
- 色差默认值与持久化钳制:public/js/modules/00-state/04-fx-defaults.js、public/js/modules/02-visual/04-visual-settings-persistence.js
- 自动化验收断言:scripts/quick-check.js
- 桌面应用
- 音视频
【免费下载链接】Mineradio-paused
一款以电影镜头、粒子视觉和歌词舞台为核心的沉浸式音乐播放器。
相关推荐
从零打造玻璃质感:Three.js材质继承系统完全指南
从零打造玻璃质感:Three.js材质继承系统完全指南 Three.js作为JavaScript 3D库的领军者,其材质系统采用面向对象的继承架构,让开发者能够
前端3D渲染图形学Bug Fix Document — AutoResearchClaw Pipeline
Bug Fix Document — AutoResearchClaw Pipeline 生成日期:YYYY MM DD 反馈来源:N 位测试者 总计问题:N
人工智能大模型AI Agent自主智能体深度研究科研MCP 服务后端玻璃拟态视觉风格全解:在 ppt-master 中用半透明玻璃面板、渐变光感与漂浮深度构建现代 SaaS 演示
玻璃拟态视觉风格全解:在 ppt master 中用半透明玻璃面板、渐变光感与漂浮深度构建现代 SaaS 演示 本文以 skills/ppt master/re
AI 技能人工智能AI 应用
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考