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

后端 未结 3 1297
我寻月下人不归
我寻月下人不归 2020-11-29 09:54

Here\'s my function.

    function duplicate_step_through_highlighted (element_jq, target_jq, char_cb) {
        console.log( element_jq);
        var conten         


        
相关标签:
3条回答
  • 2020-11-29 10:25

    Here is my solution, it is a more efficient, cleaner and faster way of doing it:

    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>

    0 讨论(0)
  • 2020-11-29 10:42

    I made a simple script to use on my website, it might help those who are looking to achieve this effect.

    Here is a link for the repo at Github, here is the explanation:

    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 讨论(0)
  • 2020-11-29 10:44

    You might want to have a look at my recent answer or this older one (Demo), on how to implement such an effect.


    Tip: Don't clone the elements into new ones, just hide them and make them appear part-for-part.

    Also, it might be easier not to deal with jQuery instances at all but native DOM elements. So yes, a rewrite might do :-) And I think it does need a stack as well.

    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

    How it works:

    • The addText function adds some character to the current text node, and sets a timeout for itself - animation! In case everything is done, it invokes the callback function.
    • childStep runs the animation on a childnode, and passes itself as the callback until no children are left - then nvokes the callback function.
    • Both together, animateNode recursively runs over the node tree and animates the textnodes in thier order.
    • the iterate function calls animateNode (after unhinding them) on all input elements, by passing itself as the callback. After all input elements are finished, it invokes the outer callback which is given as the second argument to animate.
    0 讨论(0)
提交回复
热议问题