Typer

by soulwire

JavaScript

class Typer {
	constructor(el) {
  	this.el = el || document.createElement('span');
  }
  type(html) {
  	const el = this.el;
    
    this.el.innerHTML = html;

		const iterator = document.createNodeIterator(this.el);
    const nodes = [];
    let node = iterator.nextNode();
    while (node) {
    	nodes.push(node);
    	node = iterator.nextNode();
    }

    const textNodes = nodes.filter(node => node.nodeType === document.TEXT_NODE);
    const textLengths = textNodes.map(node => node.length);
    const finalText = this.el.innerText;
    const charCount = finalText.length;
    //console.log(textNodes.map(n => n.data))
    
    // now clear texts
    textNodes.forEach(node => node.data = '');
    
    
    const duration = 4.0;
    
    // animation loop
    let lastTick = performance.now();
    let secondsElapsed = 0;
    const tick = () => {
      const now = performance.now();
      const dt = now - lastTick;
      secondsElapsed += dt / 1000;
      lastTick = now;
      if (secondsElapsed >= duration) {
      	console.log('done');
        el.innerHTML = html;
      } else {
      	requestAnimationFrame(tick);
        const progress = Math.max(0, Math.min(secondsElapsed / duration));
        const visibleIndex = Math.round(Math.pow(progress, 0.5) * charCount);
        const scrambleIndex = Math.round(Math.pow(progress, 2.0) * charCount);
        const resolvedIndex = Math.round(Math.pow(progress, 12.0) * charCount);
        const maxIndex = Math.max(visibleIndex, scrambleIndex, resolvedIndex);
        //const textBuffer = finalText.substr(0, maxIndex).split('');
        
        const textBuffer = [];
        for (let i = 0; i < maxIndex; i++) {
        	if (i <= resolvedIndex) {
          	textBuffer[i] = finalText[i];
          } else if (i <= scrambleIndex) {
          	textBuffer[i] = '*';
          } else if (i <= visibleIndex) {
          	textBuffer[i] = '_';
          }
        }
        
        let textNodeIndex = 0;
        while...