Anime.js实现SVG路径变形悬停动画未完成时无法反向播放的问题
解决SVG路径悬停动画中途反向失效的问题
我来帮你搞定这个SVG路径变形的动画问题!你遇到的核心问题是:每次鼠标悬停和移出时都新建了一个Anime.js动画实例,当正向动画还没跑完就触发反向时,两个动画会同时修改同一个<path>的d属性,导致动画混乱、反向失效。
咱们用Anime.js的动画实例复用+reverse/play方法就能完美解决,思路是:
- 提前定义好正向和反向的路径序列
- 创建一个可控制的动画实例,手动管理播放/反向
- 鼠标交互时,基于当前动画状态切换方向,而不是新建动画
修改后的完整代码
// 提前定义正向路径数组 const forwardPaths = [ { value: 'm 4 100 h -4 v -100 h 0 z' }, { value: 'm 38 100 h -38 v -100 h 0 z' }, { value: 'm 38 100 h -38 v -100 h 50 z' }, { value: 'm 87 100 h -87 v -100 h 50 z' }, { value: 'm 87 100 h -87 v -100 h 128 z' }, { value: 'M 175 100 h -175 v -100 h 125 Z' }, { value: 'M 175 100 h -175 v -100 h 175 Z' }, ]; // 直接反转正向数组得到反向路径,减少冗余 const reversePaths = forwardPaths.slice().reverse(); // 获取目标路径元素 const backPath = document.querySelector('#logo #back'); // 创建可复用的动画实例,关闭自动播放,手动控制 let pathAnimation = anime({ targets: backPath, d: forwardPaths, easing: 'easeInOutQuart', duration: 500, autoplay: false, direction: 'normal' }); // 绑定鼠标悬停/移出事件 $('#logo').hover(function() { // 如果当前是反向播放状态,先反转回正向 if (pathAnimation.direction === 'reverse') { pathAnimation.reverse(); } // 如果动画未开始或已完成,重置到起始位置 if (!pathAnimation.began || pathAnimation.completed) { pathAnimation.seek(0); } // 播放正向动画 pathAnimation.play(); }, function() { // 如果当前是正向播放状态,切换到反向 if (pathAnimation.direction === 'normal') { pathAnimation.reverse(); } // 播放反向动画 pathAnimation.play(); });
body { background: #1d1e22 } div { margin: 0 auto; width: 175px; margin-top: 10% }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js"></script> <div> <svg id="logo" data-name="logo" xmlns="http://www.w3.org/2000/svg" width="175" height="100" viewBox="0 0 175 100"> <path id="back" d="m 4 100 h 0 v -100 h 0 z" fill="#ed1e79"/> <g id="frame"> <polygon points="69.894 85.938 72.914 85.938 71.418 81.97 69.894 85.938" fill="#fff"/> <path d="M48.3137,81.535H46.8723v6.7521h1.4414c1.7847,0,3.7891-.73,3.7891-3.3624C52.1028,82.1278,50.0984,81.535,48.3137,81.535Z" fill="#fff"/> <path d="M92.0224,81.535H90.5809v6.7521h1.4415c1.7844,0,3.7887-.73,3.7887-3.3624C95.8111,82.1278,93.8068,81.535,92.0224,81.535Z" fill="#fff"/> <path d="M.0676.1441v100h175v-100ZM26.391,89.8027H24.6752V81.9622h-.0275l-2.9515,7.8405H20.4879l-2.91-7.8405H17.55v7.8405H15.9028V80.0472h2.7044l2.5261,6.9308h.0411l2.4986-6.9308h2.718Zm12.9452,0H32.6506V80.0472H39.089v1.5157H34.38v2.48h4.4616v1.46H34.38v2.756h4.9559Zm9.2522,0H45.1425V80.0472h3.4459c2.54,0,5.3677,1.2124,5.3677,4.8775C53.9561,88.3423,51.1281,89.8027,48.5884,89.8027Zm12.89,0h-1.73V80.0472h1.73Zm13.0138,0L73.5862,87.57H69.3715l-.8786,2.2325H66.5574l4.2281-9.7555h1.4964l4.187,9.7555Zm17.8047,0H88.8512V80.0472h3.4455c2.54,0,5.368,1.2124,5.368,4.8775C97.6647,88.3423,94.8367,89.8027,92.2967,89.8027Zm17.8457,0h-6.6853V80.0472h6.4382v1.5157h-4.7087v2.48h4.4616v1.46h-4.4616v2.756h4.9558Zm8.4153.2481a4.4231,4.4231,0,0,1-3.3364-1.3916l1.2357-1.1579a2.7642,2.7642,0,0,0,2.1279,1.0748c.81,0,1.7572-.3994,1.7572-1.4468,0-1.0195-1.0157-1.3085-2.2379-1.6949-1.18-.3721-2.4572-.9507-2.4572-2.7695,0-1.9839,1.7986-2.8657,3.5558-2.8657a4.2327,4.2327,0,0,1,2.8279,1.0474l-1.1257,1.24a2.3781,2.3781,0,0,0-1.7844-.84c-.7964,0-1.7164.3857-1.7164,1.3364,0,.8955.755,1.1572,1.8536,1.502,1.2493.3857,2.8415.937,2.8415,2.852C122.0993,89.0306,120.4657,90.0508,118.5577,90.0508Zm11.1191-.2481h-1.73V80.0472h1.73Zm14.7707-.51a8.2427,8.2427,0,0,1-3.6658.7579,5.1285,5.1285,0,1,1-.0408-10.2516,5.5509,5.5509,0,0,1,3.6923,1.226l-1.1943,1.2955a3.4448,3.4448,0,0,0-2.4844-.9922,3.5867,3.5867,0,0,0,.0822,7.1651,5.001,5.001,0,0,0,1.9358-.3448v-2.48h-2.0458V84.2357h3.7208Zm14.7849.51h-2.1966l-4.6129-7.5372h-.0272v7.5372h-1.73V80.0472h2.2928l4.5166,7.3028h.0272V80.0472h1.73ZM171.5,70.7715H3.6357V3.7254H171.5Z" fill="#fff"/> </g> <path id="lettern" d="M47.9876,18.2283V57.0422h-7.95L20.56,20.1657V55.9594h3.1232v1.0828h-7.78V55.9594h3.1231V18.2283H15.9028V17.1448H31.5761L46.4538,45.7574V18.2283H43.3307V17.1448h7.78v1.0835Z" fill="#fff"/> <path id="letterv" d="M90.577,46.7258l9.8813-28.4975H96.0289V17.1448h8.6884v1.0835h-2.6689L88.3623,57.6126h-4.77l-14.48-39.3843H66.5V17.1448H85.0687v1.0835H81.0371Z" fill="#fff"/> <path id="letterd" d="M159.2323,36.41q0,11.742-6.5871,16.3575-6.0771,4.2747-16.6958,4.2747H119.8788V55.9594h3.7484V18.2283h-3.7484V17.1448h17.5473q10.903,0,16.3543,4.4744T159.2323,36.41ZM134.7,55.9594h2.1584q5.735,0,8.2057-4.0751t2.47-13.6507V34.9279q0-12.8243-4.8837-15.4457a14.1046,14.1046,0,0,0-6.5871-1.2539H134.7Z" fill="#fff"/> </svg> </div>
关键改进点说明
- 复用动画实例:只创建一个Anime.js实例,彻底避免多动画同时修改元素属性的冲突问题
- 智能切换播放方向:
- 鼠标悬停时,自动判断当前动画状态,若处于反向播放则切换回正向,从当前进度继续播放
- 鼠标移出时,同样基于当前状态切换到反向,保证平滑过渡
- 减少冗余代码:通过反转正向路径数组直接得到反向序列,不用手动重复编写反向路径
现在不管你什么时候移出鼠标,动画都会从当前状态平滑地反向过渡回去,不会再出现失效的情况啦!
内容的提问来源于stack exchange,提问作者Biels




