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

Converting a recursive function into an asynchronous CPS implementation (javascript)

本文关键字:实现 javascript CPS 异步 递归函数 转换      更新时间:2024-02-20

这是我的函数。

    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 元素。这样做有一个很好的理由,我希望它被"输入"的视觉效果。

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

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

你可能想看看我最近的答案或这个旧的答案(演示(,关于如何实现这样的效果。


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

此外,除了本机 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 在子节点上运行动画,并将自身作为回调传递,直到没有子节点 - 然后调用 callback 函数。
  • 两者一起,animateNode递归地运行在节点树上,并按其顺序对文本节点进行动画处理。
  • iterate 函数通过自身作为回调传递自身,对所有输入元素调用animateNode(取消阻碍后(。完成所有输入元素后,它会调用外部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);
}
<body onload="typeWriter();">
<p class="demo"></p>
</body>

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

这是

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