mcpField
W
web-wave-designer

web-wave-designer

github.com/curiositech设计与 UI257202 次安装2 天前更新
代码仓库

关于 web-wave-designer

web-wave-designer 是面向 Web 开发者的水面视觉效果设计技能,涵盖 SVG 滤镜、CSS 动画与分层实现。它提供从海浪、涟漪到水下折射的制作指导,并强调渲染性能。

主要功能

  1. 01SVG 湍流与位移滤镜
  2. 02海浪和涟漪效果方案
  3. 03水下折射与泡沫效果
  4. 04CSS 与 JavaScript 动画
  5. 05分层设计和性能优化

适用场景

  1. 01制作海洋背景与海岸浪花
  2. 02为内容添加水下扭曲效果
  3. 03设计池塘涟漪或液态玻璃界面

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/curiositech/some_claude_skills --skill web-wave-designer

安装后在 Claude Code 中输入/web-wave-designer确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

CHANGELOG.md
.claude-plugin/
plugin.json
references/
animation-patterns.md
displacement-and-color.md
turbulence-deep-dive.md

详细介绍

Web 波浪设计师

擅长使用 SVG 滤镜、CSS 动画和分层技术,为 Web 应用创建逼真且高性能的海洋和水波效果。专注于水生视觉效果,从轻柔的涟漪到壮观的海浪,尤其精通光线在水中的折射物理效果。

何时使用此技能

适用于:

  • 海洋波浪背景和海景
  • 水下扭曲/折射效果
  • 带泡沫的海滩岸边波浪
  • 池塘/泳池涟漪动画
  • 液态玻璃 UI 效果
  • 水主题加载状态
  • 具有纵深感的视差海洋图层
  • 游戏中的风格化/卡通水面
  • 水面的倒影效果

不适用于:

  • 3D 体积海洋渲染 -> 使用 WebGL/Three.js/Ocean.js
  • 实时流体模拟 -> 使用 canvas physics engines
  • 视频效果 -> 使用视频编辑软件
  • 没有动态效果的简单蓝色渐变
  • 水滴物理效果 -> 使用粒子系统

核心区别:turbulence 与 fractalNoise

重要: 对于水效果,请使用 type="turbulence"(不要像处理云朵那样使用 fractalNoise):

类型视觉效果最适用场景
turbulence连续流动的图案,从透明黑色开始水、水波、液体
fractalNoise随机云状斑块,不透明云、烟雾、地形

<feTurbulence type="turbulence" baseFrequency="0.01 0.1" />


<feTurbulence type="fractalNoise" baseFrequency="0.01" />

水效果的 SVG 滤镜流程

基础水效果滤镜链:

Source -> feTurbulence -> feDisplacementMap -> feComponentTransfer -> feComposite
            (waves)        (distortion)         (color/opacity)      (blend)
1. feTurbulence - 波浪图案生成
<feTurbulence
  type="turbulence"           
  baseFrequency="0.01 0.1"    
  numOctaves="3"              
  seed="42"                   
  result="waves"
/>

baseFrequency 说明(两个值):

X 频率Y 频率效果
0.010.1水平长波,伴有垂直振荡
0.0050.05深海涌浪
0.020.15汹涌的表层波浪
0.030.03方形涟漪(池塘)

比值很重要:

  • X 远小于 Y:拉伸的水平波浪(海洋)
  • X == Y:圆形涟漪(池塘、泳池)
  • X 远大于 Y:垂直条纹(瀑布)
2. feDisplacementMap - 折射效果

创建弯曲/扭曲效果,让水面后方的内容呈现波纹状。

<feDisplacementMap
  in="SourceGraphic"          
  in2="waves"                 
  scale="20"                  
  xChannelSelector="R"        
  yChannelSelector="G"        
  result="refracted"
/>
Scale 值效果
10-15轻柔的泳池涟漪
15-25标准水面折射
25-40强烈的波浪扭曲
40+迷幻效果(不真实)
3. feComponentTransfer - 水的颜色

通过调整通道,将噪声转换为类似水的颜色。

<feComponentTransfer in="waves" result="waterColor">
  <feFuncR type="linear" slope="0.3" intercept="0"/>     
  <feFuncG type="linear" slope="0.7" intercept="0.2"/>   
  <feFuncB type="linear" slope="1.2" intercept="0.3"/>   
  <feFuncA type="linear" slope="0.6" intercept="0.3"/>   
</feComponentTransfer>
4. feGaussianBlur - 焦散(水下光线)
<feGaussianBlur
  in="waves"
  stdDeviation="2"            
  result="caustics"
/>
5. 合成 - 图层组装
<feFlood flood-color="#0077be" flood-opacity="0.4" result="baseWater"/>
<feBlend in="caustics" in2="baseWater" mode="screen" result="waterLayer"/>
<feComposite in="waterLayer" in2="SourceGraphic" operator="over"/>

波浪类型方案

海面波浪
<svg width="100%" height="300">
  <defs>
    <filter id="oceanWaves" x="0" y="0" width="100%" height="100%">
      <feTurbulence type="turbulence"
                    baseFrequency="0.005 0.05"
                    numOctaves="4"
                    seed="1"
                    result="waves">
        <animate attributeName="baseFrequency"
                 dur="60s"
                 values="0.005 0.05;0.007 0.06;0.005 0.05"
                 repeatCount="indefinite"/>
      </feTurbulence>
      <feDisplacementMap in="SourceGraphic" in2="waves" scale="25"/>
    </filter>
  </defs>
  <rect width="100%" height="100%" fill="url(#oceanGradient)" filter="url(#oceanWaves)"/>
</svg>
池塘涟漪(圆形)
<filter id="pondRipples">
  <feTurbulence type="turbulence"
                baseFrequency="0.02 0.02"  
                numOctaves="2"
                seed="10"
                result="ripples"/>
  <feDisplacementMap in="SourceGraphic" in2="ripples" scale="12"/>
</filter>
海滩岸边波浪(破碎浪)
<filter id="shoreWaves">
  <feTurbulence type="turbulence"
                baseFrequency="0.008 0.12"  
                numOctaves="3"
                seed="5"
                result="waves"/>
  <feGaussianBlur in="waves" stdDeviation="1.5" result="softWaves"/>
  <feDisplacementMap in="SourceGraphic" in2="softWaves" scale="30"/>
  
  <feTurbulence type="fractalNoise"
                baseFrequency="0.03"
                numOctaves="5"
                result="foam"/>
  <feColorMatrix in="foam" type="matrix"
                 values="1 0 0 0 0.9
                         1 0 0 0 0.95
                         1 0 0 0 1
                         0 0 0 0.3 0"/>
</filter>
水下扭曲(透过水面观察)
<filter id="underwater" x="-10%" y="-10%" width="120%" height="120%">
  <feTurbulence type="turbulence"
                baseFrequency="0.015 0.08"
                numOctaves="3"
                result="distort">
    <animate attributeName="baseFrequency"
             dur="8s"
             values="0.015 0.08;0.018 0.09;0.015 0.08"
             repeatCount="indefinite"/>
  </feTurbulence>
  <feDisplacementMap in="SourceGraphic" in2="distort"
                     scale="20"
                     xChannelSelector="R"
                     yChannelSelector="B"/>
  
  <feColorMatrix type="matrix"
                 values="0.9 0 0 0 0
                         0 0.95 0 0 0.02
                         0 0 1.1 0 0.05
                         0 0 0 1 0"/>
</filter>
液态玻璃效果(现代 UI)
<filter id="liquidGlass" x="-5%" y="-5%" width="110%" height="110%">
  <feTurbulence type="turbulence"
                baseFrequency="0.01 0.05"
                numOctaves="2"
                seed="99"
                result="ripple">
    <animate attributeName="seed"
             dur="4s"
             values="99;100;101;100;99"
             repeatCount="indefinite"/>
  </feTurbulence>
  <feDisplacementMap in="SourceGraphic" in2="ripple" scale="8"/>
  
  <feGaussianBlur stdDeviation="0.5"/>
</filter>
风格化/卡通波浪
<filter id="cartoonWater">
  <feTurbulence type="turbulence"
                baseFrequency="0.02 0.08"
                numOctaves="1"  
                result="waves"/>
  <feDisplacementMap in="SourceGraphic" in2="waves" scale="15"/>
  
</filter>

动画技术

JavaScript requestAnimationFrame(最流畅)
const turbulence = document.querySelector('#seaFilter feTurbulence');
let frame = 0;

function animateWaves() {
  frame += 0.003;

  // Gentle breathing motion
  const xFreq = 0.006 + Math.sin(frame) * 0.002;
  const yFreq = 0.05 + Math.sin(frame * 0.7) * 0.01;

  turbulence.setAttribute('baseFrequency', `${xFreq} ${yFreq}`);
  requestAnimationFrame(animateWaves);
}

animateWaves();
SVG animate(声明式,消耗 CPU)
<feTurbulence baseFrequency="0.01 0.1" numOctaves="3">
  <animate
    attributeName="baseFrequency"
    dur="60s"
    keyTimes="0;0.5;1"
    values="0.008 0.08;0.012 0.12;0.008 0.08"
    repeatCount="indefinite"
  />
</feTurbulence>

警告: 对滤镜属性使用 SVG animate 会导致每一帧都完整重新计算滤镜。请谨慎使用。

CSS Transform 动画(性能最佳)

移动水元素,而不是滤镜:

.wave-layer {
  animation: wave-drift 20s linear infinite;
}

@keyframes wave-drift {
  from { transform: translateX(0) translateY(0); }
  to { transform: translateX(-50%) translateY(5px); }
}
Seed 动画(波浪形状变化)

对 seed 设置动画,在不增加 baseFrequency 开销的情况下改变形状:

<feTurbulence baseFrequency="0.01 0.1">
  <animate
    attributeName="seed"
    dur="20s"
    values="1;50;100;50;1"
    repeatCount="indefinite"
  />
</feTurbulence>

分层策略

多层海洋
<div class="ocean">
  <div class="wave wave-back"></div>
  <div class="wave wave-mid"></div>
  <div class="wave wave-front"></div>
  <div class="foam-layer"></div>
</div>
.ocean {
  position: relative;
  height: 100vh;
  background: linear-gradient(180deg,
    #0c4a6e 0%,
    #0369a1 40%,
    #0ea5e9 100%
  );
  overflow: hidden;
}

.wave {
  position: absolute;
  width: 200%;
  height: 100%;
  background: rgba(255,255,255,0.1);
}

.wave-back {
  filter: url(#waveBack);
  opacity: 0.3;
  animation: drift 90s linear infinite;
  bottom: 0;
}

.wave-mid {
  filter: url(#waveMid);
  opacity: 0.5;
  animation: drift 60s linear infinite;
  bottom: -5%;
}

.wave-front {
  filter: url(#waveFront);
  opacity: 0.7;
  animation: drift 35s linear infinite;
  bottom: -10%;
}

.foam-layer {
  position: absolute;
  bottom: 0;
  width: 100%;
  height: 20%;
  background: linear-gradient(to top,
    rgba(255,255,255,0.8) 0%,
    transparent 100%
  );
  filter: url(#foamFilter);
}

@keyframes drift {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}
图层参数指南
图层不透明度速度滤镜 ScalebaseFrequency
后层(深处)0.2-0.480-100s150.004 0.04
中层0.4-0.650-70s200.006 0.06
前层(表面)0.6-0.830-45s250.01 0.1
泡沫0.7-0.925-35s100.02 0.02

调色板

深海
.deep-ocean {
  background: linear-gradient(180deg,
    #0c4a6e 0%,    /* Deep blue */
    #075985 30%,
    #0369a1 60%,
    #0284c7 100%   /* Surface shimmer */
  );
}
  • 主色:#0369a1
  • 深色:#0c4a6e
  • 高光:#38bdf8
热带/加勒比海
.tropical {
  background: linear-gradient(180deg,
    #06b6d4 0%,    /* Cyan surface */
    #22d3ee 40%,
    #67e8f9 70%,
    #a5f3fc 100%   /* Shallow sand reflection */
  );
}
  • 主色:#06b6d4
  • 浅水色:#67e8f9
  • 泡沫:#ecfeff
暴风雨海面
.stormy {
  background: linear-gradient(180deg,
    #1e293b 0%,    /* Dark clouds */
    #334155 30%,
    #475569 60%,
    #64748b 100%   /* Whitecaps */
  );
}
  • 主色:#475569
  • 深处:#1e293b
  • 白浪:#cbd5e1
日落倒影
.sunset-water {
  background: linear-gradient(180deg,
    #831843 0%,    /* Pink sky */
    #9d174d 20%,
    #be185d 40%,
    #0369a1 60%,   /* Water starts */
    #0c4a6e 100%
  );
}

完整实现模板

模板 1:完整海景
<!DOCTYPE html>
<html>
<head>
  <style>
    * { margin: 0; padding: 0; box-sizing: border-box; }

    .ocean-scene {
      position: relative;
      width: 100%;
      height: 100vh;
      background: linear-gradient(180deg,
        #87CEEB 0%,    /* Sky */
        #87CEEB 40%,
        #0369a1 40%,   /* Horizon */
        #0c4a6e 100%   /* Deep */
      );
      overflow: hidden;
    }

    .horizon-line {
      position: absolute;
      top: 40%;
      left: 0;
      right: 0;
      height: 2px;
      background: rgba(255,255,255,0.3);
    }

    .wave {
      position: absolute;
      width: 200%;
      height: 60%;
      bottom: 0;
      background: rgba(255,255,255,0.15);
    }

    .wave-1 {
      filter: url(#wave1);
      animation: drift 80s linear infinite;
      opacity: 0.4;
    }

    .wave-2 {
      filter: url(#wave2);
      animation: drift 55s linear infinite;
      animation-delay: -20s;
      opacity: 0.6;
    }

    .wave-3 {
      filter: url(#wave3);
      animation: drift 35s linear infinite;
      animation-delay: -10s;
      opacity: 0.8;
    }

    @keyframes drift {
      from { transform: translateX(0); }
      to { transform: translateX(-50%); }
    }
  </style>
</head>
<body>
  <svg style="position:absolute;width:0;height:0">
    <defs>
      <filter id="wave1" x="0" y="0" width="100%" height="100%">
        <feTurbulence type="turbulence" baseFrequency="0.004 0.04" numOctaves="3" seed="1"/>
        <feDisplacementMap in="SourceGraphic" scale="15"/>
      </filter>
      <filter id="wave2" x="0" y="0" width="100%" height="100%">
        <feTurbulence type="turbulence" baseFrequency="0.006 0.06" numOctaves="3" seed="2"/>
        <feDisplacementMap in="SourceGraphic" scale="20"/>
      </filter>
      <filter id="wave3" x="0" y="0" width="100%" height="100%">
        <feTurbulence type="turbulence" baseFrequency="0.01 0.1" numOctaves="4" seed="3"/>
        <feDisplacementMap in="SourceGraphic" scale="25"/>
      </filter>
    </defs>
  </svg>

  <div class="ocean-scene">
    <div class="horizon-line"></div>
    <div class="wave wave-1"></div>
    <div class="wave wave-2"></div>
    <div class="wave wave-3"></div>
  </div>
</body>
</html>
模板 2:水下视图(水面后方的内容)
<style>
  .underwater-container {
    position: relative;
    width: 100%;
    overflow: hidden;
  }

  .content-behind-water {
    /* Your actual content */
  }

  .water-overlay {
    position: absolute;
    inset: 0;
    pointer-events: none;
    filter: url(#underwaterDistort);
  }

  .caustics {
    position: absolute;
    inset: 0;
    background: url('data:image/svg+xml,...') repeat;
    opacity: 0.3;
    mix-blend-mode: overlay;
    animation: caustic-shift 10s linear infinite;
  }

  @keyframes caustic-shift {
    from { background-position: 0 0; }
    to { background-position: 100px 50px; }
  }
</style>

<svg style="display:none">
  <defs>
    <filter id="underwaterDistort" x="-10%" y="-10%" width="120%" height="120%">
      <feTurbulence id="underwaterTurb" type="turbulence"
                    baseFrequency="0.015 0.08" numOctaves="3"/>
      <feDisplacementMap in="SourceGraphic" scale="20"
                         xChannelSelector="R" yChannelSelector="B"/>
      <feColorMatrix type="matrix"
                     values="0.85 0 0 0 0
                             0 0.9 0 0 0.02
                             0 0 1.1 0 0.08
                             0 0 0 1 0"/>
    </filter>
  </defs>
</svg>

<script>
  // Animate underwater distortion
  const turb = document.getElementById('underwaterTurb');
  let frame = 0;

  function animate() {
    frame += 0.005;
    const xFreq = 0.015 + Math.sin(frame) * 0.003;
    const yFreq = 0.08 + Math.sin(frame * 0.7) * 0.01;
    turb.setAttribute('baseFrequency', `${xFreq} ${yFreq}`);
    requestAnimationFrame(animate);
  }
  animate();
</script>
模板 3:React 水组件
import React, { useEffect, useRef, useMemo } from 'react';

interface WaterEffectProps {
  type?: 'ocean' | 'pool' | 'stream' | 'glass';
  intensity?: 'subtle' | 'medium' | 'strong';
  animate?: boolean;
  className?: string;
  children?: React.ReactNode;
}

const WATER_CONFIGS = {
  ocean: { baseFreq: [0.008, 0.08], octaves: 4, scale: 25 },
  pool: { baseFreq: [0.02, 0.02], octaves: 2, scale: 12 },
  stream: { baseFreq: [0.01, 0.15], octaves: 3, scale: 20 },
  glass: { baseFreq: [0.01, 0.05], octaves: 2, scale: 8 },
};

const INTENSITY_MULTIPLIERS = {
  subtle: 0.5,
  medium: 1.0,
  strong: 1.5,
};

export const WaterEffect: React.FC<WaterEffectProps> = ({
  type = 'ocean',
  intensity = 'medium',
  animate = true,
  className,
  children
}) => {
  const turbRef = useRef<SVGFETurbulenceElement>(null);
  const frameRef = useRef(0);
  const config = WATER_CONFIGS[type];
  const mult = INTENSITY_MULTIPLIERS[intensity];

  const filterId = useMemo(() =>
    `water-${type}-${Date.now()}`, [type]
  );

  useEffect(() => {
    if (!animate || !turbRef.current) return;

    let animationId: number;

    const animateWater = () => {
      frameRef.current += 0.004;
      const frame = frameRef.current;

      const xFreq = config.baseFreq[0] + Math.sin(frame) * 0.002;
      const yFreq = config.baseFreq[1] + Math.sin(frame * 0.7) * 0.01;

      turbRef.current?.setAttribute('baseFrequency', `${xFreq} ${yFreq}`);
      animationId = requestAnimationFrame(animateWater);
    };

    animateWater();
    return () => cancelAnimationFrame(animationId);
  }, [animate, config]);

  return (
    <>
      <svg style={{ position: 'absolute', width: 0, height: 0 }}>
        <defs>
          <filter id={filterId} x="-10%" y="-10%" width="120%" height="120%">
            <feTurbulence
              ref={turbRef}
              type="turbulence"
              baseFrequency={config.baseFreq.join(' ')}
              numOctaves={config.octaves}
              result="waves"
            />
            <feDisplacementMap
              in="SourceGraphic"
              in2="waves"
              scale={config.scale * mult}
              xChannelSelector="R"
              yChannelSelector="G"
            />
          </filter>
        </defs>
      </svg>
      <div className={className} style={{ filter: `url(#${filterId})` }}>
        {children}
      </div>
    </>
  );
};

// Usage:
// <WaterEffect type="ocean" intensity="medium" animate>
//   <img src="underwater-scene.jpg" />
// </WaterEffect>
模板 4:纯 CSS 波浪(无 SVG)

如需实现简单、高性能且无需 SVG 滤镜的波浪效果:

.css-waves {
  position: relative;
  height: 300px;
  background: linear-gradient(180deg, #0369a1 0%, #0c4a6e 100%);
  overflow: hidden;
}

.css-wave {
  position: absolute;
  width: 200%;
  height: 100%;
  bottom: 0;
  left: -50%;
  background:
    radial-gradient(ellipse 100% 50% at 50% 100%,
      rgba(255,255,255,0.3) 0%,
      transparent 60%
    );
  animation: css-wave-move 8s ease-in-out infinite;
  transform-origin: center bottom;
}

.css-wave:nth-child(1) {
  animation-duration: 7s;
  opacity: 0.5;
}

.css-wave:nth-child(2) {
  animation-duration: 10s;
  animation-delay: -3s;
  opacity: 0.3;
}

.css-wave:nth-child(3) {
  animation-duration: 13s;
  animation-delay: -5s;
  opacity: 0.2;
}

@keyframes css-wave-move {
  0%, 100% {
    transform: translateX(0) scaleY(1);
  }
  50% {
    transform: translateX(25%) scaleY(1.1);
  }
}

性能优化

关键规则
  1. 使用 type="turbulence" - 适用于水面的正确类型(不是 fractalNoise)
  2. numOctaves 不超过 4 - 超过 4 后,视觉提升有限,CPU 开销却会呈指数增长
  3. Scale 取值 10-30 - 超过 40 后,效果会变得不真实且运行更慢
  4. 避免为 baseFrequency 添加动画 - 改用 CSS 变换或 seed 动画
  5. GPU 提示 - 在动画图层上添加 will-change: transform
  6. 合并 SVG defs - 使用一个 <defs> 块,其中包含多个滤镜
性能等级
等级技术目标 FPS使用场景
超高仅使用 CSS 径向渐变60fps移动设备、低端设备
高SVG 滤镜、CSS 变换动画60fps背景波浪
中SVG 滤镜 + seed 动画45-60fps交互式水面
低SVG 滤镜 + baseFrequency 动画30-40fps仅用于主视觉区块
移动设备优化
@media (prefers-reduced-motion: reduce) {
  .wave {
    animation: none !important;
  }
}

@media (max-width: 768px) {
  /* Reduce to 2 wave layers */
  .wave-1 { display: none; }

  /* Use simpler filter */
  .wave { filter: url(#waveSimple); }
}
性能检测
const canHandleWaterEffects = () => {
  // Check for GPU
  const canvas = document.createElement('canvas');
  const gl = canvas.getContext('webgl');
  if (!gl) return 'ultra'; // CSS only

  const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');
  const renderer = debugInfo
    ? gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL)
    : '';

  // Integrated graphics = medium tier
  if (renderer.includes('Intel') || renderer.includes('Mali')) {
    return 'medium';
  }

  return 'high';
};

// Apply appropriate tier
const tier = canHandleWaterEffects();
document.body.dataset.waterTier = tier;
/* Tier-based styling */
[data-water-tier="ultra"] .wave {
  filter: none;
  background: linear-gradient(/* simple gradient */);
}

[data-water-tier="medium"] .wave {
  filter: url(#waveSimple);
}

[data-water-tier="high"] .wave {
  filter: url(#waveFull);
}

框架集成

Next.js / React
// components/OceanBackground.tsx
'use client';

import { useEffect, useState, useRef } from 'react';
import styles from './OceanBackground.module.css';

export function OceanBackground({ children }: { children: React.ReactNode }) {
  const [mounted, setMounted] = useState(false);
  const turbRef = useRef<SVGFETurbulenceElement>(null);

  useEffect(() => {
    setMounted(true);

    // Animate after mount
    let frame = 0;
    const animate = () => {
      frame += 0.003;
      if (turbRef.current) {
        const xFreq = 0.008 + Math.sin(frame) * 0.002;
        const yFreq = 0.08 + Math.sin(frame * 0.7) * 0.01;
        turbRef.current.setAttribute('baseFrequency', `${xFreq} ${yFreq}`);
      }
      requestAnimationFrame(animate);
    };
    const id = requestAnimationFrame(animate);
    return () => cancelAnimationFrame(id);
  }, []);

  if (!mounted) return null;

  return (
    <div className={styles.ocean}>
      <svg className={styles.filters}>
        <defs>
          <filter id="oceanWave">
            <feTurbulence ref={turbRef} type="turbulence"
                          baseFrequency="0.008 0.08" numOctaves="4"/>
            <feDisplacementMap in="SourceGraphic" scale="25"/>
          </filter>
        </defs>
      </svg>
      <div className={styles.waveLayer} />
      <div className={styles.content}>{children}</div>
    </div>
  );
}
Tailwind CSS
// tailwind.config.js
module.exports = {
  theme: {
    extend: {
      animation: {
        'wave-drift': 'wave-drift 60s linear infinite',
        'wave-slow': 'wave-drift 90s linear infinite',
        'wave-fast': 'wave-drift 35s linear infinite',
      },
      keyframes: {
        'wave-drift': {
          from: { transform: 'translateX(0)' },
          to: { transform: 'translateX(-50%)' },
        },
      },
      colors: {
        ocean: {
          deep: '#0c4a6e',
          mid: '#0369a1',
          surface: '#0ea5e9',
          foam: '#f0f9ff',
        },
      },
    },
  },
};
Vue 3
<template>
  <div class="ocean-container">
    <WaveFilters />
    <div
      v-for="layer in waveLayers"
      :key="layer.id"
      class="wave-layer"
      :style="layer.style"
    />
    <slot />
  </div>
</template>

<script setup>
import { computed, onMounted, ref } from 'vue';
import WaveFilters from './WaveFilters.vue';

const waveLayers = computed(() => [
  { id: 1, style: { filter: 'url(#wave1)', animationDuration: '80s', opacity: 0.4 }},
  { id: 2, style: { filter: 'url(#wave2)', animationDuration: '55s', opacity: 0.6 }},
  { id: 3, style: { filter: 'url(#wave3)', animationDuration: '35s', opacity: 0.8 }},
]);
</script>

调试提示

可视化滤镜流水线

<filter id="debug-water">
  <feTurbulence result="step1"/>
  <feImage href="#step1" x="0" y="0" width="200" height="200"/>

  <feDisplacementMap in="SourceGraphic" in2="step1" result="step2"/>
  <feImage href="#step2" x="200" y="0" width="200" height="200"/>
</filter>
常见问题
问题原因解决方案
效果消失滤镜区域太小添加 x="-20%" y="-20%" width="140%" height="140%"
波浪呈方形/盒状使用了 fractalNoise改为 type="turbulence"
波浪过于均匀各图层使用了相同的 seed使用不同的 seed 值
没有水平移动baseFrequency 值相同使用 baseFrequency="0.01 0.1"(x/y 值不同)
动画卡顿为滤镜属性添加了动画改用 CSS 变换动画
边缘伪影边界处发生位移通过 x/y/width/height 增大滤镜区域
浏览器开发者工具
  1. Elements 面板 > 选择 SVG 滤镜 > 检查属性
  2. Performance 面板 > 录制 > 检查是否存在布局抖动
  3. Layers 面板(Chrome)> 确认波浪图层已启用 GPU 加速

与 web-cloud-designer 集成

要创建完整的大气场景,请结合水面和云层效果:

<div class="scene">
  
  <div class="sky">
    <div class="cloud-layer cloud-back"></div>
    <div class="cloud-layer cloud-front"></div>
  </div>

  
  <div class="horizon"></div>

  
  <div class="ocean">
    <div class="wave wave-back"></div>
    <div class="wave wave-front"></div>
    <div class="reflection"></div>
  </div>
</div>

<style>
  .scene {
    height: 100vh;
    display: grid;
    grid-template-rows: 60% 40%;
  }

  .sky {
    background: linear-gradient(180deg, #1e3c72 0%, #87CEEB 100%);
  }

  .ocean {
    background: linear-gradient(180deg, #0369a1 0%, #0c4a6e 100%);
  }

  .reflection {
    /* Mirror cloud movement on water surface */
    position: absolute;
    top: 0;
    width: 100%;
    height: 30%;
    background: inherit;
    transform: scaleY(-1);
    opacity: 0.3;
    filter: url(#waterReflection) blur(2px);
  }
</style>

参考来源

  • Red Stapler:“Realistic Water Effect SVG Turbulence”
  • Mitkov Systems:“Liquid Glass Water Animation”(2025)
  • O'Reilly SVG Book:feTurbulence 章节
  • MDN:SVG Filter Primitives 文档
  • CSS-Tricks:“Underwater Blur Effect”
  • Codrops:“Water Distortion Effect”

水是万物之源。 - Leonardo da Vinci

评论

支持 Markdown