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

如何实现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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 16:33:40