将递归函数转换为异步CPS实现(javascript)

这是我的function。

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元素。 这样做是有充分理由的,我希望它的视觉效果能够“输入”。

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

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

你可能想看看我最近的答案或者这个较旧的 答案 ( Demo ),看看如何实现这样的效果。


提示:不要将元素克隆到新元素中,只需隐藏它们并使它们按部分显示即可。

此外,除了本机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()); 

在jsfiddle.net上演示

这个怎么运作:

  • addText函数为当前文本节点添加了一些字符,并为自己设置了超时 - 动画! 万一完成,它会调用callback函数。
  • childStep在子childStep运行动画,并将自身作为回调传递,直到没有子节点 - 然后nvokes callback函数。
  • 两者一起, animateNode递归方式在节点树上运行, animateNode顺序为文本节点设置动画。
  • iterate函数通过将自身作为回调传递,在所有输入元素上调用animateNode (在取消它们之后)。 完成所有输入元素后,它将调用外部callback ,该callback作为animate的第二个参数给出。

这是我的解决方案,它是一种更高效,更清洁,更快捷的方式:

 var start = 0; //Makes sure you start from the very beggining of the paragraph. var text = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras viverra sem dolor, nec tempor purus luctus vitae. Nulla massa metus, iaculis et orci euismod, faucibus fringilla metus. Sed pellentesque in libero nec.'; //Your text var speed = 14; //Of course you can choose your own speed, 0 = instant, the more you add the slower it gets. function typeWriter() { if (start < text.length) { document.querySelector('.demo').innerHTML += text.charAt(start); start++; } setTimeout(typeWriter, speed); } 
  

我在我的网站上制作了一个简单的脚本,它可能会帮助那些希望实现这种效果的人。

这是Github上的回购链接 ,这里是解释:

 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); } }