Replace CSS floor with canvas perspective grid

Draws vertical lines radiating from a true central vanishing point
and horizontal lines with perspective-correct spacing — giving the
classic retrowave convergence effect instead of parallel lines viewed
from above. Horizon glow line restored above the canvas layer.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
ltadeu6 2026-06-07 08:10:22 -03:00
parent 22d9f9672b
commit 5378772d8a
No known key found for this signature in database
GPG key ID: FB9FDAB6B6F3418D

View file

@ -69,41 +69,11 @@ html, body {
/* ============================================================ /* ============================================================
RETROWAVE GRID FLOOR RETROWAVE GRID FLOOR
============================================================ */ ============================================================ */
.floor-wrap { #floorCanvas {
position: absolute; position: absolute;
bottom: 0; left: 0; right: 0; bottom: 0; left: 0;
height: 42%;
overflow: hidden;
pointer-events: none; pointer-events: none;
} }
.floor {
position: absolute;
bottom: 0;
left: -100%; right: -100%;
height: 400%;
transform: perspective(700px) rotateX(55deg);
transform-origin: bottom center;
background-image:
repeating-linear-gradient(
90deg,
transparent 0,
transparent calc(5% - 2px),
rgba(0, 200, 255, 0.3) calc(5% - 2px),
rgba(0, 200, 255, 0.3) 5%
),
repeating-linear-gradient(
0deg,
transparent 0,
transparent 48px,
rgba(0, 200, 255, 0.2) 48px,
rgba(0, 200, 255, 0.2) 50px
);
animation: gridScroll 0.833s linear infinite;
}
@keyframes gridScroll {
from { background-position: 0 0, 0 0; }
to { background-position: 0 0, 0 50px; }
}
/* Horizon glow line */ /* Horizon glow line */
.horizon { .horizon {
@ -716,14 +686,12 @@ html, body {
<!-- Twinkling starfield --> <!-- Twinkling starfield -->
<canvas id="starfield"></canvas> <canvas id="starfield"></canvas>
<!-- Retrowave grid floor -->
<canvas id="floorCanvas"></canvas>
<!-- Horizon glow --> <!-- Horizon glow -->
<div class="horizon"></div> <div class="horizon"></div>
<!-- Retrowave grid floor -->
<div class="floor-wrap">
<div class="floor"></div>
</div>
<!-- ===================== ROBOT ===================== --> <!-- ===================== ROBOT ===================== -->
<div class="robot-wrap"> <div class="robot-wrap">
<div class="robot"> <div class="robot">
@ -878,13 +846,76 @@ window.addEventListener('resize', resizeCanvas);
resizeCanvas(); resizeCanvas();
requestAnimationFrame(drawStars); requestAnimationFrame(drawStars);
const floorEl = document.querySelector('.floor'); /* ============================================================
function updateFloorPerspective() { FLOOR CANVAS — perspective grid with true vanishing point
const p = Math.round(window.innerHeight * 1.05); ============================================================ */
floorEl.style.transform = `perspective(${p}px) rotateX(55deg)`; const floorCanvas = document.getElementById('floorCanvas');
const floorCtx = floorCanvas.getContext('2d');
const FLOOR_RATIO = 0.42;
const V_LINES = 24;
const H_LINES = 24;
let floorScroll = 0;
let lastFloorTs = 0;
function resizeFloor() {
floorCanvas.width = window.innerWidth;
floorCanvas.height = Math.round(window.innerHeight * FLOOR_RATIO);
} }
window.addEventListener('resize', updateFloorPerspective);
updateFloorPerspective(); function drawFloor(ts) {
const dt = lastFloorTs ? Math.min((ts - lastFloorTs) / 1000, 0.1) : 0;
lastFloorTs = ts;
floorScroll = (floorScroll + 0.06 * dt) % 1;
const W = floorCanvas.width;
const H = floorCanvas.height;
const vx = W / 2;
floorCtx.clearRect(0, 0, W, H);
// Vertical lines radiating from vanishing point (center top)
// Bottom spread exceeds screen width so laterals are filled
const spread = W * 4;
const spreadStart = vx - spread / 2;
floorCtx.strokeStyle = 'rgba(0, 200, 255, 0.32)';
floorCtx.lineWidth = 2;
for (let i = 0; i <= V_LINES; i++) {
const bx = spreadStart + (i / V_LINES) * spread;
floorCtx.beginPath();
floorCtx.moveTo(vx, 0);
floorCtx.lineTo(bx, H);
floorCtx.stroke();
}
// Horizontal lines — perspective-correct spacing, animated
for (let i = 0; i < H_LINES; i++) {
const t = ((i / H_LINES) + floorScroll) % 1;
const depth = Math.pow(t, 2.2); // power curve: compressed near horizon
const y = depth * H;
const x1 = vx - (spread / 2) * depth;
const x2 = vx + (spread / 2) * depth;
floorCtx.strokeStyle = `rgba(0, 200, 255, ${(0.12 + depth * 0.25).toFixed(2)})`;
floorCtx.lineWidth = Math.max(1, depth * 11);
floorCtx.beginPath();
floorCtx.moveTo(x1, y);
floorCtx.lineTo(x2, y);
floorCtx.stroke();
}
// Fade near horizon to hide sub-pixel lines
const fade = floorCtx.createLinearGradient(0, 0, 0, H * 0.3);
fade.addColorStop(0, '#000814');
fade.addColorStop(1, 'rgba(0,8,20,0)');
floorCtx.fillStyle = fade;
floorCtx.fillRect(0, 0, W, H * 0.3);
requestAnimationFrame(drawFloor);
}
window.addEventListener('resize', resizeFloor);
resizeFloor();
requestAnimationFrame(drawFloor);
/* ============================================================ /* ============================================================