Refactor code structure for improved readability and maintainability
This commit is contained in:
parent
cc6249db24
commit
253d6a0c5f
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
@ -17,7 +17,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
/* ═══════════════════════════════════════════
|
/* ═══════════════════════════════════════════
|
||||||
Reusable Car Animation Setup
|
Reusable Car Animation Setup
|
||||||
═══════════════════════════════════════════ */
|
═══════════════════════════════════════════ */
|
||||||
function setupRoadAnimation(svgId, pathId, carId, mainAreaSelector, screenId, speed = 0.00016) {
|
function setupRoadAnimation(svgId, pathId, carId, mainAreaSelector, screenId, speed = 0.00016, pointNames = null) {
|
||||||
const svgEl = document.getElementById(svgId);
|
const svgEl = document.getElementById(svgId);
|
||||||
const pathEl = document.getElementById(pathId);
|
const pathEl = document.getElementById(pathId);
|
||||||
const car = document.getElementById(carId);
|
const car = document.getElementById(carId);
|
||||||
@ -45,6 +45,71 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
return { scale, offsetX, offsetY, rect };
|
return { scale, offsetX, offsetY, rect };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ── Arrival-triggered "point" reveal ──────────
|
||||||
|
Each named point is the marker + leader line + label that's
|
||||||
|
already baked into the road SVG (circle, icon, connector tick and
|
||||||
|
title/desc text paths tagged with ids/`.road-point` in the HTML)
|
||||||
|
— we animate that existing artwork in place rather than
|
||||||
|
positioning a separate HTML copy. It fades in the first time the
|
||||||
|
car's progress along the road reaches it. */
|
||||||
|
let points = [];
|
||||||
|
if (Array.isArray(pointNames) && pointNames.length) {
|
||||||
|
points = pointNames.map(name => {
|
||||||
|
const markerEl = document.getElementById(`point-${name}`);
|
||||||
|
const circleEl = markerEl ? markerEl.querySelector('circle') : null;
|
||||||
|
const extraEls = [
|
||||||
|
document.getElementById(`text-${name}-1`),
|
||||||
|
document.getElementById(`text-${name}-2`),
|
||||||
|
document.getElementById(`tick-${name}`)
|
||||||
|
].filter(Boolean);
|
||||||
|
if (!markerEl || !circleEl) return null;
|
||||||
|
|
||||||
|
const svgX = parseFloat(circleEl.getAttribute('cx'));
|
||||||
|
const svgY = parseFloat(circleEl.getAttribute('cy'));
|
||||||
|
|
||||||
|
// Find the point on the road path nearest to the marker so
|
||||||
|
// "arrival" lines up with where the car visually meets it.
|
||||||
|
let t = 0, bestDist = Infinity;
|
||||||
|
const SAMPLES = 400;
|
||||||
|
for (let i = 0; i <= SAMPLES; i++) {
|
||||||
|
const sampleT = i / SAMPLES;
|
||||||
|
const p = pathEl.getPointAtLength(sampleT * TOTAL_LEN);
|
||||||
|
const dist = (p.x - svgX) ** 2 + (p.y - svgY) ** 2;
|
||||||
|
if (dist < bestDist) { bestDist = dist; t = sampleT; }
|
||||||
|
}
|
||||||
|
|
||||||
|
return { name, t, markerEl, extraEls, revealed: false };
|
||||||
|
}).filter(Boolean).sort((a, b) => a.t - b.t);
|
||||||
|
|
||||||
|
if (typeof gsap !== 'undefined') {
|
||||||
|
points.forEach(p => {
|
||||||
|
gsap.set(p.markerEl, { opacity: 0, scale: 0.5, transformOrigin: '50% 50%' });
|
||||||
|
if (p.extraEls.length) gsap.set(p.extraEls, { opacity: 0 });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function revealPoint(p) {
|
||||||
|
if (p.revealed) return;
|
||||||
|
p.revealed = true;
|
||||||
|
|
||||||
|
if (typeof gsap === 'undefined') {
|
||||||
|
p.markerEl.style.opacity = '1';
|
||||||
|
p.extraEls.forEach(el => { el.style.opacity = '1'; });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
gsap.to(p.markerEl, { opacity: 1, scale: 1, duration: 0.4, ease: 'back.out(1.7)' });
|
||||||
|
if (p.extraEls.length) {
|
||||||
|
gsap.to(p.extraEls, { opacity: 1, duration: 0.4, ease: 'power2.out', delay: 0.1 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkPointArrivals(t) {
|
||||||
|
for (const p of points) {
|
||||||
|
if (!p.revealed && t >= p.t) revealPoint(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let progress = 0;
|
let progress = 0;
|
||||||
const SPEED = speed;
|
const SPEED = speed;
|
||||||
let lastTime = null;
|
let lastTime = null;
|
||||||
@ -104,12 +169,14 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
if (progress >= 1) {
|
if (progress >= 1) {
|
||||||
progress = 1;
|
progress = 1;
|
||||||
placeCar(1);
|
placeCar(1);
|
||||||
|
checkPointArrivals(progress);
|
||||||
cancelAnimationFrame(rafId);
|
cancelAnimationFrame(rafId);
|
||||||
doLoop();
|
doLoop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
placeCar(progress);
|
placeCar(progress);
|
||||||
|
checkPointArrivals(progress);
|
||||||
rafId = requestAnimationFrame(animateCar);
|
rafId = requestAnimationFrame(animateCar);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,7 +195,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
}, 420);
|
}, 420);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ro = new ResizeObserver(() => {});
|
const ro = new ResizeObserver(() => {
|
||||||
|
placeCar(progress);
|
||||||
|
});
|
||||||
ro.observe(mainArea);
|
ro.observe(mainArea);
|
||||||
|
|
||||||
|
|
||||||
@ -155,31 +224,36 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// 2. Stagger fade-in for SVG pointers (navy circles and white icons)
|
// 2. Stagger fade-in for SVG pointers (navy circles and white icons).
|
||||||
const pointers = Array.from(document.querySelectorAll('#' + screenId + ' svg circle, #' + screenId + ' svg path')).filter(el => {
|
// Screens with arrival-triggered points (pointNames) reveal each
|
||||||
const fill = el.getAttribute('fill');
|
// marker individually as the car reaches it instead — see
|
||||||
if (!fill) return false;
|
// revealPoint()/checkPointArrivals() above.
|
||||||
const upper = fill.toUpperCase();
|
if (!points.length) {
|
||||||
return upper === '#0A1A37' || upper === 'WHITE' || upper === '#FFFFFF';
|
const pointers = Array.from(document.querySelectorAll('#' + screenId + ' svg circle, #' + screenId + ' svg path')).filter(el => {
|
||||||
});
|
const fill = el.getAttribute('fill');
|
||||||
|
if (!fill) return false;
|
||||||
gsap.fromTo(pointers,
|
const upper = fill.toUpperCase();
|
||||||
{ opacity: 0, scale: 0, transformOrigin: "50% 50%" },
|
return upper === '#0A1A37' || upper === 'WHITE' || upper === '#FFFFFF';
|
||||||
{
|
});
|
||||||
opacity: 1,
|
|
||||||
scale: 1,
|
gsap.fromTo(pointers,
|
||||||
duration: 0.4,
|
{ opacity: 0, scale: 0, transformOrigin: "50% 50%" },
|
||||||
stagger: 0.15,
|
{
|
||||||
ease: "back.out(1.5)",
|
opacity: 1,
|
||||||
scrollTrigger: {
|
scale: 1,
|
||||||
trigger: screen,
|
duration: 0.4,
|
||||||
start: "top 50%",
|
stagger: 0.15,
|
||||||
end: "bottom 20%",
|
ease: "back.out(1.5)",
|
||||||
toggleActions: "play reverse play reverse"
|
scrollTrigger: {
|
||||||
|
trigger: screen,
|
||||||
|
start: "top 50%",
|
||||||
|
end: "bottom 20%",
|
||||||
|
toggleActions: "play reverse play reverse"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
);
|
||||||
);
|
}
|
||||||
|
|
||||||
// 3. Start car animation when screen is in view
|
// 3. Start car animation when screen is in view
|
||||||
ScrollTrigger.create({
|
ScrollTrigger.create({
|
||||||
trigger: screen,
|
trigger: screen,
|
||||||
@ -191,10 +265,12 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Initialize Screen 1
|
// Initialize Screen 1
|
||||||
setupRoadAnimation('road-svg', 'road-path', 'road-car', '.main-area--prob1', 'screen-prob1', 0.00008);
|
setupRoadAnimation('road-svg', 'road-path', 'road-car', '.main-area--prob1', 'screen-prob1', 0.00008,
|
||||||
|
['receive', 'sort', 'inspect', 'hold', 'route']);
|
||||||
|
|
||||||
// Initialize Screen 2
|
// Initialize Screen 2
|
||||||
setupRoadAnimation('road-svg-2', 'road-path-2', 'road-car-2', '.main-area--prob2', 'screen-prob2', 0.00016);
|
setupRoadAnimation('road-svg-2', 'road-path-2', 'road-car-2', '.main-area--prob2', 'screen-prob2', 0.00016,
|
||||||
|
['scan', 'decision']);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -260,6 +260,12 @@ body {
|
|||||||
filter: drop-shadow(0 4px 12px rgba(100, 140, 200, 0.3));
|
filter: drop-shadow(0 4px 12px rgba(100, 140, 200, 0.3));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Baked-in road markers (circle + icon) — hidden until the car
|
||||||
|
reaches them for the first time; see checkPointArrivals() in JS. */
|
||||||
|
.road-point {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.road-dashes {
|
.road-dashes {
|
||||||
animation: dashFlow 2s linear infinite;
|
animation: dashFlow 2s linear infinite;
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user