update line (#1297)

This commit is contained in:
NotSoToothless
2026-06-04 13:36:27 -07:00
committed by GitHub
parent 66e5a5e068
commit d546f37874
+37 -30
View File
@@ -174,6 +174,8 @@
.timeline-node {
opacity: 0;
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);
}
.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>
<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">
How <%= botName %> grew from a late-night Discord experiment into a
dedicated companion for War Thunder Squadron Battles.
Our history, the milestones we passed along the way, and the giants whose
shoulders helped us climb higher.
</p>
</div>
</section>
@@ -428,8 +430,8 @@
var nodes = Array.prototype.slice.call(timeline.querySelectorAll('.timeline-node'));
var reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
var drawProgress = 0; // 0 1, how much of the line is drawn
var animating = false;
var drawProgress = 0; // 0 to 1, how much of the line is drawn
var ticking = false;
// Build a rounded-corner path that threads through a list of points.
function roundedPath(pts, radius) {
@@ -524,30 +526,33 @@
return best;
}
function animateDraw() {
if (animating) return;
animating = true;
var start = null;
var duration = 7200; // ms for the line to travel the whole serpentine
function step(ts) {
if (start === null) start = ts;
var p = Math.min((ts - start) / duration, 1);
// ease-in-out
drawProgress = p < 0.5 ? 2 * p * p : 1 - Math.pow(-2 * p + 2, 2) / 2;
function clamp(value, min, max) {
return Math.max(min, Math.min(max, value));
}
function updateFromScroll() {
ticking = false;
var len = progress.getTotalLength();
var rect = timeline.getBoundingClientRect();
var viewport = window.innerHeight || document.documentElement.clientHeight;
var start = viewport * 0.35;
var end = -rect.height + viewport * 0.45;
var raw = (start - rect.top) / (start - end);
drawProgress = clamp(raw, 0, 1);
progress.style.strokeDashoffset = len * (1 - drawProgress);
updateComet(len);
revealNodes();
if (p < 1) {
requestAnimationFrame(step);
} else {
drawProgress = 1;
progress.style.strokeDashoffset = 0;
if (drawProgress >= 1) {
comet.style.opacity = 0;
nodes.forEach(function (n) { n.classList.add('is-visible'); });
}
}
requestAnimationFrame(step);
function requestScrollUpdate() {
if (ticking) return;
ticking = true;
requestAnimationFrame(updateFromScroll);
}
function init() {
@@ -558,21 +563,19 @@
nodes.forEach(function (n) { n.classList.add('is-visible'); });
return;
}
// Start drawing only after the user has scrolled into the timeline.
var trigger = nodes[0] || timeline;
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);
updateFromScroll();
window.addEventListener('scroll', requestScrollUpdate, { passive: true });
}
// Recompute geometry on resize (layout flips between 1 and 3 columns).
var resizeTimer;
window.addEventListener('resize', function () {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(function () { _samples = null; build(); }, 120);
resizeTimer = setTimeout(function () {
_samples = null;
build();
if (!reduceMotion) updateFromScroll();
}, 120);
});
if (document.readyState === 'loading') {
@@ -581,7 +584,11 @@
init();
}
// 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>
</body>