diff --git a/plugin/contents/ui/dancer.html b/plugin/contents/ui/dancer.html index c521641..36321d9 100644 --- a/plugin/contents/ui/dancer.html +++ b/plugin/contents/ui/dancer.html @@ -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 { + + +
- -
-
-
-
@@ -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); /* ============================================================