title: 丝绸背景
description: 一个由着色器驱动的柔和流动背景，灵感来自丝绸布料的运动与质感，使用 ShaderToy 渲染。
navBadges:
  - value: 新
    type: lime
---

```vue
<template>
  <div class="relative min-h-96 w-full">
    <SilkBackground />
  </div>
</template>

```

## API 接口

| 属性名       | 类型     | 默认值 | 描述                                              |
| ------------ | -------- | ------ | ------------------------------------------------- |
| `hue`        | `number` | `300`  | 丝绸纹理的基础色调（单位为度，范围 0–360）。      |
| `saturation` | `number` | `0.5`  | 颜色的饱和度（范围 0–1）。                        |
| `brightness` | `number` | `1`    | 输出颜色的亮度倍增因子（推荐范围 0–2）。          |
| `speed`      | `number` | `1`    | 控制动画速度的倍数（例如 `2` 表示两倍速）。       |
| `class`      | `string` | `—`    | 可选，容器 div 的额外 CSS 类（例如 z-index 等）。 |

> 💡 此组件默认使用全屏绝对定位容器。你可以通过 `class` 属性自定义位置或层级。

## 使用 CLI 安装

```vue
<InstallationCli component-id="bg-silk" />
```

## 手动安装

```vue
<template>
  <div :class="cn('absolute inset-0', props.class)">
    <ShaderToy
      :shader-code="shaderCode"
      :hue="props.hue"
      :saturation="props.saturation"
      :brightness="props.brightness"
      :speed="props.speed"
    />
  </div>
</template>

<script lang="ts" setup>
import type { HTMLAttributes } from "vue";
import { cn } from "~/lib/utils";

interface Props {
  class?: HTMLAttributes["class"];
  hue?: number;
  saturation?: number;
  brightness?: number;
  speed?: number;
}

const props = withDefaults(defineProps<Props>(), {
  hue: 300,
  saturation: 0.5,
  brightness: 1,
  speed: 1,
});

const shaderCode = `
// ShaderToy URL: https://www.shadertoy.com/view/X3yXRd
// The MIT License
// Copyright © 2024 Giorgi Azmaipharashvili
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#define INVERT 1

float noise(vec2 p) {
    return smoothstep(-0.5, 0.9, sin((p.x - p.y) * 555.0) * sin(p.y * 1444.0)) - 0.4;
}

float fabric(vec2 p) {
    const mat2 m = mat2(1.6, 1.2, -1.2, 1.6);
    float f = 0.4 * noise(p);
    f += 0.3 * noise(p = m * p);
    f += 0.2 * noise(p = m * p);
    return f + 0.1 * noise(m * p);
}

float silk(vec2 uv, float t) {
    float s = sin(5.0 * (uv.x + uv.y + cos(2.0 * uv.x + 5.0 * uv.y)) + sin(12.0 * (uv.x + uv.y)) - t);
    s = 0.7 + 0.3 * (s * s * 0.5 + s);
    s *= 0.9 + 0.6 * fabric(uv * min(iResolution.x, iResolution.y) * 0.0006);
    return s * 0.9 + 0.1;
}

float silkd(vec2 uv, float t) {
    float xy = uv.x + uv.y;
    float d = (5.0 * (1.0 - 2.0 * sin(2.0 * uv.x + 5.0 * uv.y)) + 12.0 * cos(12.0 * xy)) * cos(5.0 * (cos(2.0 * uv.x + 5.0 * uv.y) + xy) + sin(12.0 * xy) - t);
    return 0.005 * d * (sign(d) + 3.0);
}

void mainImage(out vec4 fragColor, vec2 fragCoord) {
    float mr = min(iResolution.x, iResolution.y);
    vec2 uv = fragCoord / mr;

    float t = iTime;
    uv.y += 0.03 * sin(8.0 * uv.x - t);

    if (iMouse.z > 1.0)
        uv += smoothstep(0.5, 0.0, distance(iMouse.xy / mr, uv)) * 0.08;
    // uv -= normalize(iMouse.xy / iResolution.xy * 2.0 - 1.0) * t * 0.05; // Wind, requires state

    float s = sqrt(silk(uv, t));
    float d = silkd(uv, t);

    vec3 c = vec3(s);
    c += 0.7 * vec3(1, 0.83, 0.6) * d;
    c *= 1.0 - max(0.0, 0.8 * d);
#if INVERT
    c = pow(c, 0.3 / vec3(0.52, 0.5, 0.4));
    c = 1.0 - c;
#else
    c = pow(c, vec3(0.52, 0.5, 0.4));
#endif

    fragColor = vec4(c, 1);
}
`;
</script>

```

## 特性

- **着色器驱动的丝绸效果**：基于片段着色器，使用递归三角函数和噪声模拟柔软丝绸的运动。
- **实时动画**：在 GPU 上运行，具有流畅的过渡和自然波动。
- **交互响应**：当鼠标光标活动时，具有微妙的视差和扭曲效果。
- **自定义颜色控制**：通过属性调整色调、饱和度、亮度和动画速度。
- **轻量高性能**：优化的 GLSL 逻辑，适用于现代 GPU 和浏览器。
- **模块化设计**：将 GLSL 着色器嵌入 `ShaderToy` 包装组件中，便于复用。

## 注意事项

- 改编自 [这个 ShaderToy 着色器](https://www.shadertoy.com/view/X3yXRd)，由 Giorgi Azmaipharashvili 创作（MIT 许可证）。
- 主要用于装饰性用途，例如全屏英雄背景或分割区块。
- 着色器结合了正弦波函数、噪声层和基于距离的扭曲。
- 由于对 GPU 要求较高，不推荐在低端设备上使用。

## 致谢

- 嵌入到 Vue 组件中，并根据传入属性实现动态输入。
- 灵感来自丝绸质感与有机材质中的流动运动模式。