update line (#1297)

This commit is contained in:
NotSoToothless
2026-06-04 13:36:27 -07:00
committed by GitHub
parent 66e5a5e068
commit d546f37874
+44 -37
View File
@@ -174,6 +174,8 @@
.timeline-node { .timeline-node {
opacity: 0; opacity: 0;
transform: translateY(28px); transform: translateY(28px);
position: relative;
z-index: 2;
transition: opacity 0.6s ease, transform 0.6s cubic-bezier(0.2, 0.9, 0.25, 1); transition: opacity 0.6s ease, transform 0.6s cubic-bezier(0.2, 0.9, 0.25, 1);
} }
.timeline-node.is-visible { .timeline-node.is-visible {
@@ -196,8 +198,8 @@
<p class="text-muted text-sm font-semibold tracking-[0.25em] uppercase mb-4">Our Story</p> <p class="text-muted text-sm font-semibold tracking-[0.25em] uppercase mb-4">Our Story</p>
<h1 class="text-4xl lg:text-6xl font-extrabold mb-5 gradient-text">The Timeline</h1> <h1 class="text-4xl lg:text-6xl font-extrabold mb-5 gradient-text">The Timeline</h1>
<p class="text-lg text-white/70 max-w-2xl mx-auto"> <p class="text-lg text-white/70 max-w-2xl mx-auto">
How <%= botName %> grew from a late-night Discord experiment into a Our history, the milestones we passed along the way, and the giants whose
dedicated companion for War Thunder Squadron Battles. shoulders helped us climb higher.
</p> </p>
</div> </div>
</section> </section>
@@ -428,8 +430,8 @@
var nodes = Array.prototype.slice.call(timeline.querySelectorAll('.timeline-node')); var nodes = Array.prototype.slice.call(timeline.querySelectorAll('.timeline-node'));
var reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; var reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
var drawProgress = 0; // 0 1, how much of the line is drawn var drawProgress = 0; // 0 to 1, how much of the line is drawn
var animating = false; var ticking = false;
// Build a rounded-corner path that threads through a list of points. // Build a rounded-corner path that threads through a list of points.
function roundedPath(pts, radius) { function roundedPath(pts, radius) {
@@ -524,30 +526,33 @@
return best; return best;
} }
function animateDraw() { function clamp(value, min, max) {
if (animating) return; return Math.max(min, Math.min(max, value));
animating = true; }
var start = null;
var duration = 7200; // ms for the line to travel the whole serpentine function updateFromScroll() {
function step(ts) { ticking = false;
if (start === null) start = ts; var len = progress.getTotalLength();
var p = Math.min((ts - start) / duration, 1); var rect = timeline.getBoundingClientRect();
// ease-in-out var viewport = window.innerHeight || document.documentElement.clientHeight;
drawProgress = p < 0.5 ? 2 * p * p : 1 - Math.pow(-2 * p + 2, 2) / 2; var start = viewport * 0.35;
var len = progress.getTotalLength(); var end = -rect.height + viewport * 0.45;
progress.style.strokeDashoffset = len * (1 - drawProgress); var raw = (start - rect.top) / (start - end);
updateComet(len);
revealNodes(); drawProgress = clamp(raw, 0, 1);
if (p < 1) { progress.style.strokeDashoffset = len * (1 - drawProgress);
requestAnimationFrame(step); updateComet(len);
} else { revealNodes();
drawProgress = 1; if (drawProgress >= 1) {
progress.style.strokeDashoffset = 0; comet.style.opacity = 0;
comet.style.opacity = 0; nodes.forEach(function (n) { n.classList.add('is-visible'); });
nodes.forEach(function (n) { n.classList.add('is-visible'); });
}
} }
requestAnimationFrame(step); }
function requestScrollUpdate() {
if (ticking) return;
ticking = true;
requestAnimationFrame(updateFromScroll);
} }
function init() { function init() {
@@ -558,21 +563,19 @@
nodes.forEach(function (n) { n.classList.add('is-visible'); }); nodes.forEach(function (n) { n.classList.add('is-visible'); });
return; return;
} }
// Start drawing only after the user has scrolled into the timeline. updateFromScroll();
var trigger = nodes[0] || timeline; window.addEventListener('scroll', requestScrollUpdate, { passive: true });
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (e) {
if (e.isIntersecting) { animateDraw(); io.disconnect(); }
});
}, { threshold: 0.65, rootMargin: '0px 0px -16% 0px' });
io.observe(trigger);
} }
// Recompute geometry on resize (layout flips between 1 and 3 columns). // Recompute geometry on resize (layout flips between 1 and 3 columns).
var resizeTimer; var resizeTimer;
window.addEventListener('resize', function () { window.addEventListener('resize', function () {
clearTimeout(resizeTimer); clearTimeout(resizeTimer);
resizeTimer = setTimeout(function () { _samples = null; build(); }, 120); resizeTimer = setTimeout(function () {
_samples = null;
build();
if (!reduceMotion) updateFromScroll();
}, 120);
}); });
if (document.readyState === 'loading') { if (document.readyState === 'loading') {
@@ -581,7 +584,11 @@
init(); init();
} }
// Re-measure after fonts/icons settle so markers are positioned correctly. // Re-measure after fonts/icons settle so markers are positioned correctly.
window.addEventListener('load', function () { _samples = null; build(); }); window.addEventListener('load', function () {
_samples = null;
build();
if (!reduceMotion) updateFromScroll();
});
})(); })();
</script> </script>
</body> </body>