diff --git a/assets/road-path-new-3.svg b/assets/road-path-new-3.svg
index dba6bc4..d80e56b 100644
--- a/assets/road-path-new-3.svg
+++ b/assets/road-path-new-3.svg
@@ -8,7 +8,7 @@
-
-
+
+
diff --git a/skiply/path-animation-fade.js b/skiply/path-animation-fade.js
index ec56701..5499680 100644
--- a/skiply/path-animation-fade.js
+++ b/skiply/path-animation-fade.js
@@ -17,7 +17,7 @@ document.addEventListener('DOMContentLoaded', () => {
/* ═══════════════════════════════════════════
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 pathEl = document.getElementById(pathId);
const car = document.getElementById(carId);
@@ -45,6 +45,71 @@ document.addEventListener('DOMContentLoaded', () => {
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;
const SPEED = speed;
let lastTime = null;
@@ -104,12 +169,14 @@ document.addEventListener('DOMContentLoaded', () => {
if (progress >= 1) {
progress = 1;
placeCar(1);
+ checkPointArrivals(progress);
cancelAnimationFrame(rafId);
doLoop();
return;
}
placeCar(progress);
+ checkPointArrivals(progress);
rafId = requestAnimationFrame(animateCar);
}
@@ -128,7 +195,9 @@ document.addEventListener('DOMContentLoaded', () => {
}, 420);
}
- const ro = new ResizeObserver(() => {});
+ const ro = new ResizeObserver(() => {
+ placeCar(progress);
+ });
ro.observe(mainArea);
@@ -155,31 +224,36 @@ document.addEventListener('DOMContentLoaded', () => {
}
);
- // 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 => {
- const fill = el.getAttribute('fill');
- if (!fill) return false;
- const upper = fill.toUpperCase();
- return upper === '#0A1A37' || upper === 'WHITE' || upper === '#FFFFFF';
- });
-
- gsap.fromTo(pointers,
- { opacity: 0, scale: 0, transformOrigin: "50% 50%" },
- {
- opacity: 1,
- scale: 1,
- duration: 0.4,
- stagger: 0.15,
- ease: "back.out(1.5)",
- scrollTrigger: {
- trigger: screen,
- start: "top 50%",
- end: "bottom 20%",
- toggleActions: "play reverse play reverse"
+ // 2. Stagger fade-in for SVG pointers (navy circles and white icons).
+ // Screens with arrival-triggered points (pointNames) reveal each
+ // marker individually as the car reaches it instead — see
+ // revealPoint()/checkPointArrivals() above.
+ if (!points.length) {
+ const pointers = Array.from(document.querySelectorAll('#' + screenId + ' svg circle, #' + screenId + ' svg path')).filter(el => {
+ const fill = el.getAttribute('fill');
+ if (!fill) return false;
+ const upper = fill.toUpperCase();
+ return upper === '#0A1A37' || upper === 'WHITE' || upper === '#FFFFFF';
+ });
+
+ gsap.fromTo(pointers,
+ { opacity: 0, scale: 0, transformOrigin: "50% 50%" },
+ {
+ opacity: 1,
+ scale: 1,
+ duration: 0.4,
+ stagger: 0.15,
+ ease: "back.out(1.5)",
+ scrollTrigger: {
+ trigger: screen,
+ start: "top 50%",
+ end: "bottom 20%",
+ toggleActions: "play reverse play reverse"
+ }
}
- }
- );
-
+ );
+ }
+
// 3. Start car animation when screen is in view
ScrollTrigger.create({
trigger: screen,
@@ -191,10 +265,12 @@ document.addEventListener('DOMContentLoaded', () => {
}
// 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
- 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']);
diff --git a/skiply/skiply-fade.css b/skiply/skiply-fade.css
index 58a6511..1f7ea06 100644
--- a/skiply/skiply-fade.css
+++ b/skiply/skiply-fade.css
@@ -260,6 +260,12 @@ body {
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 {
animation: dashFlow 2s linear infinite;
}
diff --git a/skiply/skiply-fade.html b/skiply/skiply-fade.html
index 8267aaf..62ea6cc 100644
--- a/skiply/skiply-fade.html
+++ b/skiply/skiply-fade.html
@@ -36,37 +36,37 @@
@@ -166,18 +166,18 @@