You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为SVG Turbulence滤镜实现鼠标悬停渐进启停动画?

实现SVG Turbulence滤镜的悬停渐变动画

核心方案:用CSS变量绑定滤镜参数(最简便)

直接给SVG滤镜的baseFrequency和scale参数绑定CSS变量,就能通过CSS或JS单独控制每个元素的滤镜强度,不会互相影响。

第一步:改造SVG滤镜

把固定参数换成CSS变量,默认值保留你原来的设置:

<svg>
  <defs>
    <filter id="turbulence">
      <!-- 用var()绑定baseFrequency,默认值用你原来的数值 -->
      <feTurbulence type="fractalNoise" baseFrequency="var(--turbulence-freq, 0.005)" numOctaves="2" result="turbulence"/>
      <!-- 位移量也可以绑定变量,方便同步调整 -->
      <feDisplacementMap in="SourceGraphic" in2="turbulence" scale="var(--turbulence-scale, 20)"/>
    </filter>
  </defs>
  <!-- 你的菜单节点 -->
  <g class="node">
    <circle cx="50" cy="50" r="40" filter="url(#turbulence)"/>
  </g>
  <g class="node">
    <circle cx="150" cy="50" r="40" filter="url(#turbulence)"/>
  </g>
</svg>

第二步:用CSS过渡实现平滑动画

直接给circle元素加CSS过渡,悬停时修改变量值就能实现渐进增强/渐弱:

.node circle {
  --turbulence-freq: 0.005; /* 初始频率 */
  --turbulence-scale: 20;    /* 初始位移量 */
  /* 给变量加过渡,实现平滑动画 */
  transition: --turbulence-freq 0.4s ease, --turbulence-scale 0.4s ease;
}

.node circle:hover {
  --turbulence-freq: 0.03; /* 悬停时增强频率 */
  --turbulence-scale: 40;  /* 同步增强位移量 */
}

这种方式完全不用JS,是最简单的实现方案,主流浏览器都支持CSS变量的过渡效果。


灵活版:JS控制动画(适合复杂逻辑)

如果需要更自定义的缓动效果或动画逻辑,比如中途打断动画、自定义时长,可以用JS结合requestAnimationFrame控制CSS变量:

// 获取所有需要动画的circle
const circles = document.querySelectorAll('.node circle');

// 通用动画函数:平滑修改滤镜参数
function animateTurbulence(element, targetFreq, targetScale, duration = 400) {
  // 获取当前参数值
  const startFreq = parseFloat(getComputedStyle(element).getPropertyValue('--turbulence-freq'));
  const startScale = parseFloat(getComputedStyle(element).getPropertyValue('--turbulence-scale'));
  const startTime = performance.now();

  function updateFrame(currentTime) {
    const elapsed = currentTime - startTime;
    const progress = Math.min(elapsed / duration, 1);
    // 用ease-in-out缓动函数让动画更自然
    const easedProgress = progress < 0.5 ? 2 * progress * progress : 1 - Math.pow(-2 * progress + 2, 2) / 2;
    
    // 计算当前参数值
    const currentFreq = startFreq + (targetFreq - startFreq) * easedProgress;
    const currentScale = startScale + (targetScale - startScale) * easedProgress;
    
    // 更新元素的CSS变量
    element.style.setProperty('--turbulence-freq', currentFreq);
    element.style.setProperty('--turbulence-scale', currentScale);
    
    // 未完成则继续动画
    if (progress < 1) {
      requestAnimationFrame(updateFrame);
    }
  }

  requestAnimationFrame(updateFrame);
}

// 绑定鼠标事件
circles.forEach(circle => {
  circle.addEventListener('mouseenter', () => {
    animateTurbulence(circle, 0.03, 40);
  });
  
  circle.addEventListener('mouseleave', () => {
    animateTurbulence(circle, 0.005, 20);
  });
});

解答你的疑问

  1. 直接修改baseFrequency会影响所有节点吗?
    会的。如果多个元素共用同一个<filter>,修改它的baseFrequency属性会让所有引用该滤镜的元素同步变化,因为滤镜是全局共享的资源。

  2. 能否借助CSS或给滤镜传递参数?
    当然可以,上面的CSS变量方案就是最优解——通过var()把滤镜参数和元素的CSS变量绑定,相当于给每个元素单独传递参数,既不用复制多个滤镜,也能实现独立控制。

内容的提问来源于stack exchange,提问作者Vladimir

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 20:55:23