typewriter-text-effect
Version:
Simple typewriter text animation in JS/TS
1 lines • 7.7 kB
Source Map (JSON)
{"version":3,"sources":["../src/typewriter.ts","../src/index.ts"],"sourcesContent":["import { TypewriterOptions } from './typewriter.types';\r\n\r\nexport class Typewriter {\r\n private el: HTMLElement;\r\n private options: Required<TypewriterOptions>;\r\n private index = 0;\r\n private charIndex = 0;\r\n private isDeleting = false;\r\n private loopTimeout: any;\r\n private isPaused = false;\r\n private isStopped = false;\r\n private status: 'idle' | 'typing' | 'paused' | 'stopped' = 'idle';\r\n\r\n\r\n constructor(el: HTMLElement, options: TypewriterOptions) {\r\n this.el = el;\r\n this.options = {\r\n speed: 100,\r\n deleteSpeed: 50,\r\n delayBetween: 1000,\r\n pauseBetweenLoops: 2000,\r\n loop: true,\r\n cursor: true,\r\n cursorChar: '|',\r\n startDelay: 0,\r\n pauseOnHover: false,\r\n randomSpeed: false,\r\n autoStart: true,\r\n textStyleClass: '',\r\n onComplete: () => { },\r\n ...options,\r\n };\r\n if (this.options.textStyleClass) {\r\n const span = document.createElement('span');\r\n span.className = this.options.textStyleClass;\r\n this.el.appendChild(span);\r\n } else {\r\n this.el.appendChild(document.createTextNode(''));\r\n }\r\n\r\n // Setup cursor\r\n if (this.options.cursor) {\r\n const cursor = document.createElement('span');\r\n cursor.className = 'typewriter-cursor';\r\n cursor.textContent = this.options.cursorChar;\r\n this.el.appendChild(cursor);\r\n }\r\n\r\n // Hover listeners\r\n if (this.options.pauseOnHover) {\r\n this.el.addEventListener('mouseenter', this.pause);\r\n this.el.addEventListener('mouseleave', this.resume);\r\n }\r\n\r\n if (this.options.autoStart) {\r\n this.start();\r\n }\r\n\r\n }\r\n public start() {\r\n this.isStopped = false;\r\n this.status = 'typing';\r\n if (this.options.startDelay > 0) {\r\n setTimeout(() => this.type(), this.options.startDelay);\r\n } else {\r\n this.type();\r\n }\r\n }\r\n\r\n private type() {\r\n if (this.isStopped || this.isPaused) return;\r\n\r\n const currentText = this.options.text[this.index];\r\n const visibleText = currentText.substring(0, this.charIndex);\r\n\r\n // const textNode = this.el.childNodes[0];\r\n // textNode.textContent = visibleText;\r\n const textNode = this.el.firstChild as Text;\r\nif (textNode) textNode.textContent = visibleText;\r\n\r\n const nextSpeed = this.options.randomSpeed\r\n ? this.getRandomSpeed()\r\n : (this.isDeleting ? this.options.deleteSpeed : this.options.speed);\r\n\r\n if (!this.isDeleting) {\r\n if (this.charIndex < currentText.length) {\r\n this.charIndex++;\r\n this.loopTimeout = setTimeout(() => this.type(), nextSpeed);\r\n } else if (this.options.loop) {\r\n this.isDeleting = true;\r\n this.loopTimeout = setTimeout(() => this.type(), this.options.delayBetween);\r\n } else {\r\n this.status = 'stopped';\r\n this.options.onComplete?.();\r\n }\r\n } else {\r\n if (this.charIndex > 0) {\r\n this.charIndex--;\r\n this.loopTimeout = setTimeout(() => this.type(), nextSpeed);\r\n } else {\r\n this.isDeleting = false;\r\n this.index = (this.index + 1) % this.options.text.length;\r\n this.loopTimeout = setTimeout(() => this.type(), this.options.pauseBetweenLoops);\r\n }\r\n }\r\n }\r\n\r\n private getRandomSpeed(): number {\r\n const base = this.isDeleting ? this.options.deleteSpeed : this.options.speed;\r\n const variation = base * 0.3;\r\n return base + Math.floor((Math.random() - 0.5) * variation);\r\n }\r\n\r\n public pause = () => {\r\n this.isPaused = true;\r\n this.status = 'paused';\r\n clearTimeout(this.loopTimeout);\r\n };\r\n\r\n public resume = () => {\r\n if (!this.isPaused || this.isStopped) return;\r\n this.isPaused = false;\r\n this.status = 'typing';\r\n this.type();\r\n };\r\n\r\n public stop() {\r\n clearTimeout(this.loopTimeout);\r\n this.isStopped = true;\r\n this.status = 'stopped';\r\n }\r\n\r\n public reset() {\r\n this.stop();\r\n this.index = 0;\r\n this.charIndex = 0;\r\n this.isDeleting = false;\r\n this.isPaused = false;\r\n this.status = 'idle';\r\n this.el.childNodes[0].textContent = '';\r\n }\r\n\r\n public updateText(newText: string[]) {\r\n this.options.text = newText;\r\n this.reset();\r\n this.start();\r\n }\r\n\r\n public isRunning(): boolean {\r\n return this.status === 'typing';\r\n }\r\n\r\n public destroy() {\r\n this.stop();\r\n this.el.innerHTML = '';\r\n this.el.removeEventListener('mouseenter', this.pause);\r\n this.el.removeEventListener('mouseleave', this.resume);\r\n }\r\n\r\n}\r\n","export { Typewriter } from \"./typewriter\";\r\nexport type { TypewriterOptions } from \"./typewriter.types\";\r\n\r\n// also allow default import\r\nimport { Typewriter } from \"./typewriter\";\r\nexport default Typewriter;\r\n"],"mappings":"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","names":["Typewriter","el","options","__spreadValues","span","cursor","_a","_b","currentText","visibleText","textNode","nextSpeed","base","variation","newText","index_default","Typewriter"]}