Skip to content

Commit 5635d40

Browse files
authored
fix: scroll behaviour in code overlay block (#365)
1 parent b62a657 commit 5635d40

File tree

3 files changed

+70
-66
lines changed

3 files changed

+70
-66
lines changed

pkgs/website/src/components/CodeOverlay.astro

Lines changed: 70 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,9 @@
256256
padding: 0;
257257
scrollbar-width: thin;
258258
scrollbar-color: var(--sl-color-accent) var(--sl-color-gray-5);
259+
scroll-behavior: auto; /* Ensure standard scrolling, not smooth */
260+
-webkit-overflow-scrolling: auto; /* Disable momentum scrolling on iOS/Safari */
261+
overscroll-behavior: contain; /* Prevent scroll chaining */
259262
}
260263

261264
/* Remove borders from code blocks inside scrollable area since container has border */
@@ -308,6 +311,10 @@
308311
// Flag to track if manual scroll event has been sent (only send once)
309312
let hasTrackedManualScroll = false;
310313

314+
// Animation frame ID to cancel animation if needed
315+
let scrollAnimationFrame: number | null = null;
316+
let isAnimating = false;
317+
311318
const handleRevealButton = () => {
312319
const button = document.querySelector('.reveal-button') as HTMLElement | null;
313320
const overlay = document.querySelector('.code-overlay') as HTMLElement | null;
@@ -316,35 +323,86 @@
316323

317324
if (!button || !overlay || !scrollableContainer || !scrollableInner) return;
318325

326+
// Function to cancel animation
327+
const cancelAnimation = () => {
328+
if (isAnimating) {
329+
isAnimating = false;
330+
isProgrammaticScroll = false;
331+
if (scrollAnimationFrame) {
332+
cancelAnimationFrame(scrollAnimationFrame);
333+
scrollAnimationFrame = null;
334+
}
335+
// Make sure reset button is visible when animation is interrupted
336+
const resetButton = document.querySelector('.reset-overlay-button') as HTMLElement;
337+
if (resetButton) {
338+
resetButton.style.display = 'block';
339+
}
340+
}
341+
};
342+
343+
// Listen for wheel events to cancel animation on manual scroll
344+
const wheelHandler = (e: WheelEvent) => {
345+
if (isAnimating) {
346+
cancelAnimation();
347+
}
348+
};
349+
350+
// Listen for touch events to cancel animation on touch devices
351+
const touchHandler = (e: TouchEvent) => {
352+
if (isAnimating) {
353+
cancelAnimation();
354+
}
355+
};
356+
319357
button.addEventListener('click', () => {
320358
// Event tracking handled by CSS class: plausible-event-name=home:reveal-code
321359
scrollableContainer.classList.add('scrollable-enabled');
322360
overlay.style.opacity = '0';
323361

362+
// Add wheel and touch event listeners to detect manual scrolling
363+
scrollableInner.addEventListener('wheel', wheelHandler, { passive: true });
364+
scrollableInner.addEventListener('touchstart', touchHandler, { passive: true });
365+
324366
// Custom 3-second scroll animation
325367
const startScroll = scrollableInner.scrollTop;
326368
const targetScroll = scrollableInner.scrollHeight;
327369
const distance = targetScroll - startScroll;
328370
const duration = 3000; // 3 seconds
329371
const startTime = performance.now();
372+
isAnimating = true;
373+
isProgrammaticScroll = true; // Keep this true for entire animation
330374

331375
function easeOutCubic(t: number) {
332376
return 1 - Math.pow(1 - t, 3);
333377
}
334378

335379
function animateScroll(currentTime: number) {
380+
if (!isAnimating) {
381+
// Animation was cancelled
382+
isProgrammaticScroll = false;
383+
scrollableInner.removeEventListener('wheel', wheelHandler);
384+
scrollableInner.removeEventListener('touchstart', touchHandler);
385+
return;
386+
}
387+
336388
const elapsed = currentTime - startTime;
337389
const progress = Math.min(elapsed / duration, 1);
338390
const easedProgress = easeOutCubic(progress);
339391

340392
scrollableInner.scrollTop = startScroll + (distance * easedProgress);
341393

342394
if (progress < 1) {
343-
requestAnimationFrame(animateScroll);
395+
scrollAnimationFrame = requestAnimationFrame(animateScroll);
396+
} else {
397+
isAnimating = false;
398+
isProgrammaticScroll = false;
399+
scrollAnimationFrame = null;
400+
scrollableInner.removeEventListener('wheel', wheelHandler);
401+
scrollableInner.removeEventListener('touchstart', touchHandler);
344402
}
345403
}
346404

347-
requestAnimationFrame(animateScroll);
405+
scrollAnimationFrame = requestAnimationFrame(animateScroll);
348406

349407
setTimeout(() => overlay.classList.add('hidden'), 200);
350408
setTimeout(() => {
@@ -363,7 +421,7 @@
363421
// Show/hide button based on scroll position
364422
const updateButtonVisibility = () => {
365423
// Track manual scroll event (only once, and only if not programmatic)
366-
if (!hasTrackedManualScroll && !isProgrammaticScroll) {
424+
if (!hasTrackedManualScroll && !isProgrammaticScroll && !isAnimating) {
367425
hasTrackedManualScroll = true;
368426
if (typeof window.plausible !== 'undefined') {
369427
window.plausible('home:code-scroll');
@@ -378,7 +436,7 @@
378436
};
379437

380438
// Listen to scroll events
381-
scrollableInner.addEventListener('scroll', updateButtonVisibility);
439+
scrollableInner.addEventListener('scroll', updateButtonVisibility, { passive: true });
382440

383441
button.addEventListener('click', () => {
384442
// Mark as programmatic scroll to avoid tracking
@@ -403,6 +461,14 @@
403461
button.addEventListener('click', () => {
404462
const scrollTopButton = document.querySelector('.scroll-top-button') as HTMLElement | null;
405463

464+
// Cancel any ongoing animation
465+
isAnimating = false;
466+
isProgrammaticScroll = false; // Reset programmatic flag
467+
if (scrollAnimationFrame) {
468+
cancelAnimationFrame(scrollAnimationFrame);
469+
scrollAnimationFrame = null;
470+
}
471+
406472
// Reset to initial state
407473
overlay.classList.remove('hidden');
408474
overlay.style.opacity = '1';

pkgs/website/src/components/SlowScroll.astro

Lines changed: 0 additions & 58 deletions
This file was deleted.

pkgs/website/src/content/docs/index.mdx

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ editUrl: false
2424
import { Card, CardGrid, LinkCard, Aside, Tabs, TabItem } from '@astrojs/starlight/components';
2525
import TestimonialCarousel from '../../components/TestimonialCarousel.astro';
2626
import CodeOverlay from '../../components/CodeOverlay.astro';
27-
import SlowScroll from '../../components/SlowScroll.astro';
2827

2928
<div class="comparison-container">
3029

@@ -678,7 +677,6 @@ Zero boilerplate required."
678677
overflow-y: auto !important;
679678
overflow-x: hidden !important;
680679
flex: 1 !important;
681-
-webkit-overflow-scrolling: touch;
682680
padding: 0.5rem !important;
683681
min-height: 0 !important;
684682
max-height: none !important;
@@ -832,5 +830,3 @@ Zero boilerplate required."
832830
}
833831
`}</style>
834832

835-
<SlowScroll />
836-

0 commit comments

Comments
 (0)