【问题标题】:Display Array Elements in forEach loop with delay在 forEach 循环中延迟显示数组元素
【发布时间】:2018-02-04 18:36:47
【问题描述】:

对于客户端,我需要使用打字机效果从一个数组中显示四行不同的文本

我的打字机效果设置得很好,但我不知道为什么我的 forEach 循环只显示数组中的最后一个元素

var i = 0;
var text;
var txt = [
  'Lorem ipsum dummy text blabla.',
  'Lorem IPSUM dummy text blabla.',
  'Lorem ipsum DUMMY text blabla.',
  'Lorem ipsum dummy TEXT blabla.'
];
var speed = 50;
var delay = 3000
var demo = document.getElementById("demo");

function go() {
  txt.forEach(function(str, index) {
    text = str; // var to pass to typeWriter
    setTimeout(typeWriter(), delay * index);
  });
}

function typeWriter() {
  if (i < text.length) {
    demo.innerHTML += text.charAt(i);
    i++;
    setTimeout(typeWriter, speed);
  } else {
    // When string is fully typed, delete after 2 second
    setTimeout(function() {
      demo.innerHTML = '';
      i = 0;
    }, 2000);
  }
}
<button onclick="go()">Click me</button>

<p id="demo"></p>

【问题讨论】:

  • 您希望所有行都在“同一”时间吗?
  • 不,我希望一个接一个地显示,中间有一点延迟,以便网站访问者有机会阅读短句

标签: javascript arrays foreach settimeout


【解决方案1】:

您需要传递 callback 函数来遍历您的数组项

看这段代码sn-p

var i = 0;
var text;
var txt = [
  'Lorem ipsum dummy text blabla.',
  'Lorem IPSUM dummy text blabla.',
  'Lorem ipsum DUMMY text blabla.',
  'Lorem ipsum dummy TEXT blabla.'
];
var speed = 50;
var delay = 1000
var demo = document.getElementById("demo");

function go() {
  function loop(index) {
    if (index === txt.length) return;
       
    setTimeout(function() {
      text = txt[index];    
      typeWriter(function() {
        loop(++index);
      });      
    }, delay * index);
  }

  loop(0);
}

function typeWriter(cb) {
  if (i < text.length) {
    demo.innerHTML += text.charAt(i);
    i++;
    setTimeout(function() {
      typeWriter(cb);
    }, speed);
  } else {
    // When string is fully typed, delete after 2 second
    setTimeout(function() {
      demo.innerHTML += '<p>';
      i = 0;
      cb();
    }, 2000);
  }
}
<button onclick="go()">Click me</button>

<p id="demo"></p>

看到了吗?现在循环正确。

【讨论】:

    【解决方案2】:

    您要更新 UI 的行是:

    demo.innerHTML = '';
    

    但必须是:

    demo.innerHTML += '<br>';
    

    这样您就不会覆盖以前的输出,而是将新的输出放在新的一行上。

    此外,您实际上是在立即调用 typewriter 函数,因为您的 setTimeout 看起来像这样:

    setTimeout(typeWriter(), delay * index);
    

    不仅仅是引用函数,像这样:

    setTimeout(typeWriter, delay * index);
    

    var i = 0;
    var text;
    var txt = [
      'Lorem ipsum dummy text blabla.',
      'Lorem IPSUM dummy text blabla.',
      'Lorem ipsum DUMMY text blabla.',
      'Lorem ipsum dummy TEXT blabla.'
    ];
    var speed = 50;
    var delay = 3000
    var demo = document.getElementById("demo");
    
    function go() {
      txt.forEach(function(str, index) {
        text = str; // var to pass to typeWriter
        setTimeout(typeWriter, delay * index);
      });
    }
    
    function typeWriter() {
      if (i < text.length) {
        demo.innerHTML += text.charAt(i);
        i++;
        setTimeout(typeWriter, speed);
      } else {
        // When string is fully typed, delete after 2 second
        setTimeout(function() {
          demo.innerHTML += '<br>';
          i = 0;
        }, 2000);
      }
    }
    <button onclick="go()">Click me</button>
    
    <p id="demo"></p>

    【讨论】:

    • 这只是将数组中的最后一个字符串打印两次
    【解决方案3】:

    forEach 没有延迟。 typeWriter 执行时会看到text = 'Lorem ipsum dummy TEXT blabla.'

    我没有那么优化的建议:

    var txt = [
      'Lorem ipsum dummy text blabla.',
      'Lorem IPSUM dummy text blabla.',
      'Lorem ipsum DUMMY text blabla.',
      'Lorem ipsum dummy TEXT blabla.'
    ];
    
    function go(){
      demo.innerHTML = '';
      txt.length && doType();
    }
    
    function doType(){
    
      if(!txt[0].length){ 
        txt.shift(); 
        demo.innerHTML += '<br />';
        return setTimeout( doType, 50 );
      }
    
      if(!txt.length) return;
    
      demo.innerHTML += txt[0][0];
      txt[0] = txt[0].substr(1);
    
      setTimeout( doType, 50 );
    }
    

    我相信您可以进行必要的修改。

    【讨论】:

      【解决方案4】:

      这是一个将 Promises 与 setTimeout 偏移控制器相结合的解决方案。

      const speed = 50;
      const delay = 2000;
            
      const demo = document.getElementById("demo");
      
      function go() {
      	
        const textArray = [
          'Lorem ipsum dummy text blabla.',
          'Lorem IPSUM dummy text blabla.',
          'Lorem ipsum DUMMY text blabla.',
          'Lorem ipsum dummy TEXT blabla.'
        ];
      	
        let linesToType = initializePromises(textArray)
      	
        linesToType.reduce((promiseChain, currentLine) => {
          return promiseChain.then(currentLine);
        }, Promise.resolve())
        
      }
      
      var offsetController = (function() {
        let _offset = 0;
        return {
          increaseOffset(time) {
            _offset = _offset + time
          },
          getOffset(){
            return _offset;
          }
        }
      })()
      
      function initializePromises(textArray) {
        return textArray.map(line => typeWriter(line) )
      }
      	
      function typeWriter(text) {
        offsetController.increaseOffset(delay);
        return new Promise((resolve, reject) => {
          let promises = text.split("").map((letter, index) => {
      	  return printNextLetter(demo, text, index)
          })
          clearLine(demo);
          Promise.all(promises).then(() => { 
            resolve() 
          })
        })
      }
      
      function printNextLetter(element, text, i) {
        offsetController.increaseOffset(speed);
        return new Promise((resolve) => setTimeout(() => {
          element.innerHTML += text.charAt(i);
          resolve()
        }, offsetController.getOffset()));
      }
      
      function clearLine(element) {
        offsetController.increaseOffset(delay);
        return new Promise((resolve) => setTimeout(() => {
          element.innerHTML = "";
          resolve()
        }, offsetController.getOffset()));
      }
      <button onclick="go()">Click me</button>
      
      <p id="demo"></p>

      【讨论】:

        【解决方案5】:

        只是觉得这是一个很好的问题来说明async iteration,一个全新的 ES2018 功能:

        async function delay(value, time) {
            return new Promise(resolve => setTimeout(() => resolve(value), time));
        }
        
        async function *eachDelayed(values, time) {
            for (let value of values)
                yield await delay(value, time);
        }
        
        async function print(xs) {
            for await (let x of eachDelayed(xs, 50))
                document.body.innerHTML += x;
        }
        
        print('The quick brown fox jumps over the lazy dog!')

        您需要最新的 Chrome/FF 才能运行它。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-02-15
          • 1970-01-01
          • 1970-01-01
          • 2014-11-10
          相关资源
          最近更新 更多