implement PPTY
This commit is contained in:
parent
a13032c76e
commit
d76ac04531
12 changed files with 499 additions and 118 deletions
|
|
@ -1,24 +1,59 @@
|
|||
import { Smc } from "./smc/smc.js"
|
||||
import { Smc, fetchShader } from "./smc/lib.js";
|
||||
import { PPTY } from "./ppty/lib.js";
|
||||
|
||||
main();
|
||||
|
||||
async function fetchShader(uri, delegate) {
|
||||
const res = await fetch(uri);
|
||||
if (res.ok)
|
||||
return await res.text();
|
||||
this.raiseError(
|
||||
SmcErr.FETCH_SHADER,
|
||||
`Failed to load shader source ${url}: ${res.status} ${res.json()}`);
|
||||
return ""
|
||||
function endBoot(root) {
|
||||
const style = getComputedStyle(root);
|
||||
const paddingLeft = parseFloat(style.paddingLeft);
|
||||
const paddingRight = parseFloat(style.paddingRight);
|
||||
const contentWidth = root.clientWidth - paddingLeft - paddingRight;
|
||||
const paddingTop = parseFloat(style.paddingTop);
|
||||
const paddingBottom = parseFloat(style.paddingBottom);
|
||||
const contentHeight = root.clientHeight - paddingTop - paddingBottom;
|
||||
|
||||
root.style.width = `${contentWidth}px`;
|
||||
root.style.height = `${contentHeight}px`;
|
||||
|
||||
const fade = root.animate(
|
||||
[{ opacity: 0 }],
|
||||
{
|
||||
duration: 400,
|
||||
delay: 3000,
|
||||
fill: "forwards",
|
||||
}
|
||||
);
|
||||
|
||||
fade.onfinish = () => root.remove();
|
||||
}
|
||||
|
||||
|
||||
function main() {
|
||||
const canvas = document.querySelector("#gl-canvas");
|
||||
const boot = document.querySelector("#boot-ppty")
|
||||
new PPTY(
|
||||
boot,
|
||||
[
|
||||
{
|
||||
cps: 12,
|
||||
promptDelay: 0.5,
|
||||
commandDelay: 0.7,
|
||||
outputDelay: 1,
|
||||
blinkTime: 0.6,
|
||||
},
|
||||
{
|
||||
cps: 10,
|
||||
promptDelay: 0,
|
||||
commandDelay: 1.5,
|
||||
outputDelay: 1.2,
|
||||
blinkTime: 0.6,
|
||||
}
|
||||
])
|
||||
.onfinish(endBoot)
|
||||
.run();
|
||||
|
||||
const canvas = document.querySelector("#bg-canvas");
|
||||
canvas.setAttribute('width', window.innerWidth);
|
||||
canvas.setAttribute('height', window.innerHeight);
|
||||
|
||||
|
||||
fetchShader("../shaders/segfault.glsl")
|
||||
.then(frag =>
|
||||
new Smc(canvas)
|
||||
|
|
|
|||
133
www/js/main.js.bak
Normal file
133
www/js/main.js.bak
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
import { Smc } from "./smc/lib.js"
|
||||
|
||||
main();
|
||||
|
||||
async function fetchShader(uri, delegate) {
|
||||
const res = await fetch(uri);
|
||||
if (res.ok)
|
||||
return await res.text();
|
||||
this.raiseError(
|
||||
SmcErr.FETCH_SHADER,
|
||||
`Failed to load shader source ${url}: ${res.status} ${res.json()}`);
|
||||
return ""
|
||||
}
|
||||
|
||||
|
||||
function animDelta(anim) {
|
||||
const timing = anim.effect.getComputedTiming();
|
||||
return (timing.delay ?? 0) + (timing.duration ?? 0) + (timing.endDelay ?? 0);
|
||||
}
|
||||
|
||||
function main() {
|
||||
const cps = 12; // chars per second
|
||||
const promptDelay = 0.5;
|
||||
const commandDelay = 5;
|
||||
const outputDelay = 5;
|
||||
const blinkTime = 600; // x2 then x1000
|
||||
|
||||
// WARNING: delay does not account for runtime
|
||||
// WARNING: it assumes all computations are down instantaneously
|
||||
var delay = 0;
|
||||
|
||||
const boot = document.querySelector("#boot-ppty");
|
||||
// WARNING: ensure boot != null
|
||||
boot
|
||||
.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");
|
||||
|
||||
// WARNING: ensure prompt|command|output != null
|
||||
const promptAnim = prompt.animate(
|
||||
[{ visibility: "visible" }],
|
||||
{
|
||||
delay: delay + promptDelay * 1000,
|
||||
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 * 1000,
|
||||
easing: `steps(${commandLen}, end)`,
|
||||
fill: "forwards",
|
||||
}
|
||||
);
|
||||
// pause the cursor while typing
|
||||
setTimeout(() => {
|
||||
clearInterval(blinkId);
|
||||
showCursor();
|
||||
cursorVisible = true;
|
||||
}, delay + commandDelay * 1000);
|
||||
delay = animDelta(commandAnim);
|
||||
setTimeout(() => {
|
||||
blinkId = startCursorBlink();
|
||||
}, delay);
|
||||
|
||||
|
||||
setTimeout(() => {
|
||||
clearInterval(blinkId);
|
||||
hideCursor();
|
||||
}, delay + outputDelay * 1000);
|
||||
|
||||
// unhide output
|
||||
output.animate(
|
||||
[{ visibility: "visible" }],
|
||||
{
|
||||
delay: delay + outputDelay * 1000,
|
||||
fill: "forwards",
|
||||
}
|
||||
);
|
||||
delay += outputDelay * 1000;
|
||||
|
||||
// hide the cursor after output displays
|
||||
setTimeout(() => command.style.borderRightColor = "transparent", delay);
|
||||
});
|
||||
|
||||
|
||||
|
||||
const canvas = document.querySelector("#bg-canvas");
|
||||
canvas.setAttribute('width', window.innerWidth);
|
||||
canvas.setAttribute('height', window.innerHeight);
|
||||
|
||||
|
||||
fetchShader("../shaders/segfault.glsl")
|
||||
.then(frag =>
|
||||
new Smc(canvas)
|
||||
.setMaxFps(30)
|
||||
.setProgram(builder =>
|
||||
builder
|
||||
.addFragmentShader(frag))
|
||||
.run()
|
||||
);
|
||||
}
|
||||
|
||||
2
www/js/ppty/lib.js
Normal file
2
www/js/ppty/lib.js
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
import { PPTY } from "./ppty.js";
|
||||
export { PPTY };
|
||||
130
www/js/ppty/ppty.js
Normal file
130
www/js/ppty/ppty.js
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,4 +1,14 @@
|
|||
import { Smc } from "./smc.js";
|
||||
import { SmcErr } from "./errors.js";
|
||||
|
||||
export { Smc, SmcErr };
|
||||
export { Smc, SmcErr, fetchShader };
|
||||
|
||||
async function fetchShader(uri, delegate) {
|
||||
const res = await fetch(uri);
|
||||
if (res.ok)
|
||||
return await res.text();
|
||||
this.raiseError(
|
||||
SmcErr.FETCH_SHADER,
|
||||
`Failed to load shader source ${url}: ${res.status} ${res.json()}`);
|
||||
return ""
|
||||
}
|
||||
|
|
|
|||
|
|
@ -164,10 +164,11 @@ class Smc {
|
|||
var delta = time - this.#prevTimeMs;
|
||||
this.render(time, delta);
|
||||
|
||||
setTimeout(
|
||||
() => requestAnimationFrame(this.renderLoop),
|
||||
Math.max(0, delta - this.#minDeltaTimeMs)
|
||||
);
|
||||
requestAnimationFrame(this.renderLoop);
|
||||
// setTimeout(
|
||||
// () => requestAnimationFrame(this.renderLoop),
|
||||
// Math.max(0, delta - this.#minDeltaTimeMs)
|
||||
// );
|
||||
this.#prevTimeMs = time;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue