Shader <mv-shader>
为你自己的片段着色器打造的全画幅 WebGL2 引擎:内联 GLSL、粘贴的完整着色器或单通道 mainImage() 都在元素内容的后方运行,并提供指针、滚动和主题令牌的 uniform。
工作原理
为你自己的片段着色器打造的全画幅 WebGL2 引擎:内联 GLSL、粘贴的完整着色器或单通道 mainImage() 都在元素内容的后方运行,并提供指针、滚动和主题令牌的 uniform。离开屏幕时暂停,与页面共享 WebGL 上下文配额,减少动态效果时保持静态画面,没有 WebGL2 时回退为令牌草图。没有 GLSL 时,它会用主题颜色绘制自己的漂移等高线图。
| 分类 | 背景 |
|---|---|
| 类型 | Web Component(<mv-shader>) |
| 状态 | 稳定版 |
| Keywords | safe-rewrite, webgl, glsl, shader, background, runtime, contour |
When to use
- A custom fragment shader must run as a section background with off-screen pause and reduced motion built in
- A reusable branded background should be built by subclassing with its own uniforms and GLSL
- An existing GLSL effect needs to be ported without adding a WebGL library
- A quiet, theme-aware contour texture is wanted behind a hero or a feature band
Avoid when
- A ready-made preset already matches the look, e.g. a fluid brand gradient → use Mesh Gradient instead
- Nobody on the team maintains GLSL; a CSS background is easier to own → use Aurora Background instead
- More than 10 shaders are on screen at once; only 10 WebGL contexts stay live, so the extra ones keep being rebuilt
安装
node scripts/add.mjs shader --out ./src/marvelous使用 Marvelous UI MCP 服务器的 AI 智能体:install_components({ slugs: ["shader"], target_dir: "<absolute path>/src/marvelous", framework: "react" })。
复制的文件(含依赖):tokens/tokens.css, core/base.css, core/canvas.js, core/dom.js, core/element.js, core/i18n.js, core/motion.js, core/observe.js, core/webgl.js, components/shader/shader.js, components/shader/shader.css。
用法
标准标记,可在此基础上通过属性、data-* 和 CSS 变量进行定制:
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));width:100%;height:100%;min-height:320px">
<mv-shader style="min-height:320px;display:grid;align-content:end;padding:1.25rem;color:var(--mv-fg)">
<p style="margin:0;width:fit-content;padding:.6rem .85rem;border-radius:var(--mv-radius-md);background:color-mix(in oklch,var(--mv-bg) 82%,transparent);backdrop-filter:blur(6px);font:600 1.05rem/1.35 var(--mv-font-sans)">Built-in default<br><span style="font-weight:400;font-size:.85rem;color:var(--mv-fg-muted)">Contour map in your theme tokens. Hover to raise it.</span></p>
</mv-shader>
<mv-shader style="min-height:320px;display:grid;align-content:end;padding:1.25rem;color:var(--mv-fg)">
<script type="x-shader/x-fragment">
// Your GLSL ES 3.0: u_time, u_resolution, u_dpr, u_mouse, u_hover and u_palette are provided.
void main() {
vec3 ink = u_palette[0];
vec3 paper = u_palette[1];
float cell = 14.0 * u_dpr;
vec2 f = fract(gl_FragCoord.xy / cell) - 0.5;
vec2 c = (floor(gl_FragCoord.xy / cell) + 0.5) * cell / u_resolution.y;
vec2 m = u_mouse * u_resolution / u_resolution.y;
float wave = 0.5 + 0.3 * sin(c.x * 9.0 + c.y * 5.0 - u_time * 1.2) + 0.2 * sin(c.y * 13.0 - c.x * 4.0 + u_time * 0.8);
wave += 0.6 * u_hover * exp(-dot(c - m, c - m) * 16.0);
float calm = smoothstep(0.05, 0.55, gl_FragCoord.y / u_resolution.y);
float r = 0.05 + 0.37 * clamp(wave, 0.0, 1.0) * calm;
float dot_ = 1.0 - smoothstep(r - 1.0 / cell, r + 1.0 / cell, length(f));
fragColor = vec4(mix(paper, ink, dot_ * 0.85), 1.0);
}
</script>
<p style="margin:0;width:fit-content;padding:.6rem .85rem;border-radius:var(--mv-radius-md);background:color-mix(in oklch,var(--mv-bg) 82%,transparent);backdrop-filter:blur(6px);font:600 1.05rem/1.35 var(--mv-font-sans)">Your own GLSL<br><span style="font-weight:400;font-size:.85rem;color:var(--mv-fg-muted)">Inline halftone, same pause and theme handling.</span></p>
</mv-shader>
</div>API
Attributes
| Name | 类型 | Default | Description |
|---|---|---|---|
colors | CSS colors (3) | var(--mv-accent), var(--mv-bg), var(--mv-fg) | Palette of <mv-shader> itself, as u_palette[3] (ink, paper, text): drawn by the built-in contour map and available to inline GLSL. Tokens accepted, read again on theme change. |
speed | number | 1 | Time multiplier. |
paused | boolean | Freezes the animation on the current frame. | |
interactive | boolean | Follows the pointer only over the element (otherwise across the whole window). | |
dpr | number | 1.5 | Max pixel density (capped at 2). |
data-fill | boolean | Fills the positioned parent (absolute, inset 0). |
Properties
| Name | 类型 | Description |
|---|---|---|
fragment | string | null | The GLSL that runs. Setting it recompiles on the next frame; null goes back to the inline script or the built-in contour map. |
static fragment / uniforms / chunks | To build a reusable background: class MvFoo extends MvShader { static chunks = ["noise"]; static uniforms = { u_colors: { attr: "colors", type: "colors", default: "var(--mv-accent), #0ea5e9", count: 3 } }; static fragment = void main() { … } }. Uniform types: number, color, colors, vec2, vec3, vec4, boolean. GLSL chunks: hash (hash12, hash22), noise (snoise, fbm), color (palette, gradient3). |
Events
| Name | Description |
|---|---|
mv-error | The GLSL failed to compile or link. detail: { message } (the compiler log). The element gets data-error and shows its fallback. |
Content structure
| Name | Description |
|---|---|
<script type="x-shader/x-fragment"> | GLSL ES 3.0: a main() body, a complete shader, or a single-pass mainImage() (iTime, iResolution, iMouse mapped; no iChannel). Provided uniforms: u_time, u_resolution, u_dpr, u_mouse, u_hover, u_scroll, u_palette. data-chunks="noise color" adds GLSL chunks. |
children | Content placed inside the element sits above the canvas. |
Accessibility
Decorative: the canvas is aria-hidden and never takes pointer events; content placed inside stays in the normal reading order. The loop stops off screen and in hidden tabs, and no context is created until the element comes near the viewport. Reduced motion (OS or <html data-motion="reduce">) and paused keep a still, representative frame. Without WebGL2, or when the GLSL does not compile, a static contour sketch in the theme tokens is shown; forced colors and print hide the canvas. Text placed over a custom shader needs its own contrast check.