第二次点击时 SVG 动画反转

SVG animate reverse on second click

我是一个尝试制作交互式 SVG 的新手 - 最好没有任何外部脚本。我想要的效果是让一个 SVG 元素充当一个交互式开关,使另一个元素出现和消失。

请在下面找到一个简单版本,其中文本“Toggle”用作切换。单击时,这将使矩形的不透明度属性从 0 变为 1,使其出现。

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
  xmlns:xlink="http://www.w3.org/1999/xlink"
  xmlns="http://www.w3.org/2000/svg"
   width="47.652294mm"
   height="10.096307mm"
   viewBox="0 0 47.652294 10.096307"
   version="1.1"
   id="svg8">
  <style
     id="style861"></style>
  <defs
     id="defs2" />
  <g
     id="layer1"
     transform="translate(-29.085516,-61.315985)">
    <rect
       fill="#ff0000"
       opacity="0"
       id="rect"
       width="9.8317242"
       height="9.8317242"
       x="66.773796"
       y="61.448277">
       <animate attributeName="opacity" fill="freeze" from="0" to="1" dur="2s" begin="toggletext.click" />
     </rect>
    <text
       xml:space="preserve"
       style="font-size:8.46667px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:sans-serif;stroke-width:0.264583"
       x="29.085516"
       y="68.002762"
       id="toggletext"><tspan
         id="tspan857"
         x="29.085516"
         y="68.002762"
         style="stroke-width:0.264583">Toggle</tspan></text>
  </g>
</svg>

任何后续点击现在只会重复相同的动画。但我想要任何第二个(第四个、第六个等)点击来反转动画(即让矩形消失)。换句话说,真正充当切换器。

任何有关如何使用尽可能少的代码 and/or 不可见元素实现此效果的建议,我们将不胜感激。 谢谢!

添加第二个矩形渐变动画

<animate  id="hide" attributeName="opacity" fill="freeze" 
  from="1" to="0" dur="2s" begin="indefinite" /> 

并添加一个切换矩形出现和消失动画的JS触发器

var svg_1 = document.getElementById("svg8"),
  hide = document.getElementById("hide"),
  visable = document.getElementById("visable");

let flag = true;

svg_1.addEventListener('click', function() {
  if (flag == true) {
    visable.beginElement();
    flag = false;
  } else {
    hide.beginElement();
    flag = true;
  }
});
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
  xmlns:xlink="http://www.w3.org/1999/xlink"
  xmlns="http://www.w3.org/2000/svg"
   width="47.652294mm"
   height="10.096307mm"
   viewBox="0 0 47.652294 10.096307"
   version="1.1"
   id="svg8">
  <style
     id="style861"></style>
  <defs
     id="defs2" />
  <g
     id="layer1"
     transform="translate(-29.085516,-61.315985)">
    <rect
       fill="#ff0000"
       opacity="0"
       id="rect"
       width="9.8317242"
       height="9.8317242"
       x="66.773796"
       y="61.448277">
       <animate id="visable" attributeName="opacity" fill="freeze" from="0" to="1" dur="2s" begin="indefinite" /> 
         <animate  id="hide" attributeName="opacity" fill="freeze" from="1" to="0" dur="2s" begin="indefinite" />
     </rect>
    <text
       xml:space="preserve"
       style="font-size:8.46667px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:sans-serif;stroke-width:0.264583"
       x="29.085516"
       y="68.002762"
       id="toggletext"><tspan
         id="tspan857"
         x="29.085516"
         y="68.002762"
         style="stroke-width:0.264583">Toggle</tspan></text>
  </g>
</svg>

这就是我的做法:我使用了 2 个重叠的文本元素,并将指针事件设置为全部或 none 单击:这样您将在一个文本上单击一次接下来是另一个。

矩形有 2 个动画元素:第一个动画将在您单击第一个文本时开始,第二个动画将在您单击第二个文本时开始。

text{font-size:8.46667px;line-height:1.25;font-family:sans-serif;stroke-width:0.264583;}
<svg width="300" viewBox="0 0 47.652294 10.096307" id="svg8">

  <g id="layer1" transform="translate(-29.085516,-61.315985)">
    <rect fill="#ff0000" opacity="0" id="rect" width="9.8317242" height="9.8317242" x="66.773796" y="61.448277">
      <animate attributeName="opacity" fill="freeze" from="0" to="1" dur="2s" begin="toggletext1.click" />
      <animate attributeName="opacity" fill="freeze" from="1" to="0" dur="2s" begin="toggletext2.click" />
    </rect>
    

    <text x="29.085516" y="68.002762" id="toggletext2">
      <tspan x="29.085516" y="68.002762">Toggle</tspan>
      <set attributeName="pointer-events" from="none" to="all" begin="toggletext1.click" />
      <set attributeName="pointer-events" from="all" to="none" begin=".click" />
    </text>
    
    <text x="29.085516" y="68.002762" id="toggletext1">
      <tspan x="29.085516" y="68.002762">Toggle</tspan>
      <set attributeName="pointer-events" from="none" to="all" begin="toggletext2.click" />
      <set attributeName="pointer-events" from="all" to="none" begin=".click" />
    </text>
  </g>
</svg>

虽然我喜欢No-JavaScript pointer-events方法;我会这样做:

当 OP 说:最好没有任何外部脚本
我认为他的意思是没有第 3 方库。

所以我会使用自定义元素 <svg-toggle>(所有现代浏览器都支持)
为您想要的任意数量的切换创建 SVG

切换动画:

  • 每次点击切换 fromto 参数
  • 重启动画

<style>
  svg {
    display: inline-block; width: 30%; vertical-align: top;
    cursor: pointer; background: teal; color: white;
  }
</style>

<svg-toggle></svg-toggle>
<svg-toggle color="yellow"></svg-toggle>
<svg-toggle color="blue" label="Blue" duration=".5"></svg-toggle>

<script>
customElements.define('svg-toggle', class extends HTMLElement {
  connectedCallback() {
    this.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 18">
    <text x="2" y="12" font-size="10px" fill="currentColor">
          ${this.getAttribute("label")||'Toggle'}</text>  
      <rect fill="${this.getAttribute("color")||'red'}" x='33' y="3" width="12" height="12">
        <animate attributeName="opacity" dur="${this.getAttribute("duration")||2}s" from="0" to="1" fill="freeze" begin="indefinite"/> 
      </rect></svg>`;
    this.animate = this.querySelector("animate");
    this.onclick = (evt) => this.toggle();
  }
  toggle( // method, so can be called from Javascript
    from = this.animate.getAttribute("from"), // optional from/to parameters
    to   = this.animate.getAttribute("to"),
  ) { 
    this.animate.setAttribute( "from", to   );
    this.animate.setAttribute( "to"  , from );
    this.animate.beginElement();
  }
});
</script>

我不想要现代 W3C 标准 Web Components 胡言乱语...

然后将 JavaScript 贴在 每个 SVG 上:

<svg 
  onclick="{
    let a = this.querySelector('animate');
    let from = a.getAttribute('from');
    a.setAttribute('from',a.getAttribute('to'));
    a.setAttribute('to',from);
    a.beginElement();
  }"
  xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 18">
  <text x="2" y="12" font-size="10px" fill="currentColor">Gold</text>  
  <rect fill="gold" x="33" y="3" width="12" height="12">
    <animate attributeName="opacity" dur=".3s" from="0" to="1" 
             fill="freeze" begin="indefinite"></animate> 
  </rect>
</svg>

我不要JavaScript

查看 Enxaneta 的 pointer-events 回答