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:
parent
22d9f9672b
commit
5378772d8a
1 changed files with 74 additions and 43 deletions
|
|
@ -69,41 +69,11 @@ html, body {
|
|||
/* ============================================================
|
||||
RETROWAVE GRID FLOOR
|
||||
============================================================ */
|
||||
.floor-wrap {
|
||||
#floorCanvas {
|
||||
position: absolute;
|
||||
bottom: 0; left: 0; right: 0;
|
||||
height: 42%;
|
||||
overflow: hidden;
|
||||
bottom: 0; left: 0;
|
||||
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 {
|
||||
|
|
@ -716,14 +686,12 @@ html, body {
|
|||
<!-- Twinkling starfield -->
|
||||
<canvas id="starfield"></canvas>
|
||||
|
||||
<!-- Retrowave grid floor -->
|
||||
<canvas id="floorCanvas"></canvas>
|
||||
|
||||
<!-- Horizon glow -->
|
||||
<div class="horizon"></div>
|
||||
|
||||
<!-- Retrowave grid floor -->
|
||||
<div class="floor-wrap">
|
||||
<div class="floor"></div>
|
||||
</div>
|
||||
|
||||
<!-- ===================== ROBOT ===================== -->
|
||||
<div class="robot-wrap">
|
||||
<div class="robot">
|
||||
|
|
@ -878,13 +846,76 @@ window.addEventListener('resize', resizeCanvas);
|
|||
resizeCanvas();
|
||||
requestAnimationFrame(drawStars);
|
||||
|
||||
const floorEl = document.querySelector('.floor');
|
||||
function updateFloorPerspective() {
|
||||
const p = Math.round(window.innerHeight * 1.05);
|
||||
floorEl.style.transform = `perspective(${p}px) rotateX(55deg)`;
|
||||
/* ============================================================
|
||||
FLOOR CANVAS — perspective grid with true vanishing point
|
||||
============================================================ */
|
||||
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);
|
||||
|
||||
|
||||
/* ============================================================
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue