首页 > 解决方案 > Javascript添加/删除动画类,只动画一次

问题描述

试图构建一个时间选择器,它应该动画输入的变化。我想我会通过在 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>

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

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 函数(在 Javascript 的末尾),我目前无法按预期运行。

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

标签: javascriptcssanimation

解决方案


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

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


推荐阅读