import { DELAY_WORDS } from "./settings"; import { el, sleep } from "./tools"; export class ScrollingTextBox { private dom_root: HTMLDivElement; private prev_line_words: HTMLDivElement[]; private cur_line_words: HTMLDivElement[]; private prev_line: HTMLParagraphElement; private current_line: HTMLParagraphElement; public constructor() { this.dom_root = el.div([], ["scrolling-textbox"]); this.cur_line_words = []; this.prev_line_words = []; this.current_line = el.div(this.cur_line_words, ["paragraph", "active"]); this.prev_line = el.div(this.prev_line_words, ["paragraph", "previous"]); this.dom_root.appendChild(this.prev_line); this.dom_root.appendChild(this.current_line); } public async update_current(text: string): Promise { this.update_words(this.current_line, this.cur_line_words, text); await sleep(10); this.show_new_words(this.cur_line_words); } public async finish_line(text: string): Promise { this.update_words(this.current_line, this.cur_line_words, text, true); const current_height = this.current_line.getBoundingClientRect().height; this.prev_line.style.transform = `translateY(calc(-100% - ${current_height}px))`; this.current_line.style.transform = "translateY(-100%)"; this.current_line.style.color = "grey"; await sleep(500); this.dom_root.removeChild(this.prev_line); this.prev_line = this.current_line; this.prev_line_words = this.cur_line_words; this.prev_line.className = "paragraph previous"; this.current_line = el.div([], ["paragraph", "active"]); this.cur_line_words = []; this.dom_root.appendChild(this.current_line); await sleep(10); } public async add_line(text: string): Promise { const current_height = this.current_line.getBoundingClientRect().height; const next_words = this.make_words(text); const next_line = el.div(next_words, ["paragraph", "previous"]); next_line.style.transform = `translateY(${current_height}px)`; next_line.style.opacity = "0"; this.dom_root.appendChild(next_line); await sleep(50); next_line.style.transform = "translateY(0px)"; next_line.style.opacity = "1"; this.current_line.style.transform = "translateY(-100%)"; this.current_line.style.color = "grey"; this.prev_line.style.transform = `translateY(calc(-100% - ${current_height}px))`; await sleep(500); this.dom_root.removeChild(this.prev_line); this.prev_line = this.current_line; this.prev_line_words = this.cur_line_words; this.prev_line.className = "paragraph previous"; this.current_line = next_line; this.current_line.className = "paragraph active"; this.cur_line_words = next_words; } private async show_new_words(words: HTMLParagraphElement[]): Promise { for (const word of words) { if (word.style.opacity != "0") { continue; } word.style.opacity = "1"; word.style.transform = "scale(100%)"; await sleep(50); } } private update_words( parent: HTMLDivElement, words: HTMLParagraphElement[], new_text: string, override: boolean = false, ): void { const new_words = new_text.split(" "); if (!override) { for (let i = 0; i < DELAY_WORDS; ++i) { new_words.pop(); } } if (words.length > new_words.length) { this.trim_words(parent, words, new_words.length); for (let i = 0; i < new_words.length; ++i) { if (words[i].innerText == new_words[i]) { continue; } words[i].innerText = new_words[i]; } return; } const to_push: HTMLParagraphElement[] = []; for (let i = 0; i < new_words.length; ++i) { if (i < words.length) { if (new_words[i] == words[i].innerText) { continue; } words[i].innerText = new_words[i]; continue; } const word = el.p(new_words[i], ["word"]); if (!override) { word.style.opacity = "0"; word.style.transform = "scale(0)"; } words.push(word); to_push.push(word); } parent.append(...to_push); } private trim_words( parent: HTMLDivElement, words: HTMLParagraphElement[], target_length: number ): void { if (target_length >= words.length || words.length == 0) { return; } for (let i = words.length - 1; i >= target_length; --i) { const word = words.pop(); if (word === undefined) { continue; } parent.removeChild(word); } } private make_words(text: string): HTMLDivElement[] { const words = text.split(" "); let res = []; for (const word of words) { res.push(el.p(word, ["word"])); } return res; } public get dom(): HTMLDivElement { return this.dom_root; } }