UNPKG

scramble-typed

Version:

A typing-in text scramble effect for unique and engaging animations. Inspired by typed.js.

179 lines (161 loc) 5.66 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.ScrambleTyped = factory()); })(this, (function () { 'use strict'; class ScrambleTyped { constructor(el, options) { this.target = typeof el === "string" ? document.querySelector(el) : el; this.text = options.text || this.target.innerText; this.useStartTrigger = options.useStartTrigger || false; this.typeSpeed = options.typeSpeed !== undefined ? options.typeSpeed : 50; this.scrambleDuration = options.scrambleDuration !== undefined ? options.scrambleDuration : 500; this.scrambleSpeed = options.scrambleSpeed !== undefined ? options.scrambleSpeed : 100; this.scrambleClasses = options.scrambleClasses || []; this.charset = options.charset || "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; this.preserveSpaces = options.preserveSpaces || false; this.restoreOnEnd = options.restoreOnEnd !== undefined ? options.restoreOnEnd : true; this.#useNodes = this.target.childNodes.length > 0; // callbacks this.onStart = options.onStart || null; this.onEnd = options.onEnd || null; // init this.#init(); } #innerds = ""; #model = null; #useNodes = false; #isRunning = false; #currentIndex = 0; #charSpanMap = []; #init() { this.#currentIndex = 0; this.#innerds = this.target.innerHTML; this.#model = this.target.cloneNode(true); if (this.#useNodes) { this.#createTextList(); } else { this.target.innerHTML = ""; } if (!this.useStartTrigger) this.start(); } #addScrambleClasses(el) { this.scrambleClasses.forEach((c) => { el.classList.add(c); }); } #removeScrambleClasses(el) { this.scrambleClasses.forEach((c) => { el.classList.remove(c); }); } #scrambleChar(finalChar, span) { if (this.preserveSpaces && /\s/.test(finalChar)) { this.#removeScrambleClasses(span); span.textContent = finalChar; return; } const startTime = Date.now(); const scramble = () => { const elapsed = Date.now() - startTime; if (elapsed >= this.scrambleDuration) { span.textContent = finalChar; this.#removeScrambleClasses(span); return; } const randomChar = this.charset[Math.floor(Math.random() * this.charset.length)]; span.textContent = randomChar; setTimeout(scramble, this.scrambleSpeed); }; scramble(); } #typeNodes() { if (this.#currentIndex >= this.#charSpanMap.length) return; const char = this.#charSpanMap[this.#currentIndex].char; const span = this.#charSpanMap[this.#currentIndex].span; this.#addScrambleClasses(span); //this.target.appendChild(span); this.#scrambleChar(char, span); this.#currentIndex++; setTimeout(() => this.#typeNodes(), this.typeSpeed); } #typeSimple() { if (this.#currentIndex >= this.text.length) return; const char = this.text[this.#currentIndex]; const span = document.createElement("span"); this.#addScrambleClasses(span); this.target.appendChild(span); this.#scrambleChar(char, span); this.#currentIndex++; setTimeout(() => this.#typeSimple(), this.typeSpeed); } #end() { if (this.restoreOnEnd) { if (this.#useNodes) { this.target.innerHTML = this.#innerds; } else { console.log("restoring"); this.target.innerHTML = this.text; } } this.#isRunning = false; if (this.onEnd && typeof this.onEnd === "function") { this.onEnd(); } } #textSplit(el) { const span = document.createElement("span"); const text = el.nodeValue; el.nodeValue = ""; [...text].forEach((character) => { const charSpan = document.createElement("span"); span.appendChild(charSpan); this.#charSpanMap.push({ span: charSpan, char: character }); }); return span; } #createTextList() { this.#traverse(this.target); } #traverse(el) { if (!el) return; else if (el.nodeType === Node.TEXT_NODE) return; else { const children = Array.from(el.childNodes); children.forEach((child) => { if (child.nodeType === Node.TEXT_NODE && child.nodeValue.trim()) { const span = this.#textSplit(child); el.replaceChild(span, child); } else { this.#traverse(child); } }); } } // public methods start() { if (this.#isRunning) { return; } const fullDuration = this.#useNodes ? this.#charSpanMap.length * this.typeSpeed + this.scrambleDuration : this.text.length * this.typeSpeed + this.scrambleDuration; setTimeout(() => { this.#end(); }, fullDuration + 200); if (this.onStart && typeof this.onStart === "function") { this.onStart(); } this.#isRunning = true; if (this.#useNodes) this.#typeNodes(); else this.#typeSimple(); } } return ScrambleTyped; }));