training/dashboard(vaporwave): fix grid line flicker

Animating background-position on a perspective-rotated element
re-rasterizes the gradient every frame; with 1px lines under
3D transform, subpixel sampling jitter caused them to flip
on/off between frames.

Fix: split rotate (static) and translate (animated) onto separate
elements, animate transform: translate3d instead, GPU-promote the
grid layer (will-change + translateZ + backface-visibility), and
bump line width to 2px. The repeating-linear-gradient is drawn
once and the layer offset moves it; translating by exactly one
cell-period loops seamlessly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Max Gorog 2026-05-07 23:02:11 -05:00
parent a027be72e7
commit 8ee77e14b5
2 changed files with 37 additions and 13 deletions

View file

@ -160,21 +160,43 @@ body[data-theme="laser"] .bg-laser { display: block; }
.vw-floor {
position: absolute; left: -50%; right: -50%;
top: var(--vw-horizon, 55%); bottom: -10%;
perspective: 800px; overflow: hidden;
perspective: 800px;
overflow: hidden;
}
.vw-floor::before {
content: ''; position: absolute; inset: 0 0 -50% 0;
background-image:
linear-gradient(transparent calc(100% - 1px), var(--c1) calc(100% - 1px)),
linear-gradient(90deg, transparent calc(100% - 1px), var(--c2) calc(100% - 1px));
background-size: 100% var(--vw-grid-size, 80px), var(--vw-grid-size, 80px) 100%;
/* Tilt is static, sits inside the perspective parent. The grid
inside it animates by translateY only, so the rotate transform
never re-rasterizes between frames. */
.vw-floor-tilt {
position: absolute; inset: 0 0 -50% 0;
transform: rotateX(var(--vw-perspective, 62deg));
transform-origin: top center;
animation: vw-floor calc(4s / var(--anim-speed, 1)) linear infinite;
transform-style: preserve-3d;
}
@keyframes vw-floor {
from { background-position: 0 0, 0 0; }
to { background-position: 0 var(--vw-grid-size, 80px), 0 0; }
/* Grid pattern is drawn once via repeating-linear-gradient. Lines
are 2px (less likely to fall between subpixels). The whole layer
is GPU-promoted via will-change/translateZ so the translate
animation is a pure compositor offset, not a re-rasterize. */
.vw-floor-grid {
position: absolute; left: 0; right: 0; top: 0; height: 200%;
background-image:
repeating-linear-gradient(
to bottom,
transparent 0 calc(var(--vw-grid-size, 80px) - 2px),
var(--c1) calc(var(--vw-grid-size, 80px) - 2px) var(--vw-grid-size, 80px)),
repeating-linear-gradient(
to right,
transparent 0 calc(var(--vw-grid-size, 80px) - 2px),
var(--c2) calc(var(--vw-grid-size, 80px) - 2px) var(--vw-grid-size, 80px));
animation: vw-floor-y calc(4s / var(--anim-speed, 1)) linear infinite;
will-change: transform;
backface-visibility: hidden;
transform: translateZ(0);
}
/* Translates by exactly one cell-period so the repeating pattern
loops seamlessly. */
@keyframes vw-floor-y {
from { transform: translate3d(0, 0, 0); }
to { transform: translate3d(0, var(--vw-grid-size, 80px), 0); }
}
.vw-scanlines {
position: absolute; inset: 0; pointer-events: none;

View file

@ -4,7 +4,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>CIS490 — live</title>
<link rel="stylesheet" href="/static/dashboard.css?v=bb44f27a">
<link rel="stylesheet" href="/static/dashboard.css?v=362f2d74">
</head>
<body>
<!-- SVG filter defs for the lava-lamp goo effect. Width/height 0
@ -41,7 +41,9 @@
<div class="vw-sky"></div>
<div class="vw-sun"><div class="vw-sun-blinds"></div></div>
<div class="vw-horizon"></div>
<div class="vw-floor"></div>
<div class="vw-floor">
<div class="vw-floor-tilt"><div class="vw-floor-grid"></div></div>
</div>
<div class="vw-scanlines"></div>
</div>