2012-07-26 120 views
2

这是我的函数。将递归函数转换为异步CPS实现(javascript)

function duplicate_step_through_highlighted (element_jq, target_jq, char_cb) { 
     console.log(element_jq); 
     var contents = element_jq.contents(); 
     for (var i = 0 ; i < contents.length; ++i) { 
      // if text node, step 
      if (contents[i].nodeType === 3) { 
       // insert empty text node 
       var new_tn = document.createTextNode(''); 
       target_jq.append(new_tn); 

       // iterate it 
       var text = contents[i].nodeValue; 
       for (var j = 0; j < text.length; j++) { 
        char_cb(text[j],new_tn); 
        new_tn.nodeValue += text[j]; 
        // *** I want an async delay here *** 
       } 
      } else { // type should be 1: element 
       // target_jq gets a duplicate element inserted, copying attrs 
       var new_elem = $(contents[i].cloneNode(false)).appendTo(target_jq); 
       duplicate_step_through_highlighted($(contents[i]),$(new_elem),char_cb); 

       // then a recursive call is performed on the newly created element as target_jq 
       // and the existing one as element_jq. char_cb is passed in 
      } 
     } 
    } 

我在做什么是通过一次重建是一个字符重建的HTML元素。这样做有很好的理由,我希望它的视觉效果能够“输入”。

所以现在没有延迟,所以我的元素立即被复制。我已经检查过结果是否一致,但我很清楚,我可能需要完全重写这些功能,以便在插入每个字符后能够进行异步延迟。

我需要重新编写它并有一个堆栈来跟踪我在元素中的位置吗?

+0

那岂不是更容易的方式元素? – Yoshi 2012-07-26 08:43:27

+0

字符串太多<-->如果我这样做了dom转换。它可能不会太慢​​,但如果我可以帮助它的话,我不会写低效的代码。我想看到元素动态地获得新的字母。我实际上计划在插入的字母上做动画。 – 2012-07-26 08:44:28

回答

4

您可能想看看我最近的answerthis older oneDemo),了解如何实现这样的效果。


提示:不要将元素复制到新元素中,只是隐藏元素并使它们显示为部分对象。

此外,除了原生DOM元素外,可能更容易处理jQuery实例。所以是的,重写可能会:-)我认为它也需要一个堆栈。

function animate(elements, callback) { 
/* get: array with hidden elements to be displayes, callback function */ 
    var i = 0; 
    (function iterate() { 
     if (i < elements.length) { 
      elements[i].style.display = "block"; // show 
      animateNode(elements[i], iterate); 
      i++; 
     } else if (callback) 
      callback(); 
    })(); 
    function animateNode(element, callback) { 
     var pieces = []; 
     if (element.nodeType==1) { 
      while (element.hasChildNodes()) 
       pieces.push(element.removeChild(element.firstChild)); 
      setTimeout(function childStep() { 
       if (pieces.length) { 
        animateNode(pieces[0], childStep); 
        element.appendChild(pieces.shift()); 
       } else 
        callback(); 
      }, 1000/60); 
     } else if (element.nodeType==3) { 
      pieces = element.data.match(/.{0,2}/g); // 2: Number of chars per frame 
      element.data = ""; 
      (function addText(){ 
       element.data += pieces.shift(); 
       setTimeout(pieces.length 
        ? addText 
        : callback, 
        1000/60); 
      })(); 
     } 
    } 
} 

animate($("#foo").children()); 

Demo at jsfiddle.net

工作原理:

  • addText功能增加了一些字符添加到当前文本节点,并设置超时为自己 - 动画!万一完成,它会调用callback函数。
  • childStep在一个childnode上运行动画,并将其自身作为回调直到没有孩子离开 - 然后提示callback函数。
  • 两者合起来,animateNode递归地运行在节点树上并按照它们的顺序为文本节点设置动画。
  • iterate函数调用animateNode(在解除它们之后)在所有输入元素上,通过传递自己作为回调。在所有的输入元素完成后,它调用外部callback,它作为animate的第二个参数给出。
+0

等一下,什么?你说我需要一个堆栈,但你的实现不需要一个。这段代码似乎工作得很好,但我似乎无法围绕它... – 2012-07-26 20:46:56

+0

这是一个调用堆栈:-)此外,此解决方案构建了一堆要添加的片段,它们要么递归地用子节点动画或文本片段逐帧添加。 – Bergi 2012-07-27 09:23:07

+0

你能告诉我你制作这段代码的过程吗?你用什么样的规则将我开始使用的代码转换成这个?我认为我接近了解为什么以及如何工作,但我仍然不确定代码在等待超时时间段时阻止的位置。这似乎是必要的,但我不清楚这些部分在哪里。谢谢 – 2012-07-29 01:02:41

1

我做了一个简单的脚本来我的网站上使用,它可能会帮助那些谁正在寻找达到这个效果。

这里是在Github上回购一个link,这里的解释是:该元素导出为一个字符串,然后该字符串工作,而不是与真正的工作

class Typer { 

    constructor(typingSpeed, content, output) { 

     this.typingSpeed = typingSpeed; 
     // Parses a NodeList to a series of chained promises 
     this.parseHtml(Array.from(content), output); 
    }; 

    makePromise(node, output) { 

     if (node.nodeType == 1) // element 
     { 
      // When a new html tag is detected, append it to the document 
      return new Promise((resolve) => { 
       var tag = $(node.outerHTML.replace(node.innerHTML, "")); 
       tag.appendTo(output); 
       resolve(tag); 
      }); 

     } else if (node.nodeType == 3) // text 
     { 
      // When text is detected, create a promise that appends a character 
      // and sleeps for a while before adding the next one, and so on... 
      return this.type(node, output, 0); 
     } else { 
      console.warn("Unknown node type"); 
     } 
    } 

    parseHtml(nodes, output) { 
     return nodes.reduce((previous, current) => previous 
      .then(() => this.makePromise(current, output) 
       .then((output) => this.parseHtml(Array.from(current.childNodes), output))), Promise.resolve()); 
    } 

    type(node, output, textPosition) { 
     var textIncrement = textPosition + 1; 

     var substring = node.data.substring(textPosition, textIncrement); 

     if (substring !== "") { 
      return new Promise(resolve => setTimeout(resolve, this.typingSpeed)) 
       .then(() => output.append(substring)) 
       .then(() => this.type(node, output, textIncrement)); 
     } 

     return Promise.resolve(output); 
    } 
} 
+0

我最后的回答被删除了,也许是因为它只有一个链接?这是一个更完整的版本 – 2017-02-09 12:48:05

+1

是的,我的评论随之而来。你有没有想过使用发电机?我认为生成器是表达这种异步行为的自然方式 – 2017-02-09 17:00:07

+0

说实话,我从来没有听说过它们。谷歌搜索后,似乎他们可以极大地简化这个代码!我肯定会仔细看看它,谢谢你的洞察力:) – 2017-02-09 19:15:24