【问题标题】:Infinite SVG animation smoothly retrieve initial state on hover无限 SVG 动画在悬停时平滑地检索初始状态
【发布时间】:2021-03-09 07:33:36
【问题描述】:

我希望我的无限 svg 动画在悬停时暂停在其初始或最终状态。我尝试使用 JS 删除类或在 CSS 中将animation-iteration-count: unset; 设置为悬停或设置animation: none;,但在每种情况下,转换都是突然的。我希望它顺利。这是代码: HTML

    <div class="button">
        <svg version="1.1" id="sound--icon" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
        viewBox="0 0 100 100"   >
          <rect class="rect-1 rect-anim" x="10" fill="currentColor"></rect>
          <rect class="rect-2 rect-anim" x="30" fill="currentColor"></rect>
          <rect class="rect-3 rect-anim" x="50" fill="currentColor"></rect> 
          <rect class="rect-4 rect-anim" x="70" fill="currentColor"></rect>
      </svg>
    </div>

CSS

#sound--icon .rect-anim{
    animation-name: sound-icon-anim;
    animation-duration: .8s;
    animation-timing-function: cubic-bezier(.97,0,0,1);
    animation-iteration-count: infinite;
}

/*  #sound--icon:hover .rect-anim{
    animation-iteration-count: unset;
} 
 */

#sound--icon .rect-1{
    animation-delay: .1s;
}

#sound--icon .rect-2{
    animation-delay: .3s;
}

#sound--icon .rect-3{
    animation-delay: .5s;
}

#sound--icon .rect-4{
    animation-delay: .7s;
}

@keyframes sound-icon-anim{
    0%{
        height: 30%;
    }
    10%{
        height: 70%;
    }
    20%{
        height: 50%;
    }
    50%{
        height: 80%;
    }
    60%{
        height: 100%;
    }
    70%{
        height: 70%;
    }
    100%{
        height: 30%;
    }
}

我最终的目标是:

  • 动画正在运行
  • 当 div 悬停/动画暂停时,高度平滑地达到 50%
  • 当点击 div/动画暂停时,高度平滑地达到 30%
  • 当 div 再次悬停/动画仍然暂停时,高度平滑地达到 50%
  • 再次单击 div 时动画会重新运行(因此从 50% 开始,而不是从 0% 动画状态状态的 30% 开始)

在纯 CSS 中这对您来说是否可行?

【问题讨论】:

    标签: css animation svg hover css-transitions


    【解决方案1】:

    我认为您无法在纯 CSS 中做到这一点。使用Web Animations API,这是可能的,但在当前的实施状态下,只有部分方法是现实的。

    您可以实现的是在迭代周期结束时停止动画。在一个不是循环结束的点停止将需要实现AnimationTimeline。但目前 Chrome 缺少它。

    document.querySelectorAll('.button').forEach(button => {
        //.getAnimations() returns an array
        const animations = [...button.querySelectorAll('.rect-anim')]
            .map(r => r.getAnimations())
            .flat();
    
        button.addEventListener('mouseenter', () => {
            for (let animation of animations) {
                const iterations = animation.effect.getComputedTiming()
                    .currentIteration + 1;
                animation.effect.updateTiming({iterations})
            }
        });
    
        button.addEventListener('mouseleave', () => {
            for (let animation of animations) {
                animation.effect.updateTiming({iterations: Infinity});
            }
      });
    });
    .button {
      margin: 20px;
      width: 100px;
    }
    
    #sound--icon .rect-anim{
        animation-name: sound-icon-anim;
        animation-duration: .8s;
        animation-timing-function: cubic-bezier(.97,0,0,1);
        animation-iteration-count: infinite;
    }
    
    #sound--icon .rect-1{
        animation-delay: .1s;
    }
    
    #sound--icon .rect-2{
        animation-delay: .3s;
    }
    
    #sound--icon .rect-3{
        animation-delay: .5s;
    }
    
    #sound--icon .rect-4{
        animation-delay: .7s;
    }
    
    @keyframes sound-icon-anim{
        0%{
            height: 30%;
        }
        10%{
            height: 70%;
        }
        20%{
            height: 50%;
        }
        50%{
            height: 80%;
        }
        60%{
            height: 100%;
        }
        70%{
            height: 70%;
        }
        100%{
            height: 30%;
        }
    }
    <div id="this-button" class="button">
        <svg version="1.1" id="sound--icon" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
        viewBox="0 0 100 100"   >
          <rect class="rect-1 rect-anim" x="10" y="40" width="20" height="20" fill="currentColor"></rect>
          <rect class="rect-2 rect-anim" x="30" y="40" width="20" height="20" fill="currentColor"></rect>
          <rect class="rect-3 rect-anim" x="50" y="40" width="20" height="20" fill="currentColor"></rect> 
          <rect class="rect-4 rect-anim" x="70" y="40" width="20" height="20" fill="currentColor"></rect>
      </svg>
    </div>

    【讨论】:

      【解决方案2】:

      谢谢老兄,我学到了一些东西。最后,我对 JS 中的结果做了一些非常令人满意的事情。当悬停getComputedStyle 时,我计算了矩形的高度,并将其更改为我想要添加过渡的高度。我认为代码可以优化,尤其是setTimeout 部分,它删除了定义动画的 CSS 类。如果它不存在,它会与第四个矩形混淆。

      const svg = document.querySelector('#sound__icon')
      const btn = document.querySelector('.mute')
      const rects = svg.children
      let mute = false
      
      function muting(){
          if(!mute){
              mute = true
            for(let rect of rects){
                  rect.style.height = '30%'
            }
          }
          else{
              mute = false
              audioAnimActivate()
          }
      }
      
      function audioAnimActivate(){
          for(let rect of rects){
              rect.classList.add('rect-anim')
          }
          svg.classList.add('sound__icon--on')
      }
      
      function audioAnimFreeze(){
          for(let rect of rects){
              let rectHeight = window.getComputedStyle(rect).height
              rect.style.height = rectHeight
              rect.classList.remove('rect-anim')
              rect.style.transition = 'height .5s ease'
              rect.style.height = '50%'
          }
          //? How to code this better ???
          setTimeout(() => svg.classList.remove('sound__icon--on'), 16)
      }
      
      btn.addEventListener('click', muting)
      btn.addEventListener('mouseenter', () => {
          if(mute){
              rects[0].style.height = '60%'
              rects[1].style.height = '20%'
              rects[2].style.height = '40%'
              rects[3].style.height = '80%'
          }
          else{
              audioAnimFreeze()
          }
          
      })
      btn.addEventListener('mouseleave', () => {
          if(!mute){
              audioAnimActivate()
          }
          else{
              for(let rect of rects){
                  rect.style.height = '30%'
              }
          }
      }) 
      .mute{
          width: 25vw;
          height: 25vw;
          border: none;
          display: flex;
          justify-content: center;
          align-items: center;
          background: none;
      }
      
      .rect-1, .rect-2, .rect-3, .rect-4{
          width: 10%;
          height: 30%;
      }
      
      .sound__icon--on .rect-anim{
          animation-name: sound-icon-anim;
          animation-duration: 1s;
          animation-timing-function: cubic-bezier(.97,0,0,1);
          animation-iteration-count: infinite;
      }
      
      .sound__icon--on .rect-1{
          animation-delay: .1s;
      }
      
      .sound__icon--on .rect-2{
          animation-delay: .3s;
      }
      
      .sound__icon--on .rect-3{
          animation-delay: .5s;
      }
      
      .sound__icon--on .rect-4{
          animation-delay: .7s;
      }
      
      @keyframes sound-icon-anim{
          0%{
              height: 30%;
          }
          10%{
              height: 70%;
          }
          20%{
              height: 50%;
          }
          50%{
              height: 80%;
          }
          60%{
              height: 100%;
          }
          70%{
              height: 70%;
          }
          100%{
              height: 30%;
          }
      }
          <div class="mute">
              <svg version="1.1" id="sound__icon" class="sound__icon--on"xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
              viewBox="0 0 100 100"   >
                <rect class="rect-1 rect-anim" x="15" fill="currentColor"></rect>
                <rect class="rect-2 rect-anim" x="35" fill="currentColor"></rect>
                <rect class="rect-3 rect-anim" x="55" fill="currentColor"></rect> 
                <rect class="rect-4 rect-anim" x="75" fill="currentColor"></rect>
            </svg>
          </div>

      【讨论】:

        猜你喜欢
        • 2016-06-01
        • 2015-12-10
        • 2015-10-09
        • 2022-10-06
        • 2014-09-28
        • 2013-08-01
        • 2016-06-27
        • 2015-12-26
        • 1970-01-01
        相关资源
        最近更新 更多