Files
pbakaus_impeccable/public/js/utils/scroll.js
T
Paul BakausandClaude Opus 4.6 577c9c872c Overdrive laser demo, deep link support, and split-compare fix
Demo:
- Laser-etched "Paul Bakaus" signature drawn in real-time on dark
  surface, adapted from pbakaus/shaders laser-precision
- Two-canvas architecture: persistent burn canvas + cleared-per-frame
  spark overlay to prevent pixel accumulation
- Smooth quadraticCurveTo rendering for fluid signature strokes
- Sparks with motion trails, multi-layer tip glow

Infrastructure:
- Add init() support for command demos — JS can now execute after
  demo HTML is inserted into the DOM
- Fix split-compare retriggerAnimations to not destroy canvas elements
  (clone-and-replace for CSS-only demos, individual retrigger when
  canvas is present)
- Add deep linking to commands (#cmd-overdrive etc.) with hash
  tracking and initial load support
- Remove auto-#hero hash — hero is the default state

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 11:13:05 -07:00

100 lines
2.9 KiB
JavaScript

// Instant anchor scroll - no smooth scrolling for better UX on long pages
export function initAnchorScroll() {
document.querySelectorAll('a[href^="#"]').forEach((anchor) => {
anchor.addEventListener("click", (e) => {
e.preventDefault();
const target = document.querySelector(anchor.getAttribute("href"));
if (target) {
// Instant jump with small offset for visual breathing room
const offset = 40;
const targetPosition = target.getBoundingClientRect().top + window.scrollY - offset;
window.scrollTo({ top: targetPosition, behavior: 'auto' });
}
});
});
}
export function initHashTracking() {
const sections = document.querySelectorAll('section[id]');
if (!sections.length) return;
let currentHash = window.location.hash.slice(1) || '';
let ticking = false;
function updateHash() {
// Don't override command deep links while user is in the commands section
if (currentHash.startsWith('cmd-')) {
const cmdEl = document.getElementById(currentHash);
if (cmdEl) {
const rect = cmdEl.getBoundingClientRect();
// Only clear the cmd hash if user scrolled well away from commands section
if (rect.top > window.innerHeight * 2 || rect.bottom < -window.innerHeight) {
currentHash = '';
} else {
ticking = false;
return;
}
}
}
const scrollY = window.scrollY;
const viewportHeight = window.innerHeight;
const triggerPoint = scrollY + viewportHeight * 0.3;
let activeSection = '';
sections.forEach(section => {
const rect = section.getBoundingClientRect();
const sectionTop = scrollY + rect.top;
const sectionBottom = sectionTop + rect.height;
if (triggerPoint >= sectionTop && triggerPoint < sectionBottom) {
activeSection = section.id;
}
});
// Don't set #hero — it's the default state, no hash needed
if (activeSection === 'hero') activeSection = '';
if (activeSection !== currentHash) {
currentHash = activeSection;
if (activeSection) {
history.replaceState(null, '', `#${activeSection}`);
} else {
history.replaceState(null, '', window.location.pathname);
}
}
ticking = false;
}
window.addEventListener('scroll', () => {
if (!ticking) {
requestAnimationFrame(updateHash);
ticking = true;
}
}, { passive: true });
// Handle initial hash on page load - instant jump
if (window.location.hash) {
const hash = window.location.hash.slice(1);
const target = document.getElementById(hash);
if (target) {
currentHash = hash;
setTimeout(() => {
const offset = 40;
const targetPosition = target.getBoundingClientRect().top + window.scrollY - offset;
window.scrollTo({ top: targetPosition, behavior: 'auto' });
// If it's a command deep link, activate it
if (hash.startsWith('cmd-') && target.classList.contains('manual-entry')) {
target.click();
}
}, 100);
}
} else {
// No hash — don't set one on initial load
}
}