Add hyperspace starfield to landing page
Replaces the static Backstage template page with a canvas-based warp starfield and a glowing "SETH'S TEST" HUD overlay. Self-contained, no external assets; star count is fixed and resize-safe.
This commit is contained in:
@@ -13,7 +13,7 @@ def index():
|
|||||||
return render_template(
|
return render_template(
|
||||||
"index.html",
|
"index.html",
|
||||||
name="sethtest",
|
name="sethtest",
|
||||||
description="Seth doing stuff",
|
description="Warp drive engaged",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+168
-30
@@ -3,50 +3,188 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<title>{{ name }}</title>
|
<title>{{ name }} — warp drive</title>
|
||||||
<style>
|
<style>
|
||||||
|
html,
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-family: Arial, sans-serif;
|
padding: 0;
|
||||||
color: #0f172a;
|
height: 100%;
|
||||||
background: #f8fafc;
|
background: #000;
|
||||||
|
color: #cfe7ff;
|
||||||
|
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
main {
|
canvas#stars {
|
||||||
max-width: 760px;
|
position: fixed;
|
||||||
margin: 6rem auto;
|
inset: 0;
|
||||||
padding: 0 1.5rem;
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
section {
|
.hud {
|
||||||
background: #ffffff;
|
position: fixed;
|
||||||
border: 1px solid #dbe3ee;
|
inset: 0;
|
||||||
border-radius: 8px;
|
display: flex;
|
||||||
padding: 2rem;
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
pointer-events: none;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
.title {
|
||||||
margin-top: 0;
|
font-size: clamp(2.5rem, 8vw, 6rem);
|
||||||
|
letter-spacing: 0.35em;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #e8f6ff;
|
||||||
|
text-shadow:
|
||||||
|
0 0 8px #6cb9ff,
|
||||||
|
0 0 24px #2b7bd6,
|
||||||
|
0 0 64px #103a78;
|
||||||
|
animation: pulse 4s ease-in-out infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
p,
|
.subtitle {
|
||||||
li {
|
margin-top: 1.5rem;
|
||||||
line-height: 1.6;
|
font-size: 0.95rem;
|
||||||
color: #334155;
|
letter-spacing: 0.4em;
|
||||||
|
opacity: 0.7;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meta {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 1rem;
|
||||||
|
left: 1rem;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
letter-spacing: 0.2em;
|
||||||
|
opacity: 0.5;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse {
|
||||||
|
0%,
|
||||||
|
100% {
|
||||||
|
text-shadow:
|
||||||
|
0 0 8px #6cb9ff,
|
||||||
|
0 0 24px #2b7bd6,
|
||||||
|
0 0 64px #103a78;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
text-shadow:
|
||||||
|
0 0 12px #9fd2ff,
|
||||||
|
0 0 36px #4aa0ff,
|
||||||
|
0 0 96px #1d57b3;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<main>
|
<canvas id="stars"></canvas>
|
||||||
<section>
|
<div class="hud">
|
||||||
<h1>{{ name }}</h1>
|
<div class="title">SETH'S TEST</div>
|
||||||
<p>{{ description }}</p>
|
<div class="subtitle">{{ description }}</div>
|
||||||
<ul>
|
</div>
|
||||||
<li>Backed by Flask and Gunicorn</li>
|
<div class="meta">{{ name }} · /health</div>
|
||||||
<li>Container listens on port 8080</li>
|
|
||||||
<li>Health check is available at <code>/health</code></li>
|
<script>
|
||||||
</ul>
|
(function () {
|
||||||
</section>
|
const canvas = document.getElementById("stars");
|
||||||
</main>
|
if (!canvas || !canvas.getContext) return;
|
||||||
|
const ctx = canvas.getContext("2d");
|
||||||
|
if (!ctx) return;
|
||||||
|
|
||||||
|
const STAR_COUNT = 400;
|
||||||
|
const MAX_Z = 1.0;
|
||||||
|
const SPEED = 0.006;
|
||||||
|
const FOCAL = 320;
|
||||||
|
|
||||||
|
let width = 0;
|
||||||
|
let height = 0;
|
||||||
|
let cx = 0;
|
||||||
|
let cy = 0;
|
||||||
|
let stars = [];
|
||||||
|
|
||||||
|
function resize() {
|
||||||
|
width = window.innerWidth;
|
||||||
|
height = window.innerHeight;
|
||||||
|
const dpr = window.devicePixelRatio || 1;
|
||||||
|
canvas.width = Math.floor(width * dpr);
|
||||||
|
canvas.height = Math.floor(height * dpr);
|
||||||
|
canvas.style.width = width + "px";
|
||||||
|
canvas.style.height = height + "px";
|
||||||
|
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
||||||
|
cx = width / 2;
|
||||||
|
cy = height / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
function spawn() {
|
||||||
|
const z = Math.random() * (MAX_Z - 0.2) + 0.2;
|
||||||
|
return {
|
||||||
|
x: (Math.random() - 0.5) * 2,
|
||||||
|
y: (Math.random() - 0.5) * 2,
|
||||||
|
z: z,
|
||||||
|
pz: z
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function reset(s) {
|
||||||
|
s.x = (Math.random() - 0.5) * 2;
|
||||||
|
s.y = (Math.random() - 0.5) * 2;
|
||||||
|
s.z = MAX_Z;
|
||||||
|
s.pz = MAX_Z;
|
||||||
|
}
|
||||||
|
|
||||||
|
function project(x, y, z) {
|
||||||
|
return { sx: (x / z) * FOCAL + cx, sy: (y / z) * FOCAL + cy };
|
||||||
|
}
|
||||||
|
|
||||||
|
function frame() {
|
||||||
|
// Trail effect: fade the previous frame instead of clearing.
|
||||||
|
ctx.fillStyle = "rgba(0, 0, 0, 0.25)";
|
||||||
|
ctx.fillRect(0, 0, width, height);
|
||||||
|
ctx.lineCap = "round";
|
||||||
|
|
||||||
|
for (let i = 0; i < stars.length; i++) {
|
||||||
|
const s = stars[i];
|
||||||
|
s.pz = s.z;
|
||||||
|
s.z -= SPEED;
|
||||||
|
if (s.z <= 0.02) {
|
||||||
|
reset(s);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const p = project(s.x, s.y, s.z);
|
||||||
|
if (
|
||||||
|
p.sx < -50 ||
|
||||||
|
p.sx > width + 50 ||
|
||||||
|
p.sy < -50 ||
|
||||||
|
p.sy > height + 50
|
||||||
|
) {
|
||||||
|
reset(s);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const pp = project(s.x, s.y, s.pz);
|
||||||
|
const k = 1 - s.z / MAX_Z;
|
||||||
|
const w = Math.max(0.4, k * 2.2);
|
||||||
|
ctx.strokeStyle = "rgba(220, 240, 255, " + (0.3 + k * 0.7) + ")";
|
||||||
|
ctx.lineWidth = w;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(pp.sx, pp.sy);
|
||||||
|
ctx.lineTo(p.sx, p.sy);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
requestAnimationFrame(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener("resize", resize);
|
||||||
|
resize();
|
||||||
|
stars = new Array(STAR_COUNT);
|
||||||
|
for (let i = 0; i < STAR_COUNT; i++) stars[i] = spawn();
|
||||||
|
requestAnimationFrame(frame);
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user