1

I need to animate the function descramble(). So, it changes the first character then moves on the next character and so on. I have tried using setTimeout() but that just delays the launch of the function. I just need to animate the ROT13 conversion letter by letter.

I'm using slice() to remove the first element of the string replacing it with ROT13 then replacing the string with the new letter plus the whole string. I was unable to found a way to only remove the first letter of the paragraph. You can click the third paragraph in the jsfiddle to see the conversion.

$("#last-second-inside p:nth-child(3)").one('click', function() {
  $(this).css('cursor', 'default');
  var str = "Vg'f fvzcyr lbh jvyy whfg nqq nabgure pbyhza nsgre sbhe'f cynpr naq gung pbyhza jvyy unir gur cynpr inyhr bs rvtug'f.";
  var strArr = str.split('');
  var decodedArr = [];
  var increaseNum = -1;
  descramble();

  function descramble() {
    strArr.map(function(num) {
      increaseNum++;
      var currentLetter = num.charCodeAt();
      if (currentLetter >= 65 && currentLetter <= 90 || currentLetter >= 97 && currentLetter <= 122) {
        if (currentLetter >= 78 && currentLetter <= 90 || currentLetter >= 110 && currentLetter >= 97) {
          decodedArr.push(String.fromCharCode(currentLetter - 13));
        } else {
          decodedArr.push(String.fromCharCode(currentLetter + 13));
        }
      } else {
        decodedArr.push(num);
      }
      var sliced = str.slice(increaseNum + 1, str.length - 1);
      $("#last-second-inside p:nth-child(3)").text(decodedArr.join('') + sliced);
    })
  }
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="last-second-inside">
  <p>You probably now understand that how binary works and how simple it is.</p>
  <p>Now, I need you to think about that how you might display 8 in binary. After giving it a shot, you can click on the next paragraph to descramble it.</p>
  <p>Vg'f fvzcyr lbh jvyy whfg nqq nabgure pbyhza nsgre sbhe'f cynpr naq gung pbyhza jvyy unir gur cynpr inyhr bs rvtug'f.</p>
</div>
Lavios
  • 1,169
  • 10
  • 22
  • `window.setInterval(func, delay)` calls the function repetatedly infinitely while `window.setTimeout(func, delay)` calls it only once. You could add `setTimeout(func, delay)` to the `descrable` function after a condition if you don't want infinite calls. – BoltKey Jan 12 '16 at 23:04

4 Answers4

1

Instead of using map(), let descramble() work on only one letter at a time, based on the value of increaseNum.

After descramble() has run, use setTimeout() to call it again after an appropriate interval:

$("#last-second-inside p:nth-child(3)").one('click', function() {
  $(this).css('cursor', 'default');
  var str = "Vg'f fvzcyr lbh jvyy whfg nqq nabgure pbyhza nsgre sbhe'f cynpr naq gung pbyhza jvyy unir gur cynpr inyhr bs rvtug'f.";
  var strArr = str.split('');
  var decodedArr = [];
  var increaseNum = 0;  // changed from -1
  descramble();

  function descramble() {
    var num = str[increaseNum++],
        currentLetter = num.charCodeAt();

    if (currentLetter >= 65 && currentLetter <= 90 || currentLetter >= 97 && currentLetter <= 122) {
      if (currentLetter >= 78 && currentLetter <= 90 || currentLetter >= 110 && currentLetter >= 97) {
        decodedArr.push(String.fromCharCode(currentLetter - 13));
      } else {
        decodedArr.push(String.fromCharCode(currentLetter + 13));
      }
    } else {
      decodedArr.push(num);
    }
    var sliced = str.slice(increaseNum + 1, str.length - 1);
    $("#last-second-inside p:nth-child(3)").text(decodedArr.join('') + sliced);
    if(increaseNum < str.length) {
      setTimeout(descramble, 10);
    }
  }
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="last-second-inside">
  <p>You probably now understand that how binary works and how simple it is.</p>
  <p>Now, I need you to think about that how you might display 8 in binary. After giving it a shot, you can click on the next paragraph to descramble it.</p>
  <p>Vg'f fvzcyr lbh jvyy whfg nqq nabgure pbyhza nsgre sbhe'f cynpr naq gung pbyhza jvyy unir gur cynpr inyhr bs rvtug'f.</p>
</div>
Rick Hitchcock
  • 35,202
  • 5
  • 48
  • 79
1

Try using .queue()

$("#last-second-inside p:nth-child(3)").one('click', function() {
  $(this).css('cursor', 'default');
  var str = "Vg'f fvzcyr lbh jvyy whfg nqq nabgure pbyhza nsgre sbhe'f cynpr naq gung pbyhza jvyy unir gur cynpr inyhr bs rvtug'f.";
  var strArr = str.split('');
  var decodedArr = [];
  var increaseNum = -1;
  descramble();

  function descramble() {
    $({}).queue("descramble", strArr.map(function(num) {
      return function(next) {
        increaseNum++;
        var currentLetter = num.charCodeAt();
        if (currentLetter >= 65 && currentLetter <= 90 || currentLetter >= 97 && currentLetter <= 122) {
          if (currentLetter >= 78 && currentLetter <= 90 || currentLetter >= 110 && currentLetter >= 97) {
            decodedArr.push(String.fromCharCode(currentLetter - 13));
          } else {
            decodedArr.push(String.fromCharCode(currentLetter + 13));
          }
        } else {
          decodedArr.push(num);
        }
        var sliced = str.slice(increaseNum + 1, str.length - 1);
        $("#last-second-inside p:nth-child(3)").text(decodedArr.join('') + sliced);
        // set duration of `setTimeout` here
        setTimeout(next, 100)
      };
    })).dequeue("descramble")
  }
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="last-second-inside">
  <p>You probably now understand that how binary works and how simple it is.</p>
  <p>Now, I need you to think about that how you might display 8 in binary. After giving it a shot, you can click on the next paragraph to descramble it.</p>
  <p>Vg'f fvzcyr lbh jvyy whfg nqq nabgure pbyhza nsgre sbhe'f cynpr naq gung pbyhza jvyy unir gur cynpr inyhr bs rvtug'f.</p>
</div>
guest271314
  • 1
  • 15
  • 104
  • 177
0

use setInterval, not setTimeout. setTimeout will only run once (after the timer expires). setInterval will keep firing at the interval you set until you escape out of the loop.

You can consider setTimeout as a single trigger countdown timer. setInterval can be viewed as a loop that triggers at the interval you set.

Korgrue
  • 3,430
  • 1
  • 13
  • 20
0

I believe you are going to have to do the mapping manually. You cannot use setTimeout or setInterval inside of an iterator like each, map, etc. and get the results you are expecting because setTimeout is non-blocking, so it returns immediately. You'll have to call a self-invoking function that converts 1 letter at a time and has break logic when you reach the end of the string like so: How do I add a delay in a JavaScript loop?

Community
  • 1
  • 1
mhodges
  • 10,938
  • 2
  • 28
  • 46
  • You place the interval timer outside of the function it calls and use it to call the function in question. The iterator becomes the function that is being called each interval. Use a counter and break out of the loop by returning false when all the letters have been moved. OP has to rewrite some code, but setInterval will work fine in this use case. – Korgrue Jan 12 '16 at 23:33
  • Simple setInterval does not work for this case. The accepted answer was exactly what I was saying, a self-invoking function that converts 1 letter at a time - I just did not have the time to code it out for the OP – mhodges Jan 12 '16 at 23:42