为了账号安全,请及时绑定邮箱和手机立即绑定

Javascript添加/删除动画类,只动画一次

Javascript添加/删除动画类,只动画一次

千万里不及你 2021-11-04 15:08:42
试图构建一个时间选择器,它应该为输入的变化设置动画。我想我会通过从 CSS 中添加和删除动画类来做到这一点。问题是,它只在第一次有效。我尝试设置一个间隔开始,在一定时间后删除类,略高于动画持续时间,但是每次单击都会减少动画的动画时间,并且通过第三次或第四次单击,它没有更长的动画。然后我只是选择检查该类是否存在,删除它,然后添加它。确保每次都从头开始。但现在它只是第一次动画。HTML:    <div id="wrapper">      <div id="uphour"></div>      <div id="upminute"></div>      <div id="upampm"></div>      <div class="animated">        <div id="hour" data-hour="1">2</div>      </div>      <div class="animated">        <div id="minute" data-minute="1">50</div>      </div>      <div class="animated">        <div id="ampm" data-minute="AM">AM</div>      </div>      <div id="downhour"></div>      <div id="downminute"></div>      <div id="downampm"></div>      </div>..它是 swapAMPM 函数(在 Javascript 的末尾)我目前没有按预期行事。关于为什么我会遇到这种行为的任何建议?我缺少一些最佳实践吗?
查看完整描述

3 回答

?
MM们

TA贡献1886条经验 获得超2个赞

我想这就是你在香草中的做法


gugateider 是对的,你需要一个 setTimout,有 AnimationEvent API 但它不完全支持。


下面是一个简化的方法:


不需要事件监听器或收集 ID


更新:

包括去抖动以停止对同一按钮的多次点击

调整超时(添加超时以便值在视图外发生变化以产生数字实际旋转的错觉,AnimationEvent 无法检测您是否已完成动画。)

<div class="wrapper">


  <div id="hour" class="unit">

    <div class="plus button" onClick="pressedButton(event);">+</div>

    <div class="value"><div>10</div></div>

    <div class="minus button" onClick="pressedButton(event);">-</div>

  </div>


    <div id="minute" class="unit">

    <div class="plus button"  onClick="pressedButton(event);">+</div>

      <div class="value"><div>36</div></div>

    <div class="minus button"  onClick="pressedButton(event);">-</div>

  </div>


    <div id="meridiem" class="unit">

    <div class="plus button"  onClick="pressedButton(event);">+</div>

      <div class="value"><div>AM</div></div>

    <div class="minus button"  onClick="pressedButton(event);">-</div>

  </div>




</div>


.wrapper {

  display: flex;

  flex-flow: row no-wrap;

  justify-content: space-between;

  align-items: center;

  width: 200px;

  height: 200px;

  margin: 100px auto;

  background: red;

  padding: 20px;

}


.unit {

  display: flex;

  flex-flow: column;

  justify-content: space-between;

  align-items: space-between;

  height: 100%;

  position: relative;

}


.button {

  border: 2px solid black;

  height: 50px;

  width: 50px;

  cursor: pointer;

  display: flex;

  justify-content: center;

  align-items: center;

  background: grey;


  &:hover {

    opacity: 0.8;

  }


}



.value {

  border: 2px solid black;

  height: 50px;

  width: 50px;

  font-family: sans-serif;

  font-size: 20px;

  display: flex;

  justify-content: center;

  align-items: center;

  background: lightgrey;

    overflow: hidden;

}


.animate {

  top: 0;

  position: relative;

  overflow: hidden;

  animation-name: downandout;

  animation-duration: 1s;

  animation-iteration-count: 1;

  transition-timing-function: ease-in-out;


  &--reverse {

    animation-direction: reverse;

  }

}





 @keyframes downandout {

   0%  {top: 0}

   50% {top: 50px}

   51% {top: -50px}

   100%  {top: 0}

}

debounce = function(func, wait, immediate) {

  var timeout;

  return function() {

    var context = this,

      args = arguments;

    var later = function() {

      timeout = null;

      if (!immediate) func.apply(context, args);

    };

    var callNow = immediate && !timeout;

    clearTimeout(timeout);

    timeout = setTimeout(later, wait);

    if (callNow) func.apply(context, args);

  };

}


pressedButton = function($event) {

  $event.preventDefault();


  // debouncedFn to stop multiple clicks to the same button

  var debouncedFn = debounce(function() {

    // All the taxing stuff you do

    const target = $event.target;

    const elm = target.parentNode.children[1];


    let direction;


    // clone the element to restart the animation

    const newone = elm.cloneNode(true);

    // add the first animate

    newone.children[0].classList.add("animate");


    // What button was pressed

    if (target.classList.contains("minus")) {

      direction = "down";

    } else {

      direction = "up";

    }


    // direction of animation

    if (direction === "down") {

      newone.children[0].classList.add("animate--reverse");

    } else {

      newone.children[0].classList.remove("animate--reverse");

    }


    // add the new element to the DOM

    elm.parentNode.replaceChild(newone, elm);


    // change value after half of the animation has completed

    setTimeout(function() {

      switch (target.parentNode.id) {

        case "hour":

        case "minute":

          if (direction === "down") {

            newone.children[0].innerText--;

          } else {

            newone.children[0].innerText++;

          }

          break;


        case "meridiem":

          if (newone.children[0].innerText === "PM") {

            newone.children[0].innerText = "AM";

          } else {

            newone.children[0].innerText = "PM";

          }

      }

    }, 100);

  }, 250);


  // Call my function

  debouncedFn();


};

https://codepen.io/eddy14u/pen/VwZJmdW


查看完整回答
反对 回复 2021-11-04
?
明月笑刀无情

TA贡献1828条经验 获得超4个赞

您可以使用动画迭代事件侦听器来检测动画何时结束,并正确删除您的类。为此,动画迭代必须设置为无限。在我的更改中,我还利用classList元素的属性从元素中添加/删除类。


注意:如果没有至少 1 次迭代,动画迭代事件将永远不会触发,因此代码将无法运行!


编辑:浏览器支持classList是最近的,所以如果你需要支持旧浏览器,你可以回退到不同的解决方案,或者添加一个classList polyfill


首先,我们需要一个函数来检测浏览器支持哪个动画迭代事件:


function whichAnimationEvent(){

  var el = document.createElement("fakeelement");


  var animations = {

    "animation"      : "animationiteration",

    "OAnimation"     : "oAnimationIteration",

    "MozAnimation"   : "animationiteration",

    "WebkitAnimation": "webkitAnimationIteration"

  };


  for (let t in animations){

    if (el.style[t] !== undefined){

      return animations[t];

    }

  }

}

接下来,我们需要为该事件添加一个事件侦听器,当迭代触发时,我们从元素的 中删除该类classList,如下所示:


ampmEl.addEventListener(whichAnimationEvent(),function(){

  console.log('ampmEl event listener fired')

  ampmEl.classList.remove('animateStart');

});

接下来,我们将swapAMPM函数更改为在执行交换之前使用add元素的方法classList添加类,使其具有动画效果。


function swapAMPM() {

  let value = ampmEl.innerHTML;

  ampmEl.classList.add('animateStart');

  if (value === "AM") {

      value = "PM";

      ampmEl.innerHTML = value;

      console.log("Changed from AM");

  } else  {

    value = "AM";

    ampmEl.innerHTML = value;

    console.log("Changed from PM");

  }

}

最后,我们需要更新 css 以进行infinite动画迭代,以便触发我们的事件。


.animateStart {

  -webkit-animation-name: downandout; /* Safari 4.0 - 8.0 */

  animation-name: downandout;

  -webkit-animation-duration: 250ms; /* Safari 4.0 - 8.0 */

  animation-duration: 250ms;

  -webkit-animation-iteration-count: infinite;

  animation-iteration-count: infinite;

  transition-timing-function: cubic-bezier(1,0,0,1);

}


查看完整回答
反对 回复 2021-11-04
?
饮歌长啸

TA贡献1951条经验 获得超3个赞

我知道 setTimeout 不是在那里使用的最佳选择,但是由于您已经完成了很多工作,您可以尝试用以下代码替换 swapAMPM 函数:


function swapAMPM() {

 let value = ampmEl.innerHTML;

 if (ampmEl.hasAttribute("class")) {

     ampmEl.removeAttribute("class");

 }

 setTimeout( () => { 

     ampmEl.setAttribute("class", "animateStart");

     if (value === "AM") {

      value = "PM";

      ampmEl.innerHTML = value;

      console.log("Changed from AM");

     } else  {

        value = "AM";

        ampmEl.innerHTML = value;

        console.log("Changed from PM");

    } 

 }, 150);

}


查看完整回答
反对 回复 2021-11-04
  • 3 回答
  • 0 关注
  • 523 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信