UNPKG

@arwes/text

Version:

Futuristic Sci-Fi UI Web Framework

64 lines (63 loc) 2.59 kB
import { filterProps, randomizeList } from '@arwes/tools'; import { createAnimation } from '@arwes/animated'; import { walkTextNodes } from '../internal/walkTextNodes/index.js'; import { setTextNodesContent } from '../internal/setTextNodesContent/index.js'; const CIPHERED_CHARACTERS = ' ----____abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; const animateTextDecipher = (props) => { const { rootElement, contentElement, duration, easing = 'linear', isEntering = true, hideOnExited = true, hideOnEntered, characters = CIPHERED_CHARACTERS } = filterProps(props); if (!rootElement || !contentElement) { throw new Error('ARWES animateTextDecipher() requires valid DOM elements.'); } const cloneElement = contentElement.cloneNode(true); Object.assign(cloneElement.style, { position: 'absolute', inset: 0, visibility: 'visible', opacity: 1 }); const textNodes = []; const textsReal = []; walkTextNodes(cloneElement, (child) => { textNodes.push(child); textsReal.push(child.textContent || ''); }); const length = textsReal.join('').length; const indexes = randomizeList(Array(length) .fill(null) .map((_, i) => i)); const deciphered = {}; rootElement.appendChild(cloneElement); contentElement.style.visibility = 'hidden'; return createAnimation({ duration, easing, direction: isEntering ? 'normal' : 'reverse', onUpdate: (progress) => { const newPositionsLength = Math.round(length * progress); for (let index = 0; index < length; index++) { deciphered[indexes[index]] = index < newPositionsLength; } const textsCurrent = textsReal.map((text) => text .split('') .map((char, index) => { if (char === ' ') return ' '; if (deciphered[index]) return char; return characters[Math.round(Math.random() * (characters.length - 1))]; }) .join('')); setTextNodesContent(textNodes, textsCurrent, length); }, onFinish: () => { contentElement.style.visibility = (isEntering && hideOnEntered) || (!isEntering && hideOnExited) ? 'hidden' : ''; cloneElement.remove(); }, onCancel: () => { contentElement.style.visibility = ''; cloneElement.remove(); } }); }; export { animateTextDecipher };