120 lines
3.1 KiB
TypeScript
120 lines
3.1 KiB
TypeScript
import jQuery from "jquery";
|
|
|
|
// Helpers
|
|
|
|
/**
|
|
* "Types" out a DOM element, emulating the way a human might.
|
|
*/
|
|
class Typer {
|
|
private element: JQuery;
|
|
private text: string;
|
|
private cursor: boolean;
|
|
private typed: number;
|
|
private min: number;
|
|
private max: number;
|
|
private blink_tick: number;
|
|
private blink_timeout: number;
|
|
private end?: number;
|
|
|
|
/**
|
|
* Create the typer.
|
|
* @param {HTMLElement} element - The element to type.
|
|
* @param {number} blink - The time between cursor blinks.
|
|
* @param {number} blink_timeout - How long the cursor should keep
|
|
* blinking for after the text
|
|
* finishes typing.
|
|
*/
|
|
constructor(element: JQuery<HTMLElement>, blink: number, blink_timeout: number) {
|
|
// Retrieve the current content and wipe it. We also make the
|
|
// element visible if it was hidden.
|
|
this.element = element;
|
|
this.text = this.element.html();
|
|
this.element.html("");
|
|
this.element.css("visibility", "visible");
|
|
|
|
this.cursor = false;
|
|
this.typed = 0;
|
|
|
|
this.min = 20;
|
|
this.max = 70;
|
|
this.blink_tick = blink;
|
|
this.blink_timeout = blink_timeout;
|
|
|
|
this.end = null;
|
|
}
|
|
|
|
/**
|
|
* Start typing.
|
|
*/
|
|
type() {
|
|
this._type();
|
|
this._blink();
|
|
}
|
|
|
|
/**
|
|
* Draw the current text line, i.e., anything that has been typed
|
|
* so far, and a cursor if it is currently supposed to be on.
|
|
* @private
|
|
*/
|
|
_draw() {
|
|
let text = this.text.slice(0, this.typed);
|
|
|
|
if (this.cursor) {
|
|
text += "\u2588";
|
|
}
|
|
|
|
window.requestAnimationFrame(() => this.element.html(text));
|
|
}
|
|
|
|
/**
|
|
* Type the next character, and prepare to draw the next one. If
|
|
* no new characters are to be drawn, set the end timestamp.
|
|
* @private
|
|
*/
|
|
_type() {
|
|
this.typed += 1;
|
|
this._draw();
|
|
|
|
if (this.typed != this.text.length)
|
|
setTimeout(this._type.bind(this), this._type_tick());
|
|
else {
|
|
this.end = Date.now();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Make the cursor change blink status, and prepare for the next
|
|
* blink.
|
|
* @private
|
|
*/
|
|
_blink() {
|
|
this.cursor = !this.cursor;
|
|
this._draw();
|
|
|
|
// As long as we are typing, keep blinking
|
|
if (this.typed != this.text.length)
|
|
setTimeout(this._blink.bind(this), this.blink_tick);
|
|
// Once typing ends, keep going for a little bit
|
|
else if (Date.now() - this.end < this.blink_timeout)
|
|
setTimeout(this._blink.bind(this), this.blink_tick);
|
|
// Make sure we get rid of the cursor in the end
|
|
else {
|
|
this.cursor = true;
|
|
setTimeout(this._blink.bind(this), this.blink_tick);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Calculate a "human" time for the next character to type.
|
|
* @private
|
|
*/
|
|
_type_tick() {
|
|
return Math.round(Math.random() * this.max) + this.min;
|
|
}
|
|
}
|
|
|
|
jQuery(($) => {
|
|
const typer = new Typer($(".head-line .typed").first(), 500, 3000);
|
|
typer.type();
|
|
});
|