142 lines
4.4 KiB
TypeScript
142 lines
4.4 KiB
TypeScript
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<void> {
|
|
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<void> {
|
|
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<void> {
|
|
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<void> {
|
|
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;
|
|
}
|
|
}
|