如何实现固定点静止、SVG路径移动经过该点的动画效果
问题:如何实现固定点不动、SVG路径反向移动经过该点的动画
这看起来可能有些奇怪,但我想要让SVG路径移动经过一个固定点。我清楚可以使用<animateMotion>实现点沿路径运动的动画,现在我想要得到相同的运动逻辑,只是点保持固定,由路径移动经过该点。
到目前为止,我想到可以用path.getTotalLength()来实现,我写了循环在路径上生成采样点,想要让路径在动画中逐次居中到每个采样点的位置,但我对SVG和JavaScript的了解比较有限,现在卡住了不知道该怎么推进。
我目前的代码如下:
现有JS代码
const path = document.getElementById('chemin') let svg = document.getElementById("mySVG"); let totalLength = path.getTotalLength(); let intersections = 30; for (var i = 0; i <= intersections; i++) { let distance = i * 1 / intersections * totalLength; let point = path.getPointAtLength(distance); addCircleToSVG(point.x, point.y); } function addCircleToSVG(x, y) { let circle = document.createElementNS("http://www.w3.org/2000/svg", 'circle'); circle.setAttribute("cx", x); circle.setAttribute("cy", y); circle.setAttribute("r", "5"); circle.setAttribute("fill", "#8888ff"); svg.appendChild(circle); }
现有HTML代码
<div class="svgDiv"> <svg version="1.1" id="mySVG" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1306 989" style="enable-background:new 0 0 1306 989;" xml:space="preserve"> <style type="text/css"> .st1 { fill: #fff; stroke: #000000; stroke-width: 3; stroke-miterlimit: 10; } .st2 { fill: none; stroke: #ff0000; stroke-width: 8; stroke-miterlimit: 10; stroke-linecap: round; } </style> <g> <path id="chemin" class="st2" d="M315,443l13-11h13l13-2h9l7.5,1.5h10l8-4l4-5l15,5l10,4h12h16l16-2l4-1h11h9l8-6h6l8-2l5-3l4-2l5-4l3-3v-4l4-3 l5-1h11c0,0,0,0,2,0s12,0,12,0l6,3l3,4l4,4l6,2c0,0,1,0,3-1s6-3,6-3l3-2l5,1h6l8,3l8,1c0,0,4,1,5,1s9,0,9,0l7,1h9h5l6-2l4-1l4-3 l5-2l6-4l4-5l4-4l2-5c0,0,1-2,1-4s0-5,0-5v-4c0,0,4,3,4,3l7,2l5,3l6,1l11,2l8,2l7,5l10,2c3-3,6-5,6-5l4-3l4-2l4,2l4-1l-2-6l5-5 c0,0,8,3,8,0s4-6,4-6l6-4l3-4l6-6c0,0,0-3-2-7s-5-8-5-8s-2-5-3-9s-4-9,4-11s2-6,1-8s-5-7-5-7v-8l-1-7v-5l-8-11"> </path> </g> </svg> </div>
一开始我觉得这个思路可行,但现在我觉得这个方法走不通,完全卡住了。
编辑补充:我想要实现的分步效果如下图:
解决方案
这个需求本质是相对运动的逆向实现,不需要修改原有路径的坐标,只需要通过transform属性动态平移路径即可实现效果,具体逻辑如下:
- 先确定目标固定点坐标,这里直接取SVG viewBox的中心
(653, 494.5)作为固定点 - 动画运行时,根据当前进度取路径上对应的采样点
- 计算偏移量:用固定点坐标减去采样点坐标,把这个偏移量作为路径的
translate值,就能让当前采样点刚好对齐到固定点,视觉上就是路径移动经过固定点 - 用
requestAnimationFrame实现平滑动画
可运行完整代码
<!DOCTYPE html> <html> <body> <div class="svgDiv"> <svg version="1.1" id="mySVG" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1306 989" style="enable-background:new 0 0 1306 989; width: 800px;" xml:space="preserve"> <style type="text/css"> .st2 { fill: none; stroke: #ff0000; stroke-width: 8; stroke-miterlimit: 10; stroke-linecap: round; } /* 固定点样式 */ .fixed-point { fill: #8888ff; r: 8; } </style> <!-- 固定点,位置居中 --> <circle class="fixed-point" cx="653" cy="494.5" /> <g id="pathGroup"> <path id="chemin" class="st2" d="M315,443l13-11h13l13-2h9l7.5,1.5h10l8-4l4-5l15,5l10,4h12h16l16-2l4-1h11h9l8-6h6l8-2l5-3l4-2l5-4l3-3v-4l4-3 l5-1h11c0,0,0,0,2,0s12,0,12,0l6,3l3,4l4,4l6,2c0,0,1,0,3-1s6-3,6-3l3-2l5,1h6l8,3l8,1c0,0,4,1,5,1s9,0,9,0l7,1h9h5l6-2l4-1l4-3 l5-2l6-4l4-5l4-4l2-5c0,0,1-2,1-4s0-5,0-5v-4c0,0,4,3,4,3l7,2l5,3l6,1l11,2l8,2l7,5l10,2c3-3,6-5,6-5l4-3l4-2l4,2l4-1l-2-6l5-5 c0,0,8,3,8,0s4-6,4-6l6-4l3-4l6-6c0,0,0-3-2-7s-5-8-5-8s-2-5-3-9s-4-9,4-11s2-6,1-8s-5-7-5-7v-8l-1-7v-5l-8-11"> </path> </g> </svg> </div> <script> const path = document.getElementById('chemin'); const pathGroup = document.getElementById('pathGroup'); const totalLength = path.getTotalLength(); // 固定点坐标,可自行修改 const fixedX = 653; const fixedY = 494.5; // 动画总时长 单位毫秒 const duration = 5000; let startTime = null; function animate(timestamp) { if (!startTime) startTime = timestamp; const progress = Math.min((timestamp - startTime) / duration, 1); const currentDistance = progress * totalLength; const point = path.getPointAtLength(currentDistance); // 计算偏移量 让采样点对齐固定点 const offsetX = fixedX - point.x; const offsetY = fixedY - point.y; pathGroup.setAttribute('transform', `translate(${offsetX}, ${offsetY})`); // 动画循环 if (progress < 1) { requestAnimationFrame(animate); } else { // 如需循环播放,可取消下方两行注释 // startTime = null; // requestAnimationFrame(animate); } } requestAnimationFrame(animate); </script> </body> </html>
实现说明
- 没有修改路径本身的
d属性,而是给路径套了父级g标签,通过修改父级的transform实现整体平移,渲染性能更高 - 偏移量逻辑很简单:要让路径上的点(x,y)移动到(fixedX, fixedY),只需要把整个路径往右移
fixedX - x、往下移fixedY - y即可,刚好和点沿路径移动的偏移方向相反 - 如需调整固定点位置,直接修改
fixedX和fixedY的值即可
内容的提问来源于stack exchange,提问作者Monsieur_PC
相关产品推荐
相关产品推荐

