如何为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); }); });
解答你的疑问
直接修改baseFrequency会影响所有节点吗?
会的。如果多个元素共用同一个<filter>,修改它的baseFrequency属性会让所有引用该滤镜的元素同步变化,因为滤镜是全局共享的资源。能否借助CSS或给滤镜传递参数?
当然可以,上面的CSS变量方案就是最优解——通过var()把滤镜参数和元素的CSS变量绑定,相当于给每个元素单独传递参数,既不用复制多个滤镜,也能实现独立控制。
内容的提问来源于stack exchange,提问作者Vladimir
相关产品推荐
相关产品推荐

