繁体   English   中英

Javascript 添加/删除 animation class,仅动画一次

[英]Javascript add/remove animation class, only animates once

试图构建一个时间选择器,它应该动画输入的变化。 我想我会通过从 CSS 添加和删除 animation class 来做到这一点。 问题是,它只在第一次工作。

I tried to set an interval to start with, that removed the class after a certain amount of time, slightly above the animation duration, but that reduced the animation time of the animation with each click, and by the third or fourth click, it no更长的动画。

然后我只是选择检查 class 是否存在开始,删除它开始,然后添加它。 确保每次都从头开始。 但现在它只在第一次动画。

密码笔

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>

(这里的数据属性目前没有使用)

SCSS:


    $finger: 2cm;
    $smallfinger: .3cm;

    #wrapper {
      display: grid;
      grid-template-columns: $finger $finger $finger;
      grid-template-rows: $finger $finger $finger;
      grid-column-gap: $smallfinger;
      grid-row-gap: $smallfinger;
      position:absolute;
      width: 100vw;
      background: #FF00FF;
      height: calc( (#{$finger} * 3) + (#{$smallfinger} * 4) );
      box-sizing: border-box;
      bottom:$finger;
      left:0;
      padding:0;
      margin:0;
      align-content: center;
      justify-content: center;
      }

    #uphour,
    #upminute,
    #upampm,
    #downhour,
    #downminute,
    #downampm {
      display: flex;
      background: rgba(255,0,0,.4);
      width: $finger;
      height: $finger;
      margin-left: 1vw;
      margin-right: 1vw;
      border: 1px solid black;
    }

    .animated {
      display: flex;
      width: $finger;
      height: $finger;
      margin-left: 1vw;
      margin-right: 1vw;
      border: 1px solid black;
      overflow:hidden;
    }


    #minute, #hour, #ampm {
      display: flex;
      /*background: rgba(255,0,0,.4); for testing of animation only*/
      width: $finger;
      height: $finger;
      border: 0;
      position: relative;
      align-items: center;
      justify-content: center;
      font-size: .8cm;
    }

    .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);
    }

    /* Safari 4.0 - 8.0 */
    @-webkit-keyframes downandout {
      0%   {top: 0; left: 0;}
      10%  {top: 100%; left:0;}
      50%  {top: 100%; left: 100%;}
      90% {top: -100%; left:100%;}
    }

    /* Standard syntax */
    @keyframes downandout {
      0%   {top: 0; left: 0;}
      40%  {top: 100%; left:0;}
      42%  {top: 100%; left: calc(#{$finger} * 2);}
      48% {top: -110%; left: calc(#{$finger} * 2);}
      50% {top: -110%; left:0;}
      100% {top: 0; left:0;}
    }

Javascript:


    var uphourEl = document.getElementById("uphour");
    var downhourEl = document.getElementById("downhour");
    var upminuteEl = document.getElementById("upminute");
    var downminuteEl = document.getElementById("downminute");
    var upampmEl = document.getElementById("upampm");
    var downampmEl = document.getElementById("downampm");

    uphourEl.addEventListener("click", increaseHour);
    downhourEl.addEventListener("click", decreaseHour);
    upminuteEl.addEventListener("click", increaseMinute);
    downminuteEl.addEventListener("click", decreaseMinute);
    upampmEl.addEventListener("click", swapAMPM);
    downampmEl.addEventListener("click", swapAMPM);

    var hourEl = document.getElementById("hour"); 
    var minuteEl = document.getElementById("minute"); 
    var ampmEl = document.getElementById("ampm"); 

    function increaseHour() {
      let value = hourEl.innerHTML;
      if (value > 11) {
          value = 1
      } else  {
        value++;
      }
      hourEl.innerHTML = value;
      console.log(value);
    }

    function decreaseHour() {
      let value = hourEl.innerHTML;
      if (value < 2) {
          value = 12
      } else  {
        value--;
      }
      hourEl.innerHTML = value;
      console.log(value);
    }

    function increaseMinute() {
      let value = minuteEl.innerHTML;
      if (value > 58) {
          value = 0
      } else  {
        value++;
      }
      minuteEl.innerHTML = value;
      console.log(value);
    }

    function decreaseMinute() {
      let value = minuteEl.innerHTML;
      if (value < 1) {
          value = 59
      } else  {
        value--;
      }
      minuteEl.innerHTML = value;
      console.log(value);
    }

    function swapAMPM() {
      let value = ampmEl.innerHTML;
      if (ampmEl.hasAttribute("class")) {
        ampmEl.removeAttribute("class");
      }
      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");
      }
    }

..这是 swapAMPM function (在 Javascript 的末尾)我目前没有按预期行事。

关于我为什么会遇到这种行为的任何建议? 我缺少一些最佳实践吗?

我想这就是你在香草中会做的事情

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

以下是一种简化的方法:

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

更新:

  • 包括去抖动以停止多次单击同一按钮
  • 调整超时(添加超时以使值发生变化,从而产生数字实际旋转的错觉,AnimationEvent 无法检测到您是否已完成 animation 的一半。
<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

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

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);
}

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

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

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

首先,我们需要一个 function 来检测浏览器支持哪个 animation 迭代事件:

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中删除 class ,如下所示:

ampmEl.addEventListener(whichAnimationEvent(),function(){
  console.log('ampmEl event listener fired')
  ampmEl.classList.remove('animateStart');
});

接下来,我们将swapAMPM function 更改为使用元素classListadd方法在执行交换之前添加 class,使其具有动画效果。

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);
}

完整的工作示例

密码笔

在您的 function swapAMPM中的if statement ,您尝试删除一个属性,这不会引起太大的影响,因为您立即将其设置回来..

if (ampmEl.hasAttribute("class")) {
  ampmEl.removeAttribute("class");
}
ampmEl.setAttribute("class", "animateStart")

快速解决方法是在 animation 完成后立即设置超时以删除 class:

function swapAMPM() {
  let value = ampmEl.innerHTML;
  ampmEl.classList.add("animateStart");
  setTimeout(() => { 
    ampmEl.classList.remove("animateStart") 
  }, 260)
  if (value === "AM") {
      value = "PM";
      ampmEl.innerHTML = value;
      console.log("Changed from AM");
  } else  {
    value = "AM";
    ampmEl.innerHTML = value;
    console.log("Changed from PM");
  }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM