0

there is a weird behavior that I don't understand when I use the animate function inside the setInterval.

here is the code:

//task2
let interv;
var direction = 1;
$('input:eq(1)').click(function () {
  let input = $(this);
  var image = $('img');
  if (input.val() === 'Animate') {
    input.val('Stop');

    interv = setInterval(function () {
      if (image.position().left >= 398) {
        direction = -1;
      } else if (image.position().left <= 0) {
        direction = 1;
      }
      image.animate({ left: '+=' + 10 * direction }, 20);
    }, 100);
  } else if (input.val() === 'Stop') {
    input.val('Animate');
    clearInterval(interv);
    image.stop();
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>JQuery</title>
    <script src="jquery-3.6.4.js"></script>
    <script src="Lab.js" defer></script>
    <style>
      .container1 {
        margin: auto;
        margin-bottom: 100px;
        width: fit-content;
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
      }
      #div1 {
        border: 2px solid black;
        width: 500px;
        height: 200px;
        display: inline-block;
        margin-bottom: 20px;
      }
      #div2 {
        border: 2px solid black;
        width: 500px;
        height: 200px;
        display: inline-block;
      }
      #div3 {
        border: 2px solid black;
        width: 500px;
        height: 200px;
        display: inline-block;
        position: relative;
      }

      img {
        border: 1px solid black;
        position: absolute;
      }
    </style>
  </head>

  <body>
    <div class="container1">
      <div><h1>Task 1</h1></div>
      <div id="div1">
        <ul>
          <li>OS</li>
          <li>SD</li>
          <li>Java</li>
          <li>Elearning</li>
        </ul>
      </div>

      <div id="div2">
        <ul></ul>
      </div>
      <input
        style="margin-left: 400px; background-color: blueviolet"
        type="button"
        value=">>"
      />
    </div>

    <div class="container1">
      <div><h1>Task 2</h1></div>
      <div id="div3">
        <img
          style="padding-top: 50px"
          src="facebook.png"
          alt=""
          width="100"
          height="100"
        />
      </div>

      <input
        style="margin-left: 50px; background-color: lime"
        type="button"
        value="Animate"
      />
    </div>
  </body>
</html>

look at task 2, the animate button starts an interval, the image will move until it reaches the end of the div then move backward. the animation button (now is stop) when clicked again stops the interval. the code is working fine at this point.

if you change the time of the animation to 1 millisecond and the time of the set interval to 3 milliseconds the code crashes. which is weird because the logic didn't even change.

can anyone explain this behavior or tell me where should I search for this.

KarimMaged
  • 19
  • 4
  • 1
    Relevant: [setInterval not accurate](https://stackoverflow.com/questions/23981382/setinterval-delays-not-accurate/23981436#23981436) (and other, similar questions on SO). You're overloading the setInterval. – freedomn-m Mar 31 '23 at 14:52
  • Separately, if your animation duration (20) is longer than the setTimeout (eg 10) then you're queuing multiple animations unless you add a `.stop()` - so if you set the timeout to 10, then click start, when you click stop it doesn't stop for some time. Equally, because you determined whether to go left/right *before* the animation starts (rather than animate to a specific position) it goes out of bounds in both directions. – freedomn-m Mar 31 '23 at 14:56
  • 2
    You should use the animation [complete](https://api.jquery.com/animate/) callback and a [recursive function](https://developer.mozilla.org/en-US/docs/Glossary/Recursion). Discard setInterval. – Louys Patrice Bessette Mar 31 '23 at 14:56
  • Removed it .... – KarimMaged Mar 31 '23 at 14:56

1 Answers1

1

The interval timing is getting screwed up. In my answer I replaced the Interval by using the animation's complete function. Inside that function I check the current position to determine if the slide function should get ran again or switched so it slides back.

function slideLeft(target) {
    target.animate({left: "-=10"}, 100, "swing", () => {
      if(target.position().left == 0){slideRight(target)}
      else{slideLeft(target)}
    });
}
function slideRight(target) {
    let maxRight = target.parent().width() - target.width();
    target.animate({left: "+=10"}, 100, "swing", () => {
      if(target.position().left >= maxRight){slideLeft(target)}
      else{slideRight(target)}
    });
}


let interv;
var direction = 1;
$('input:eq(1)').click(function () {
  let input = $(this);
  var image = $('img');
  if (input.val() === 'Animate') {
    input.val('Stop');
    slideRight(image);
  } else if (input.val() === 'Stop') {
    input.val('Animate');
    image.stop();
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>JQuery</title>
    <script src="jquery-3.6.4.js"></script>
    <script src="Lab.js" defer></script>
    <style>
      .container1 {
        margin: auto;
        margin-bottom: 100px;
        width: fit-content;
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
      }
      #div1 {
        border: 2px solid black;
        width: 500px;
        height: 200px;
        display: inline-block;
        margin-bottom: 20px;
      }
      #div2 {
        border: 2px solid black;
        width: 500px;
        height: 200px;
        display: inline-block;
      }
      #div3 {
        border: 2px solid black;
        width: 500px;
        height: 200px;
        display: inline-block;
        position: relative;
      }

      img {
        border: 1px solid black;
        position: absolute;
      }
    </style>
  </head>

  <body>
    <div class="container1">
      <div><h1>Task 1</h1></div>
      <div id="div1">
        <ul>
          <li>OS</li>
          <li>SD</li>
          <li>Java</li>
          <li>Elearning</li>
        </ul>
      </div>

      <div id="div2">
        <ul></ul>
      </div>
      <input
        style="margin-left: 400px; background-color: blueviolet"
        type="button"
        value=">>"
      />
    </div>

    <div class="container1">
      <div><h1>Task 2</h1></div>
      <div id="div3">
        <img
          style="padding-top: 50px"
          src="facebook.png"
          alt=""
          width="100"
          height="100"
        />
      </div>

      <input
        style="margin-left: 50px; background-color: lime"
        type="button"
        value="Animate"
      />
    </div>
  </body>
</html>
imvain2
  • 15,480
  • 1
  • 16
  • 21