2

这是我的功能。

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

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

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

4

3 回答 3

4

您可能想看看我最近的答案这个较旧的答案(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在子节点上运行动画,并将自身作为回调传递,直到没有子节点离开 - 然后调用该callback函数。
  • 两者一起animateNode递归地运行在节点树上并按顺序为文本节点设置动画。
  • 通过将自身作为回调传递,函数在所有输入元素上调用(取消隐藏它们之后)iterateanimateNode在所有输入元素完成后,它调用callback作为第二个参数给定的外部元素animate
于 2012-07-26T08:58:30.783 回答
2

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

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>

于 2018-10-19T18:42:19.370 回答
1

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

这是Github 上的 repo链接,解释如下:

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);
    }
}
于 2017-02-09T12:47:25.937 回答