悬停时动画 SVG - 悬停时从不微笑

Animate SVG on hover - Make a smile from donts on hover

我有一个 SVG 文件,其中的点在直线上。我想要实现的是当用户将鼠标悬停在 svg 文件上时,这些点会像附​​图一样变成微笑。

过渡需要平稳。

最好的方法是什么?我可以只使用 css 还是我也应该使用 js?

谢谢

在下一个示例中,我将计算路径上每个点的位置,每个点的位置是使用 getTotalLength 计算的路径总长度的十分之一。对于每个圆圈,我都设置了初始 cx 的值(在这种情况下,因为初始 cy==0 我没有设置它。

我还在每个圆圈内添加了 2 个 <animate> 元素,这些元素将使 cx 和 cy 动画到遏制路径上的下一个位置。

动画将在点击时开始。

const SVG_NS = "http://www.w3.org/2000/svg";//svg namespace
const dur = .5;// the animation duration
let circles = Array.from(document.querySelectorAll("circle"))

let la = a.getTotalLength();
let lb = b.getTotalLength();
let start = {x:-100,y:0}

circles.forEach((c,i) =>{
  let da = i*la/10;
  let db = i*lb/10;
  let pa = a.getPointAtLength(da); 
  let pb = b.getPointAtLength(db); 
  c.setAttribute("cx",pa.x);
  
  let a1 = document.createElementNS(SVG_NS,"animate");
  a1.setAttribute("attributeName","cx");
  a1.setAttribute("from",pa.x);
  a1.setAttribute("to",pb.x);
  a1.setAttribute("dur",dur);
  a1.setAttribute("begin","svg.click");  
  c.appendChild(a1);
  
  
  let a2 = document.createElementNS(SVG_NS,"animate");
  a2.setAttribute("attributeName","cy");
  a2.setAttribute("from",pa.y);
  a2.setAttribute("to",pb.y);
  a2.setAttribute("dur",dur);
  a2.setAttribute("begin","svg.click");  
  c.appendChild(a2);
})

svg.addEventListener("click",()=>{
  circles.forEach((c,i) =>{
  let db = i*lb/10
  let pb = b.getPointAtLength(db);  
  c.setAttribute("cx",pb.x)
  c.setAttribute("cy",pb.y)
})
})
svg{border:solid}
path{fill:none; stroke:black;}
<svg id="svg" viewBox="-150 -50 300 150" width="300">
  <path id="a" d="M-100,0 A15000,15000 0 0 0 100,0"/>
  <path id="b" d="M-100,0 A150,150 0 0 0 100,0"/>
  
  <circle r="5" />
  <circle r="5" />
  <circle r="5" />
  <circle r="5" />
  <circle r="5" />
  <circle r="5" />
  <circle r="5" />
  <circle r="5" />
  <circle r="5" />
  <circle r="5" />
  <circle r="5" />
</svg>

在这个例子中,用于计算圆圈位置的路径是描边的。您可以从 CSS.

中删除笔划

使用 SMIL animation,无需任何脚本即可实现。它将立方贝塞尔曲线路径从直线变形为曲线并返回。动画由位于线条顶部的透明矩形上的 mouseovermouseout 事件触发。

该行本身使用了两个小技巧的组合:您可以将 pathLength 设置为属性,以便 stroke-dasharray 然后根据它计算破折号长度。对于 stroke-dasharray: 0 1stroke-linecap 的组合,零长度破折号显示为以笔划宽度作为直径的点。只需使用 pathLengthstroke-width 的值来改变点的距离和大小。

#line {
  fill: none;
  stroke: black;
  stroke-width: 4;
  stroke-dasharray: 0 1;
  stroke-linecap: round;
}
#overlay {
  opacity: 0;
}
<svg viewBox="0 0 100 100" width="150">
  <path id="line" d="M 10 50 C 35 50 65 50 90 50" pathLength="15">
    <animate attributeName="d" begin="overlay.mouseover" dur="0.3s"
             fill="freeze" restart="whenNotActive"
             from="M 10 50 C 35 50 70 50 90 50"
             to="M 15 30 C 20 70 80 70 85 30"/>
    <animate attributeName="d" begin="overlay.mouseout" dur="0.3s"
             fill="freeze" restart="whenNotActive"
             from="M 15 30 C 20 70 80 70 85 30"
             to="M 10 50 C 35 50 65 50 90 50"/>
  </path>
  <rect id="overlay" width="100%" height="100%" />
</svg>