在 JS 中滑动 and/or 动画的问题

Issues with swipe and/or animate in JS

它是什么: 我的可滑动表单有问题。它使用 vanilla JS 方法实现滑动识别和 .animate 循环遍历表单的每个部分。

我的问题: 当连续执行太多滑动或连续单击太多下一个按钮时,.animate 似乎会失败并且它们开始堆积在可见区域的中心。

我尝试过的事情: 我试过 stopPropagation() 和 setTimeout(),但都没有帮助。我也试过在函数开始时删除监听器,然后在 nextPrev() 函数的末尾添加它们,这也没有解决问题。

我不确定到底哪里出错了,因此不知道如何解决这个问题。我在不同的阶段尝试过不同的东西,但似乎没有什么可以解决这个问题。我正在寻找一种方法来防止这种情况发生。

对于没有展示 MCVE,我深表歉意,我故意留下了所有内容,以便有人可以体验循环浏览表单并重现问题。

代码:

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <script
  src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js">
  </script>
    <style>
      body {
        align-items: center;
        width: 100%;
        font-size: 2em;
        }
      #form {
        display: flex;
        margin: auto;
        align-items: center;
        text-align: center;
        width: 750px;
        height: 750px;
        border: 1px solid black;
        position: relative;
        overflow: hidden;
        }
      #btns {
        margin: auto;
        text-align: center;
        width: 250px;
        height: 50px;
        }
      #back {
        float: left;
        }
      #next {
        float: right;
        }
      table {
        margin: auto;
        }
      .part:not(:first-child) {
        display: inline-block;
        position: absolute;
        right: 0px;
        left: 5000px;
        }
      .part:first-child {
        display: inline-block;
        position: absolute;
        right: 0px;
        left: 0px;
        }
      input[type=text],input[type=number],input[type=date],input[type=time], select {
        width: 500px;
        padding: 30px 20px;
        margin: 8px 0;
        border: 1px solid Black;
        border-radius: 10px;
        box-sizing: border-box;
        font-size: 1em;
        color: Black;
        }
    input[type=radio] {
        width: 100px;
        height: 100px;
        }
    input[type=checkbox] {
        width: 40px;
        height: 40px;
        }
    </style>
  </head>
  <body>
    <div id="form">
    <div id="0" class="part"><h3>Driver Information</h3></div>
    <div id="1" class="part">Company:<br><input type="text" name="carrier"></div>
    <div id="2" class="part">Station:<br><table>
    <tr><td>PDX:</td><td><input type="radio" id="pdx" name="address"></td></tr>
    <tr><td>EUG:</td><td><input type="radio" id="pdx" name="address"></td></tr>
    <tr><td>SEA:</td><td><input type="radio" id="pdx" name="address"></td></tr>
    <tr><td>SFO:</td><td><input type="radio" id="pdx" name="address"></td></tr></table></div>
    <div id="3" class="part">Name:<br><input type="text" name="name" placeholder="Your Full Name"></div>
    <div id="4" class="part">Employee Number:<br><input type="number" name="employeeNumber" placeholder="Your Employee Number"></div>
    <div id="5" class="part">Date:<br><input type="date" id="date" name="date"></div>
    <div id="6" class="part">Time:<br><input type="time" id="time" name="time"></div>
    <div id="7" class="part"><h3>Vehicle<br>Information</h3></div>
    <div id="8" class="part">Tractor/Truck Number:<br><input type="number" id="tractorTruckNumber" name="tractorTruckNumber"></div>
    <div id="9" class="part">Odometer:<br><input type="number" id="odometer" name="odometer"></div>    
    <div id="10" class="part"><h4><p>Vehicle Inspection<br><small></small></p></h4></div>
    <div id="11" class="part">
    <table>
    <tr><td>Air Compressor:</td><td><input type="checkbox" class="truck" id="aircompressor"></td></tr>
    <tr><td>Air Lines:</td><td><input type="checkbox" class="truck" id="airlines"></td></tr>
    <tr><td>Battery:</td><td><input type="checkbox" class="truck" id="battery"></td></tr>
    <tr><td>Belts and Hoses:</td><td><input type="checkbox" class="truck" id="beltsandhoses"></td></tr>
    <tr><td>Body:</td><td><input type="checkbox" class="truck" id="body"></td></tr>
    </table>
    </div>
    <div id="12" class="part">
    <table>
    <tr><td>Brake Accessories:</td><td><input type="checkbox" class="truck" id="brakeaccessories"></td></tr>
    <tr><td>Brakes, Parking:</td><td><input type="checkbox" class="truck" id="brakesparking"></td></tr>
    <tr><td>Brakes, Service:</td><td><input type="checkbox" class="truck" id="brakesservice"></td></tr>
    <tr><td>Clutch:</td><td><input type="checkbox" id="clutch"></td></tr>
    <tr><td>Coupling Devices:</td><td><input type="checkbox" class="truck" id="couplingdevices"></td></tr>
    </table>
    </div>
    <div id="13" class="part">
    <table>
    <tr><td>Defroster/Heater:</td><td><input type="checkbox" class="truck" id="defrosterheater"></td></tr>
    <tr><td>Drive Line:</td><td><input type="checkbox" class="truck" id="driveline"></td></tr>
    <tr><td>Engine:</td><td><input type="checkbox" class="truck" id="engine"></td></tr>
    <tr><td>Exhaust:</td><td><input type="checkbox" class="truck" id="exhaust"></td></tr>
    <tr><td>Fifth Wheel:</td><td><input type="checkbox" class="truck" id="fifthwheel"></td></tr>
    </table>
    </div>
    <div id="14" class="part">
    <table>
    <tr><td>Fluid Levels:</td><td><input type="checkbox" class="truck" id="fluidlevels"></td></tr>
    <tr><td>Frame and Assembly:</td><td><input type="checkbox" class="truck" id="frameandassembly"></td></tr>
    <tr><td>Front Axle:</td><td><input type="checkbox" class="truck" id="frontaxle"></td></tr>
    <tr><td>Fuel Tanks:</td><td><input type="checkbox" class="truck" id="fueltanks"></td></tr>
    <tr><td>Horn:</td><td><input type="checkbox" class="truck" id="horn"></td></tr>
    </table>
    </div>
    <div id="15" class="part">
    <table>
    <tr><td>Lights:</td><td><input type="checkbox" class="truck" id="lights"></td></tr>
    </table>
    <small><center><p>Head/Stop, Tail/Dash, Turn Indicators, Clearance/Marker</p></center></small>
    </div>
    <div id="16" class="part">
    <table>
    <tr><td>Mirrors:</td><td><input type="checkbox" class="truck" id="mirrors"></td></tr>
    <tr><td>Muffler:</td><td><input type="checkbox" class="truck" id="muffler"></td></tr>
    <tr><td>Oil Pressure:</td><td><input type="checkbox" class="truck" id="oilpressure"></td></tr>
    <tr><td>Radiator:</td><td><input type="checkbox" class="truck" id="radiator"></td></tr>
    <tr><td>Read End:</td><td><input type="checkbox" class="truck" id="rearend"></td></tr>
    <tr><td>Reflectors:</td><td><input type="checkbox" class="truck" id="reflectors"></td></tr> 
    </table>
    </div>
    <div id="17" class="part">
    <table>
    <tr><td>Safety Equipment:</td><td><input type="checkbox" class="truck" id="safetyequipment"></td></tr>
    </table>
    <small><center><p>Fire Extinguisher, Flags/Flares/Fusees, Reflective Triangles, Spare Bulbs/Fuses, Spare Seal Beam</p></center></small>
    </div>
    <div id="18" class="part">
    <table>
    <tr><td>Starter:</td><td><input type="checkbox" class="truck" id="starter"></td></tr>
    <tr><td>Steering:</td><td><input type="checkbox" class="truck" id="steering"></td></tr>
    <tr><td>Suspension System:</td><td><input type="checkbox" class="truck" id="suspensionsystem"></td></tr>
    <tr><td>Tire Chains:</td><td><input type="checkbox" class="truck" id="tirechains"></td></tr>
    <tr><td>Tires:</td><td><input type="checkbox" class="truck" id="tires"></td></tr> 
    <tr><td>Transmission:</td><td><input type="checkbox" class="truck" id="transmission"></td></tr>
    </table>
    </div>
    <div id="19" class="part">
    <table>
    <tr><td>Trip Recorder:</td><td><input type="checkbox" class="truck" id="triprecorder"></td></tr>
    <tr><td>Wheels and Rims</td><td><input type="checkbox" class="truck" id="wheelsandrims"></td></tr>
    <tr><td>Windows</td><td><input type="checkbox" class="truck" id="windows"></td></tr>
    <tr><td>Windshield Wipers:</td><td><input type="checkbox" class="truck" id="windshieldwipers"></td></tr>
    <tr><td>Other:</td><td><input type="checkbox" class="truck" id="other"></td></tr> 
    </table>
    </div>
    <div id="20" class="part">Remarks:<br><input type="text" id="truckremarks"></div>
    <div id="21" class="part"><h3>Trailer Inspection</h3></div>
    <div id="22" class="part">Trailer Number:<br><input type="number" id="trailernumber"><br><h6>(if no trailer, click next)</h6></div>

    </div>
    <div id="btns"><input type="button" id="back" onclick="nextPrev(-1)" value="<< Back"><input type="button" id="next" onclick="nextPrev(1);" value="Next >>"></div>
  </body>
  <script>
  var part = $('.part')
  var currentPart = 0;
  checkButtons();
  var isSwiping = false;

  function startSwipe(){
    if (isSwiping){
      return false;
      }
      else {
      isSwiping = true;
      $('#back, #next').attr('disabled',true);
      return true;
      }
    }
  function endSwipe(){
    $('#back, #next').removeAttr('disabled');
    isSwiping = false;
    }

  function nextPrev(n){
    if (startSwipe()){//this doesn't work when swiping with startSwipe(), but buttons still work
      if (n === 1){
        if (currentPart !== part.length-1){
          $('#'+currentPart).filter(':not(:animated)').animate({'left':'-5000px'},500,function(){
            $(this).css({'left':'-5000px'});//set current left
            });//move current left
          $('#'+(currentPart+n)).delay(200).filter(':not(:animated)').animate({'left':'0'},500,function(){
            $(this).css({'left':'0'});//set next centered
            currentPart = currentPart + n;
            checkButtons();
            endSwipe();
            });//move next right   
          }
        }
      if (n === -1){
        if (currentPart !==0){
          $('#'+currentPart).filter(':not(:animated)').animate({'left':'5000px'},500,function(){
            $('#'+currentPart).css({'left':'5000px'});//set current right
            });//move current left
          $('#'+(currentPart+n)).delay(200).filter(':not(:animated)').animate({'left':'0'},500,function(){
            $('#'+(currentPart+n)).css({'left':'0'});//set next centered
            currentPart = currentPart + n;
            checkButtons();
            endSwipe();
            });//move next right
          }
        }
      }  
    }

  function checkButtons(){
    if (currentPart == 0){
      $('#back').css({'display':'none'});
      }
    else if (currentPart == part.length-1){
      $('#next').css({'display':'none'});
      }
    else {
      $('#back').css({'display':'block'});
      $('#next').css({'display':'block'});
      }
    }

document.getElementById('form').addEventListener('touchstart', handleTouchStart, false);        
document.getElementById('form').addEventListener('touchmove', handleTouchMove, false);
document.getElementById('form').addEventListener('touchend', handleTouchEnd, false);

var xDown = null;                                                        
var yDown = null;

function getTouches(evt) {
  return evt.touches ||             // browser API
         evt.originalEvent.touches; // jQuery
}                                                     

function handleTouchStart(evt) {
  if (startSwipe()){
    const firstTouch = getTouches(evt)[0];                                      
    xDown = firstTouch.clientX;                                      
    yDown = firstTouch.clientY;  
  }
};                                                

function handleTouchMove(evt) {
  if (isSwiping){
    if ( ! xDown || ! yDown ) {
        return;
    }
    var xUp = evt.touches[0].clientX;                                    
    var yUp = evt.touches[0].clientY;

    var xDiff = xDown - xUp;
    var yDiff = yDown - yUp;

    if ( Math.abs( xDiff ) > Math.abs( yDiff ) ) {/*most significant*/
        if ( xDiff > 0 ) {
            nextPrev(1);
        } else {
            nextPrev(-1);
        }                       
    }

    /* reset values */
    xDown = null;
    yDown = null;
  }
};

function handleTouchEnd(evt) {
  endSwipe();
  };
  </script>
</html>

更新:ariel 的解决方案很好,虽然它没有解决我的问题。这种标记很重要,我的代码应该已经配备了它。我仍然遇到问题,尽管我在这里发现了一些有希望的东西:https://css-tricks.com/full-jquery-animations/ 虽然它没有完全解决我的问题,但它已经非常接近解决方案了,我会在有解决方案时更新。

您需要使用标志来防止在动画发生时触发事件:

var isSwiping = false;

function startSwipe() {
  if (isSwiping) {
    return false;
  } else {
    isSwiping = true;
    $("button#back, button#next").attr("disabled", true);
    return true;
  }
}

function endSwipe() {
  $("button#back, button#next").removeAttr("disabled");
  isSwiping = false;
}

function nextPrev(n) {
  if (startSwipe()) {
    ...
      // Inside animate callback:
      endSwipe();
    ...
}

...

document.getElementById('form').addEventListener('touchstart', handleTouchStart, false);        
document.getElementById('form').addEventListener('touchmove', handleTouchMove, false);
document.getElementById('form').addEventListener('touchend', handleTouchEnd, false);

...

function handleTouchStart(evt) {
  if (startSwipe()) {
    ...                                      
  }
};                                                

function handleTouchMove(evt) {
  if (isSwiping) {
    ...
  }
};

function handleTouchEnd(evt) {
  endSwipe();
};