如何实现SVG中线条与圆形的风吹式振动(振荡)动画效果
SVG实现风吹摆动振荡效果
你要的这类生动的晃动感,本质是模拟一端固定的弹性物体受风力后的简谐振荡,不用复杂的动画库,两种简单方案就能实现:
- 直线左端(x=10,y=50)作为固定端完全锁死不动
- 直线右端连带旁边的圆形作为自由端,做小幅度上下+微幅水平的往复位移
- 想更真实就给线条加弯曲形变,动画加缓动、随机扰动,避免机械循环感
方法1:纯SVG原生动画(无JS)
不需要写任何脚本,直接用SVG内置的<animate>标签就能实现基础的柔和摆动效果,代码量最小:
<svg height="1000" width="1000"> <line x1="10" y1="50" x2="250" y2="50" stroke="black" stroke-width="3" fill="red"> <!-- 右端点Y轴往复振动,上下幅度15px --> <animate attributeName="y2" values="50;35;50;65;50" dur="2s" repeatCount="indefinite" calcMode="spline" keySplines="0.4 0 0.6 1;0.4 0 0.6 1;0.4 0 0.6 1;0.4 0 0.6 1"/> <!-- 右端点X轴微幅偏移,模拟风的推拉感 --> <animate attributeName="x2" values="250;258;250;245;250" dur="2s" repeatCount="indefinite" calcMode="spline" keySplines="0.4 0 0.6 1;0.4 0 0.6 1;0.4 0 0.6 1;0.4 0 0.6 1"/> </line> <circle cx="270" cy="50" r="20" stroke="black" stroke-width="3" fill="red"> <!-- 圆形和线条右端点同步位移 --> <animate attributeName="cy" values="50;35;50;65;50" dur="2s" repeatCount="indefinite" calcMode="spline" keySplines="0.4 0 0.6 1;0.4 0 0.6 1;0.4 0 0.6 1;0.4 0 0.6 1"/> <animate attributeName="cx" values="270;278;270;265;270" dur="2s" repeatCount="indefinite" calcMode="spline" keySplines="0.4 0 0.6 1;0.4 0 0.6 1;0.4 0 0.6 1;0.4 0 0.6 1"/> </circle> </svg>
这里用spline缓动代替默认的匀速动画,避免生硬的机械感,默认2秒一个摆动周期,是比较柔和的微风效果。要更大摆幅就改values里的偏移数值,要风更急就把dur改小(比如改成1.2s)就行。
方法2:JS实现高拟真自然风效果
如果要更真实的体验——比如风力忽大忽小、随机出现阵风、线条自然弯曲,用十几行JS就能实现,效果比纯SMIL灵活很多:
这里把原来的硬直线换成二次贝塞尔路径,给线条中间加控制点,振动时控制点位移比末端稍大,模拟软质线条(比如草叶、绳带)被风吹的自然弯曲:
<svg height="1000" width="1000"> <!-- 二次贝塞尔曲线:固定端(10,50),中间控制点初始(130,50),末端初始(250,50) --> <path d="M 10 50 Q 130 50 250 50" stroke="black" stroke-width="3" fill="none" id="swing-line"/> <circle cx="270" cy="50" r="20" stroke="black" stroke-width="3" fill="red" id="swing-circle"/> </svg> <script> const line = document.getElementById('swing-line'); const circle = document.getElementById('swing-circle'); // 可调整的效果参数 const conf = { endBaseX: 250, endBaseY: 50, maxYOffset: 20, // 最大上下摆幅 maxXOffset: 10, // 最大水平风推偏移 speed: 0.003, // 摆动速度 gustRate: 0.005, // 阵风出现概率 gustStrength: 1 } let tick = 0; function playAnimate() { tick += 1; // 基础简谐振动 let offsetY = Math.sin(tick * conf.speed) * conf.maxYOffset; let offsetX = Math.cos(tick * conf.speed * 0.8) * conf.maxXOffset; // 随机触发阵风,临时加大摆幅 if (Math.random() < conf.gustRate) conf.gustStrength = 0.8 + Math.random() * 0.7; offsetY *= conf.gustStrength; offsetX *= conf.gustStrength; // 阵风强度逐帧衰减 conf.gustStrength = Math.max(1, conf.gustStrength * 0.992); // 计算当前点位 const curEndX = conf.endBaseX + offsetX; const curEndY = conf.endBaseY + offsetY; const curCtrlX = 130 + offsetX * 0.7; const curCtrlY = 50 + offsetY * 1.2; // 更新图形 line.setAttribute('d', `M 10 50 Q ${curCtrlX} ${curCtrlY} ${curEndX} ${curEndY}`); circle.setAttribute('cx', curEndX + 20); circle.setAttribute('cy', curEndY); requestAnimationFrame(playAnimate); } playAnimate(); </script>
这个版本没有固定的循环周期,会随机出现阵风效果,线条会自然弯曲不会像硬杆一样僵直,直接修改conf里的参数就能调整摆幅、风速、阵风频率,适配不同的场景需求。
如果要做「被风吹一下之后慢慢停稳」的效果,只要给振动振幅加一个逐帧衰减的系数就行,比如每帧给振幅乘0.98,几秒后摆动就会完全停止回到初始位置。
内容的提问来源于stack exchange,提问作者E Epsylon
相关产品推荐
相关产品推荐

