2012-07-26 1 views
2

다음은 필자의 기능입니다.재귀 함수를 비동기 CPS 구현으로 변환 (자바 스크립트)

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

문자열로 요소를 내 보낸 다음 대신 실제 작업의 해당 문자열에서 작동하는 방법을 쉽게하지 않을까요 : 여기

는 Github에서의 REPO에 대한 link이며, 여기에 설명이다 집단? – Yoshi

+0

너무 많은 문자열 <--> 돔 변환하면됩니다. 너무 느리지는 않겠지 만 도움이된다면 비효율적 인 코드를 작성하지 않을 것입니다. 엘리먼트가 동적으로 새로운 글자를 얻고 싶습니다. 실제로 삽입되는 편지에 애니메이션 작업을 할 계획입니다. –

답변

4

이러한 효과를 구현하는 방법에 대한 최신 answer 또는 this older one (Demo)을보고 싶을 수 있습니다.


팁 : 요소를 새로운 요소로 복제하지 말고 숨기고 파트별로 표시하십시오.

또한 DOM 요소를 제외하고는 jQuery 인스턴스를 다루지 않는 것이 더 쉽습니다. 그래서 예, 재 작성은 다음을 할 수 있습니다 :-) 그리고 나는 스택이 필요하다고 생각합니다.

  • addText 함수는 현재 텍스트 노드에 일부 문자를 추가하고 자체에 대한 시간 제한을 설정 - 애니메이션 :

    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

    그것은 작동하는 방법! 모든 작업이 완료되면 callback 함수가 호출됩니다.

  • childStep은 자식 노드에서 애니메이션을 실행하고 자식이 남지 않을 때까지 콜백으로 전달하고 callback 함수를 사용합니다.
  • 둘 다 animateNode은 노드 트리를 반복적으로 실행하고 텍스트 노드를 그 순서대로 움직입니다.
  • iterate 함수는 콜백으로 전달하여 모든 입력 요소에 animateNode (이후에 그것들을 해체 한 후)을 호출합니다. 모든 입력 요소가 끝나면 animate의 두 번째 인수로 주어진 외부 callback을 호출합니다.
+0

잠깐, 뭐라구? 스택이 필요 하겠지만 구현에는 필요하지 않습니다.이 코드는 정말 잘 작동하는 것 같지만 그 주위에 내 머리를 감쌀 수없는 것 같습니다 ... –

+0

그것은 호출 스택입니다 :-) 또한이 솔루션은 자식 노드를 반복적으로 추가 할 조각 스택을 만듭니다 프레임별로 프레임을 추가 할 애니메이션 또는 텍스트 조각. – Bergi

+0

이 코드를 작성하기 위해 통과 한 절차 *를 말씀해 주시겠습니까? 시작한 코드를이 코드로 변환하는 데 어떤 종류의 규칙을 사용합니까? 이유와 작동 방식을 이해하는 데 가깝다고 생각하지만 시간 초과가 발생하기를 기다릴 때 코드 블록이 어디에 있는지 확실하지 않습니다. 그것은 꼭 필요한 것 같지만 그 부분이 어디에 있는지 분명하지 않습니다. 감사합니다. –

1

내 웹 사이트에서 사용할 간단한 스크립트를 만들었으므로이 효과를 얻으려는 사람들에게 도움이 될 수 있습니다.

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

링크가 있었기 때문에 마지막 답이 삭제 되었습니까? 여기에 더 완전한 버전 –

+1

그래, 내 의견이 함께 갔다. 발전기 사용에 대해 생각해 보셨습니까? 나는 발전기가 이러한 유형의 비동기 행동을 표현하는 자연스러운 방법이라고 생각한다. –

+0

솔직히 말해서, 나는 그들에 대해 들어 본 적이 없다. 인터넷 검색을 한 후에는이 코드를 매우 간단하게 만들 수 있습니다! 통찰력 덕분에 확실히 자세히 살펴볼 것입니다. –

관련 문제