使用CSS代替<animateMotion>对SVG路径进行动画处理

时间:2020-05-05 15:03:51

标签: css animation svg

我试图在贝塞尔曲线的中点放置一个箭头。在问题How properly shift arrow head on cubic bezier in SVG to its center中使用<animateMotion>的解决方案仅在Firefox中有效,该解决方案移动箭头即箭头<path>并将其冻结在贝塞尔曲线的中间。由于在我的情况下曲线的点经常变化,因此我不想使用marker-mid,因为每次计算贝塞尔曲线的中点都比较昂贵。

<svg viewBox="0 0 500 500">
<g>
    <path id="path1" d="M291.698 268.340 C321.698 268.340, 411.904 93.133 441.904 93.133"></path>
    <path class="path_arrow" d="M0,0 L6,6 L0,12" transform="translate(-3,-6)">
        <animateMotion dur="0s" rotate="auto" fill="freeze" keyTimes="0;1" keyPoints="0.5;0.5">
            <mpath xlink:href="#path1"></mpath>
        </animateMotion>
    </path> 
</g>
<g transform="translate(166.698,243.340)">
    <circle r="5" class="p1"></circle>
</g>
<g transform="translate(441.904,68.133)" >
 <circle r="5" class="p2"></circle>
</g>
</svg>

有什么方法可以使用CSS动画来避免使用<animateMotion>吗?

编辑1:

此处的曲线端点是可拖动的,因此曲线的点倾向于频繁变化。动画是将箭头移动到曲线的中心,而不计算中点。 enter image description here

编辑2:

感谢Kaiido的评论,我添加了calcMode="linear",现在箭头已按预期放置在路径上。但是,当我通过拖动来重新定位端点时,箭头在Chrome中停留在其初始位置(如图所示),但预计它将沿着父路径移动。在Firefox中,它可以像以前一样正常工作。

enter image description here

2 个答案:

答案 0 :(得分:2)

您可以使用CSS offset-pathoffset-distanceoffset-rotate属性来实现相同的目的:

var startDate = moment().subtract(1, 'months').format('YYYY-MM-DD');
var endDate = moment().format('YYYY-MM-DD');

const data = {
   labels: [startDate, endDate],
   //rest of the code
}
#path1 {
  fill: none;
  stroke: black;
}
.path_arrow {
  transform: translate( -3px, -6px );
  offset-path: path("M220 104C220 144,400 180,400 224");
  offset-rotate: auto;
  offset-distance: 50%;
}
body { background: white; }

但是their browser support远低于the one of SMIL,所以我不推荐它。

请注意,我确实修复了the answer there缺失的<svg width="500" height="500" > <path id="path1" d="M 220 104 C 220 144 400 180 400 224" fill="none" stroke-width="2" stroke="black" /> <path class="path_arrow" d="M0,0 L0,12 L12,6 z" /> </svg>属性,以使Blink浏览器满意。

如果您需要IE支持,则可以尝试使用似乎支持calcMode="linear"<animateMotion>的{​​{3}},但要记住我并不是自己测试的。



关于问题的“编辑2”:

Chrome确实确实需要显式调用才能更新rotate。可以通过在每次更新后调用<mpath>元素的this js implementation方法来实现:

<animationMotion>
document.querySelector('svg').onmousemove = function(e) {
  const rect = this.getBoundingClientRect();
  const x = e.clientX - rect.left;
  const y = e.clientY - rect.top;
  path1.setAttribute( 'd', `M ${x} ${y} C 220 144 400 180 400 224` );
  // Chrome requires an explicit update
  document.querySelector('animateMotion').beginElement();
}

答案 1 :(得分:0)

您必须在CSS内使用Webkit动画。我也建议https://codepen.io/collection/yivpx/。是用于SVG优化的开源项目。我还建议您在SVG中命名所有类,以便通过动画制作酷炫的CSS素材,例如:

animation: kaboom 5s ease alternate infinite;