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...