如何实现SVG弯曲传送带的运行动画效果?
实现SVG弯曲传送带运行动画的可行方法
原生成代码
JavaScript
const cx = 50; const cy = 50; const width = 40; const radius = 100; // CV size const TwoPi = Math.PI * 0.5; const circ = TwoPi * radius; const height = circ / 4; // Roller counts const parent = document.getElementById("bend"); for (let i = 0; i < circ; i += height) { let seg = document.createElementNS("http://www.w3.org/2000/svg", "path"); let rs = (i / circ) * TwoPi; let re = ((i + height) / circ) * TwoPi; let ss = Math.sin(rs); let cs = Math.cos(rs); let se = Math.sin(re); let ce = Math.cos(re); seg.setAttribute("d", `M${(cs * radius) + cx},${(ss * radius) + cy}` + `A${radius},${radius} ${((re - rs) / Math.PI) * 180},0,1 ${(ce * radius) + cx},${(se * radius) + cy}` + `L${(ce * (radius - width)) + cx},${(se * (radius - width)) + cy}` + `A${radius - width},${radius - width} ${((re - rs) / Math.PI) * -180},0,0 ${(cs * (radius - width)) + cx},${(ss * (radius - width)) + cy}z` ); seg.setAttribute("fill", "url(#grad2)"); seg.setAttribute("stroke", "#008000"); parent.appendChild(seg); }
HTML
<svg width="200" height="200" viewBox="0 0 200 200" style="transform: rotate(-90deg)"> <defs> <linearGradient id="grad2" x1="0%" y1="0%" x2="40%" y2="100%"> <stop offset="0%" style="stop-color:#008000;stop-opacity:1"/> <stop offset="100%" style="stop-color:#c7fdc7;stop-opacity:1"/> </linearGradient> </defs> <g id="bend"></g> </svg>
可行实现方法
方法1:整体旋转动画(快速实现循环滚动)
利用SVG原生动画标签,让整个传送带组围绕圆心旋转,是最简便的方案。
修改HTML中的<g id="bend">标签,添加动画:
<g id="bend"> <animateTransform attributeName="transform" type="rotate" origin="50 50" <!-- 对应代码里的cx、cy圆心坐标 --> from="0" to="90" <!-- 对应TwoPi的90度,刚好覆盖四分之一圆范围 --> dur="3s" repeatCount="indefinite" ease="linear" /> </g>
原理:传送带是四分之一圆弧,整体旋转90度后刚好回到初始视觉状态,循环后即可模拟持续滚动效果。
方法2:分段位移动画(更细腻的滚动感)
如果需要每个传送带分段独立移动,而非整体旋转,可通过JS实时更新分段的路径位置。
在原JS代码末尾添加以下逻辑:
// 记录每个分段的初始偏移量 const segments = Array.from(parent.children); const offsets = segments.map((_, idx) => idx * height); let animationId; function animate() { offsets.forEach((offset, idx) => { // 每帧移动1单位,超出圆弧周长后重置 let newOffset = (offset + 1) % circ; offsets[idx] = newOffset; let rs = (newOffset / circ) * TwoPi; let re = ((newOffset + height) / circ) * TwoPi; let ss = Math.sin(rs); let cs = Math.cos(rs); let se = Math.sin(re); let ce = Math.cos(re); // 更新当前分段的路径 segments[idx].setAttribute("d", `M${(cs * radius) + cx},${(ss * radius) + cy}` + `A${radius},${radius} ${((re - rs) / Math.PI) * 180},0,1 ${(ce * radius) + cx},${(se * radius) + cy}` + `L${(ce * (radius - width)) + cx},${(se * (radius - width)) + cy}` + `A${radius - width},${radius - width} ${((re - rs) / Math.PI) * -180},0,0 ${(cs * (radius - width)) + cx},${(ss * (radius - width)) + cy}z` ); }); animationId = requestAnimationFrame(animate); } // 启动动画 animate(); // 如需停止动画:cancelAnimationFrame(animationId);
原理:每个分段沿着圆弧路径逐步移动,偏移量超出范围后重置,实现循环滚动,视觉上每个分段都在独立前进。
方法3:配合渐变动画增强真实感
在上述两种动画的基础上,给传送带的渐变添加动画,模拟滚动时的纹理移动,提升视觉真实度。
修改<linearGradient>部分:
<linearGradient id="grad2" x1="0%" y1="0%" x2="40%" y2="100%"> <stop offset="0%" style="stop-color:#008000;stop-opacity:1"> <animate attributeName="offset" from="0%" to="100%" dur="1.5s" repeatCount="indefinite" ease="linear" /> </stop> <stop offset="100%" style="stop-color:#c7fdc7;stop-opacity:1"> <animate attributeName="offset" from="100%" to="200%" dur="1.5s" repeatCount="indefinite" ease="linear" /> </stop> </linearGradient>
原理:渐变的颜色偏移跟随传送带滚动方向移动,让表面呈现出持续流动的质感,和旋转/分段动画配合效果更逼真。
内容的提问来源于stack exchange,提问作者Choco
相关产品推荐
相关产品推荐

