130 lines
3.6 KiB
JavaScript
130 lines
3.6 KiB
JavaScript
export { PPTY };
|
|
|
|
function animDelta(anim) {
|
|
const timing = anim.effect.getComputedTiming();
|
|
return (timing.delay ?? 0) + (timing.duration ?? 0) + (timing.endDelay ?? 0);
|
|
}
|
|
|
|
class PPTY {
|
|
#root;
|
|
#blockOptions;
|
|
#startCallback = _ => { };
|
|
#finishCallback = _ => { };
|
|
|
|
constructor(root, blockOptions) {
|
|
this.#root = root;
|
|
this.#blockOptions = blockOptions;
|
|
}
|
|
|
|
onstart(handler) {
|
|
this.#startCallback = handler;
|
|
return this;
|
|
}
|
|
|
|
onfinish(handler) {
|
|
this.#finishCallback = handler;
|
|
return this;
|
|
}
|
|
|
|
run() {
|
|
var delay = 0;
|
|
|
|
this.#startCallback(this.#root);
|
|
|
|
this.#root
|
|
.querySelectorAll(".ppty-block")
|
|
.forEach((block, index, blocks) => {
|
|
const prompt = block.querySelector(".ppty-prompt");
|
|
const command = block.querySelector(".ppty-command");
|
|
const output = block.querySelector(".ppty-output");
|
|
|
|
const options = this.#blockOptions[index];
|
|
const cps = options["cps"]; // chars per second
|
|
const promptDelay = options["promptDelay"] * 1000;
|
|
const commandDelay = options["commandDelay"] * 1000;
|
|
const outputDelay = options["outputDelay"] * 1000;
|
|
const blinkTime = options["blinkTime"] * 2 * 1000; // x2 then x1000
|
|
|
|
// WARNING: ensure prompt|command|output != null
|
|
const promptAnim = prompt.animate(
|
|
[{ visibility: "visible" }],
|
|
{
|
|
delay: delay + promptDelay,
|
|
fill: "forwards",
|
|
},
|
|
);
|
|
delay = animDelta(promptAnim);
|
|
|
|
const showCursor = () => { command.style.borderRightColor = cursorColor; return true; };
|
|
const hideCursor = () => { command.style.borderRightColor = "transparent"; return false; };
|
|
const startCursorBlink = () => setInterval(() => {
|
|
cursorVisible = cursorVisible ? hideCursor() : showCursor();
|
|
}, blinkTime);
|
|
|
|
const cursorColor = command.style.borderRightColor;
|
|
var cursorVisible = true;
|
|
var blinkId = null;
|
|
setTimeout(() => {
|
|
command.style.visibility = "visible";
|
|
blinkId = startCursorBlink();
|
|
}, delay);
|
|
|
|
// WARNING: ensure command.textContent != null
|
|
const commandLen = command.textContent.trim().length
|
|
const commandAnim = command.animate(
|
|
[
|
|
{
|
|
width: "0ch",
|
|
visibility: "visible",
|
|
},
|
|
{
|
|
width: `${commandLen}ch`,
|
|
visibility: "visible",
|
|
}
|
|
],
|
|
{
|
|
duration: commandLen / cps * 1000,
|
|
delay: delay + commandDelay,
|
|
easing: `steps(${commandLen}, end)`,
|
|
fill: "forwards",
|
|
}
|
|
);
|
|
// pause the cursor while typing
|
|
setTimeout(() => {
|
|
clearInterval(blinkId);
|
|
showCursor();
|
|
cursorVisible = true;
|
|
}, delay + commandDelay);
|
|
delay = animDelta(commandAnim);
|
|
setTimeout(() => {
|
|
blinkId = startCursorBlink();
|
|
}, delay);
|
|
|
|
// delay until output is visible
|
|
delay += outputDelay;
|
|
setTimeout(() => {
|
|
clearInterval(blinkId);
|
|
hideCursor();
|
|
}, delay);
|
|
|
|
// unhide output
|
|
output.animate(
|
|
[{ visibility: "visible" }],
|
|
{
|
|
delay: delay,
|
|
fill: "forwards",
|
|
}
|
|
);
|
|
|
|
// hide the cursor after output displays and run the callback
|
|
setTimeout(() => {
|
|
command.style.borderRightColor = "transparent";
|
|
if (index == blocks.length - 1)
|
|
this.#finishCallback(this.#root);
|
|
}, delay);
|
|
});
|
|
}
|
|
}
|
|
|
|
|
|
|