如何在每次迭代之间有间隔的循环中为DOM元素设置动画?

时间:2016-12-22 12:00:00

标签: javascript

我有一个函数foo,我想添加一个睡眠/等待函数来制作一种DOM元素动画。我已经做了一些研究,我知道暂停一个javascript函数是不可能的,因为它会冻结浏览器 - 如果我错了,请纠正我。我怎样才能克服它?

function foo() {     
 while (someCondition) {
  var $someDiv = $('.someDiv:nth-child(' + guess + ')');
  $someDiv.css({'background-color': 'red'});
  wait 1000ms
  $someDiv.css({'background-color': 'blue'});
  wait 1000ms
  if (someCondition2) { 
   doSomething; }
  else {
   for loop }
 }
}

$someDiv引用每个while循环迭代的不同DOM元素,因为变量guess更改

我尝试了什么

  • 我使用下面的函数并且它有效,但问题是我无法在异步函数foo中使用for循环

    function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
    }
    
  • 我尝试setTimeout,但我无法获得任何有效结果。

    如果我将这段代码包裹在setTimeout中: ('$someDiv').css({'background-color': 'red'});然后在指定的时间后,所有$someDiv's一起更改css样式(请记住,$someDiv引用每个while循环迭代的不同DOM元素)。

    如果我使用setTimeoutif语句将else包含在一段代码中,那么我就会收到错误 - 无限循环

问题

简化foo功能只是为了可视化问题。我正在处理的原始函数可以在codepenfindNumber函数)

上找到

我想制作二进制搜索算法动画。类似于this

的东西

如何达到预期效果?

一般情况下:如何在每次迭代之间有间隔的循环中为DOM元素设置动画?

5 个答案:

答案 0 :(得分:2)

您可能想要检查jQuery queue()fiddle):

$("div#someDiv")
  .queue(function() {
    console.log('step 1');
    $(this).css({
      'background-color': 'blue'
    }).dequeue();
  })
  .delay(800)
  .queue(function() {
    console.log('step 1');
    $(this).css({
      'background-color': 'red'
    }).dequeue();
  })

你也可以玩Settimeout(fiddle):

var steps = [
  function() {
    $('#someDiv').css({
      'background-color': 'red'
    });
  },
  function() {
    $('#someDiv').css({
      'background-color': 'orange'
    });
  },
  function() {
    $('#someDiv').css({
      'background-color': 'yellow'
    });
  },
  function() {
    $('#someDiv').css({
      'background-color': 'green'
    });
  },
  function() {
    $('#someDiv').css({
      'background-color': 'blue'
    });
  }
];


(function(count) {
  if (count < 5) {
    steps[count]();
    var caller = arguments.callee;
    window.setTimeout(function() {
      caller(count + 1);
    }, 1000);
  }
})(0);

答案 1 :(得分:2)

这个问题最好,最干净的解决方案是async/await feature,它将出现在未来版本的Javascript(ES2017)中。这可以让你摆脱回调地狱。您可以创建一个简单的sleep函数,如下所示:

function sleep(time) {
  return new Promise(resolve => setTimeout(()=>resolve(), time));
}

您可以使用正常的Promise处理:

sleep(1000).then(()=>console.log('A second later'));

但是,使用async功能,您可以使用await关键字使代码等待承诺在继续之前得到解决。

async function doSomething() {
  await sleep(1000);
  console.log('A second later');
}

这意味着您也可以使用普通循环,包括breakcontinue语句:

async function doSomething() {
  let i = 0;
  while (true) {
    await sleep(1000);
    console.log(i);
    if (++i === 5) break;
  }
}

这意味着您的代码可以大大简化:

async function foo() {
  var n = 5;
  while (n > 0) {
    n--;
    var wait = 0;
    //$('#someDiv').css({'background-color': 'red'});
    console.log('doSomething-1');
    //wait 1000ms
    await sleep(1000);
    //$('#someDiv').css({'background-color': 'blue'});
    console.log('doSomething-2');

    //wait 1000ms
    await sleep(1000);
    if (true) {
      console.log('doSomething-3');
      break;
    } else {
      console.log('loop')
    }
  }
}

jsFiddle

唯一的问题是此功能远非普遍支持。因此,您需要使用Babel等软件进行转换。

另请注意,在幕后,您的foo功能现在立即返回并提供Promise。使用函数的返回值解析Promise。因此,如果您希望在foo完成后执行更多代码,则必须执行foo().then(/*callback*/)

答案 2 :(得分:1)

您可以使用jQuery queue()dequeue()delay()

来执行此操作

但是,由于你有closures inside loop,你必须使用bind()将每次迭代的变量绑定到排队的函数中。

这里有body的队列,每个guess都有一个队列,每个猜测的队列都在队列出列队列中出队。循环内部的循环。恕我直言,可以有更简单的方法,这只是为了对队列,延迟和绑定提出一个想法。

我对您的代码进行了一些小的更改,只是为了让演示工作。

$(document).ready(function() {
  var body = $('body');
  //buttons click handler functions
  $('#generateButton').on("click", generateArray);
  $('#findButton').on("click", findNumber);
  //enable calling functions by 'enter' key
  $('#generateInput').keypress(function(e) {
    if (e.which == 13) {
      generateArray();
    }
  });
  $('#findInput').keypress(function(e) {
    if (e.which == 13) {
      findNumber();
    }
  });

  //functions
  function generateArray() {
    //variables
    var $generateGroup = $('.generate');
    var $generateInput = $('#generateInput');
    var $generateInputVal = $generateInput.val();
    var $generateButton = $('#generateButton');
    var $findInput = $('#findInput');
    var $findButton = $('#findButton');
    var $arraySection = $('.array-section');
    //validation
    if ($.isNumeric($generateInputVal) && $generateInputVal >= 10 && $generateInputVal <= 100) {
      //set styling if success
      $generateGroup.removeClass('has-error');
      $generateButton.removeClass('error');
      $generateGroup.addClass('has-success');
      $generateButton.addClass('success');
      //disable generate input group
      $generateInput.prop('disabled', true);
      $generateButton.prop('disabled', true);
      //enable find input group
      $findInput.prop('disabled', false);
      $findButton.prop('disabled', false);
      //clear array section
      $arraySection.empty();
      //generate array = create divs and append them to array section
      for (var i = 0; i < $generateInputVal; i++) {
        var $number = $('<div>', {
          'class': 'number'
        });
        $arraySection.append($number.text(i + 1));
      }
    } else {
      // set styling if error
      $generateGroup.removeClass('has-success');
      $generateButton.removeClass('success');
      $generateGroup.addClass('has-error');
      $generateButton.addClass('error');
    }
  }




  function findNumber() {
    //variables
    var animationSpeed = 5000;
    var animationCut = 1000;
    var $generateInput = $('#generateInput');
    var $generateInputVal = $generateInput.val();
    var $findInput = $('#findInput');
    var $findInputVal = $findInput.val();
    var min = 0;
    var max = parseInt($generateInputVal);
    var guess;
    var n = 0;
    var guesses = [];
    var rejected;
    // --- binary search loop ---
    while (max >= min) {
      n++;
      //compute guess as the average of max and min
      guess = Math.ceil((min + max) / 2);
      console.log("GUESS",guess);
      //guessed number animation
      var $guessNumber = $('.number:nth-child(' + guess + ')');
      console.log(min + ' ' + max + ' ' + guess);
      $guessNumber.queue('guessNum', function(next) {
        $(this).css({
          'background-color': '#000000',
          'color': '#ffffff',
          'font-weight': 'bold'
        });
        next();
      });
      $guessNumber.delay(animationCut, 'guessNum');
      //await sleep(animationSpeed);
      //var myVar = setInterval(function(){
      $guessNumber.queue('guessNum', function(next) {
        $(this).css({
          'background-color': 'white',
          'color': 'black',
          'border': '3px solid #000000'
        });
        next()
      });
      $guessNumber.delay(animationCut, 'guessNum');
      
      body.queue('guessNumbers', function(){
        console.log('guessNumbers');
        $(this).dequeue('guessNum');
        body.dequeue('guessNumbers');
      }.bind($guessNumber));
      //await sleep(animationSpeed);
      //if guessed number equals find number then stop
      if (guess === parseInt($findInputVal)) {
        //found number animation
        $guessNumber.queue('guessNum', function(next) {
          console.log('GOT RESULT', this);
          $(this).css({
            'color': '#3c763d',
            'background-color': '#dff0d8',
            'border': '3px solid #3c763d'
          });
          next();
        });
        body.dequeue('guessNumbers');
        break;
      }
      //if guessed nsumber is to low, set new min value
      else if (guess < parseInt($findInputVal, 10)) {
        
        rejected = $('.number').slice(min, guess);
        min = guess + 1;
      }
      //if guessed number is to high, set new max value
      else if(guess > parseInt($findInputVal, 10)) {
        rejected = $('.number').slice(guess, max);
        max = guess - 1;
      }
      body.queue('guessNumbers',function(){
        console.log("rejected",rejected);
        this.css({backgroundColor: 'red'});
        body.dequeue('guessNumbers');
      }.bind(rejected)).delay(animationSpeed, 'guessNumbers');
    }
  }
});

//   function sleep(ms) {
//   return new Promise(resolve => setTimeout(resolve, ms));
// }
html,
body {
  margin: 0 auto;
  box-sizing: border-box;
}
.section {
  margin-top: 40px;
  margin-bottom: 40px;
}
.array-section {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-wrap: wrap;
}
.input-group {
  margin: 5px;
}
.number {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 40px;
  height: 40px;
  text-align: center;
  margin: 5px;
  padding: 5px;
  border: 1px solid gray;
  border-radius: 3px;
  transition: all 0.8s;
}
.error {
  background: rgb(202, 60, 60);
  color: white;
  border: 1px solid rgb(202, 60, 60);
  transition: 0.5s;
}
.success {
  background: rgb(28, 184, 65);
  color: white;
  border: 1px solid rgb(28, 184, 65);
  transition: 0.5s;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container text-center">
  <div class="section section-title">
    <h1>BINARY SEARCH</h1>
  </div>
  <div class="section input-section">
    <div class="col-md-4 col-md-offset-2 col-sm-6 col-sm-offset-3 col-xs-8 col-xs-offset-2">
      <div class="input-group generate">
        <input type="text" class="form-control input-lg" placeholder="10 - 100" id="generateInput">
        <div class="input-group-btn">
          <button class="btn btn-default btn-lg" id="generateButton">
            Generate array
          </button>
        </div>
      </div>

    </div>
    <div class="col-md-4 col-md-offset-1 col-sm-6 col-sm-offset-3 col-xs-8 col-xs-offset-2">
      <div class="input-group">
        <input type="text" class="form-control input-lg" placeholder="1 - 100" id="findInput" disabled>
        <div class="input-group-btn">
          <button class="btn btn-default btn-lg" type="submit" id="findButton" disabled>
            Find number
          </button>
        </div>
      </div>
    </div>
  </div>

  <div class="col-xs-12 section array-section">
    <div class="number">1</div>
    <div class="number">2</div>
    <div class="number">3</div>
    <div class="number">...</div>
    <div class="number">n</div>
  </div>
</div>

答案 3 :(得分:0)

我制作一个简单的代码...也许可以帮到你:

function foo() {
    while (true) {
        var wait = 0;
        $('#someDiv').css({'background-color': 'red'});

        //wait 1000ms
        wait = wait + 1000;
        setTimeout(function(){
            $('#someDiv').css({'background-color': 'blue'});
        }, wait);

        //wait 1000ms
        wait = wait + 1000;
        setTimeout(function(){
            if (true) { 
            console.log('doSomething');
            }
            else {
            console.log('loop')
            }
        }, wait);
        break;
    }
}
foo();

秘密在&#34;等待&#34;,这个值将是你想要的时间和最后一个&#34;等待&#34;的总和。值。您将获得预期的结果,但另一种方法。

我希望我有所帮助。

答案 4 :(得分:0)

我更正了安德烈罗德里格斯的代码:

var nbCall = 0;

function foo() {
    var wait = 0;
    $('#someDiv').css({'background-color': 'red'});

    //wait 1000ms
    wait = wait + 1000;
    setTimeout(function(){
        $('#someDiv').css({'background-color': 'blue'});
    }, wait);

    //wait 2000ms
    wait = wait + 1000;
    setTimeout(function(){
        if (nbCall++ > 5) { // Are other cond
             console.log('doSomething');
        }
        else {
            foo();
        }
    }, wait);
}
foo();

jsFiddle:https://jsfiddle.net/nnf8vcko/