Ripple Grid <mv-ripple-grid>
WebGL 背景:透视视角下地面上的一张制图网格,仿佛覆盖着一层薄薄的水;圆环以稳定的节奏从原点出发,弯曲所经过的线条,用强调色为其着色并闪出高光。
工作原理
WebGL 背景:透视视角下地面上的一张制图网格,仿佛覆盖着一层薄薄的水;圆环以稳定的节奏从原点出发,弯曲所经过的线条,用强调色为其着色并闪出高光。指针带着一个放大镜,点击会激起一道强波纹,移动的指针会留下柔和的尾迹。颜色来自主题令牌,因此在浅色和深色主题下都适用。
| 分类 | 背景 |
|---|---|
| 类型 | WebGL 元素(<mv-ripple-grid>) |
| 状态 | 稳定版 |
| 同时安装 | shader |
| Keywords | safe-rewrite, webgl, shader, grid, ripple, water, perspective, interactive, hero |
When to use
- A developer tool, API or SaaS hero wants a technical grid with a calm pulse behind the headline, in light or dark themes
- Clicks and pointer moves should send ripples across the backdrop as playful feedback
- A ripple must be triggered from code, e.g. on a successful submit or a milestone
Avoid when
- A static or CSS-only grid is enough, or a retro scrolling floor is wanted → use Grid Pattern instead
- Flowing wave lines rolling in from a horizon fit better than a ruled grid → use Waves instead
- A rotating sweep with blips suits a monitoring theme better → use Radar instead
安装
node scripts/add.mjs ripple-grid --out ./src/marvelous使用 Marvelous UI MCP 服务器的 AI 智能体:install_components({ slugs: ["ripple-grid"], 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, components/ripple-grid/ripple-grid.js, components/ripple-grid/ripple-grid.css。
用法
标准标记,可在此基础上通过属性、data-* 和 CSS 变量进行定制:
<div style="display:flex;flex-wrap:wrap;min-height:320px">
<section style="flex:1.6 1 22rem;position:relative;display:grid;place-items:center;text-align:center;overflow:hidden;padding:2rem 1.5rem">
<mv-ripple-grid data-fill></mv-ripple-grid>
<div style="position:relative">
<p style="margin:0;font:700 clamp(1.6rem,4vw,2.6rem)/1.1 var(--mv-font-sans);letter-spacing:-.03em">Ship on a steady beat</p>
<p style="margin:.6rem auto 1.2rem;max-width:30rem;color:var(--mv-fg-muted)">Rings roll across the grid. Click anywhere to drop your own.</p>
<a class="mv-button" href="#ripple-grid">Start building</a>
</div>
</section>
<section style="flex:1 1 14rem;position:relative;display:grid;place-items:center;text-align:center;overflow:hidden;border-left:1px solid var(--mv-border);min-height:220px;padding:1.5rem">
<mv-ripple-grid data-fill perspective="0" cells="9" origin="0.2 0.3" interval="3"></mv-ripple-grid>
<div style="position:relative">
<strong style="display:block;font-size:1.1rem">Flat grid</strong>
<span style="font-size:.85rem;color:var(--mv-fg-muted)">perspective="0" origin="0.2 0.3"</span>
</div>
</section>
</div>API
Attributes
| Name | 类型 | Default | Description |
|---|---|---|---|
color | CSS color | var(--mv-accent) | Accent of the rings, glints and pointer lens (tokens accepted, read again on theme change). |
background | CSS color | var(--mv-bg) | Paper color under the grid (tokens accepted). |
line-color | CSS color | var(--mv-fg) | Ink of the grid lines, drawn at low opacity over the paper (tokens accepted). |
cells | number | 10 | Number of cells along the height, at the middle of the frame. |
major | number | 4 | A stronger line, with crosses where two meet, every N cells (0 or 1 removes them). |
amplitude | number | 1 | How much the rings bend the lines and how bright their glint is (0 keeps only the tint). |
line | number | 1 | Line thickness multiplier. |
perspective | number | 0.5 | Tilt of the floor toward the back (0 = flat grid seen from above, up to 0.9). |
interval | number | 2.4 | Seconds between two rings leaving the origin (0.4 minimum). |
origin | x y | 0.5 0.42 | Where the beat starts, in 0..1 element coordinates (y pointing up). |
trail | number | 1 | Strength of the soft wake left by a moving mouse or pen, 0 to 2 (0 turns it off; clicks still drop ripples). |
speed | number | 1 | Time multiplier. |
paused | boolean | Freezes the animation on the current frame. | |
interactive | boolean | Lens and ripples follow the pointer only over the element itself (otherwise anywhere inside its area, even over content placed above it). | |
dpr | number | 1.5 | Max pixel density (capped at 2). |
data-fill | boolean | Fills the positioned parent (absolute, inset 0). |
Methods
| Name | Description |
|---|---|
ripple(x, y, strength = 1) | Drops a ripple at (x, y), in 0..1 element coordinates (y pointing up); strength from 0 to 2. Ignored while paused or under reduced motion. |
Events
| Name | Description |
|---|---|
mv-error | Inherited from <mv-shader>: the GLSL failed to compile. The element shows its still fallback grid. |
Content structure
| Name | Description |
|---|---|
children | Content placed inside the element sits above the grid. |
Accessibility
Decorative: the canvas is aria-hidden and never takes pointer events, and ripples come from window pointer events, so clicks on content above the grid still work. The loop stops off screen and in hidden tabs, and no WebGL context is created until the element nears the viewport. Reduced motion (OS or <html data-motion="reduce">) and paused keep a still frame with a few rings, and no ripple is emitted. Without WebGL2 a still CSS grid with rings is shown; forced colors and print hide the canvas. Rings pass behind the content: keep headline text in the theme foreground, which the faint grid leaves readable.