Chore/architecture refactoring #42
@@ -83,9 +83,15 @@ async function handleJump(targetIndex: number) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Debounce wait before asking the font lifecycle manager to load fonts
|
||||||
|
* for the current visible window. Coalesces rapid scroll into one batch.
|
||||||
|
*/
|
||||||
|
const TOUCH_DEBOUNCE_MS = 150;
|
||||||
|
|
||||||
const debouncedTouch = debounce((configs: FontLoadRequestConfig[]) => {
|
const debouncedTouch = debounce((configs: FontLoadRequestConfig[]) => {
|
||||||
fontLifecycleManager.touch(configs);
|
fontLifecycleManager.touch(configs);
|
||||||
}, 150);
|
}, TOUCH_DEBOUNCE_MS);
|
||||||
|
|
||||||
// Re-touch whenever visible set or weight changes — fixes weight-change gap
|
// Re-touch whenever visible set or weight changes — fixes weight-change gap
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
|
|||||||
@@ -28,6 +28,12 @@
|
|||||||
|
|
||||||
import { Spring } from 'svelte/motion';
|
import { Spring } from 'svelte/motion';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spring tuning for the perspective animation. Lower stiffness = slower
|
||||||
|
* easing into back/front state; higher damping = less overshoot.
|
||||||
|
*/
|
||||||
|
const PERSPECTIVE_SPRING_CONFIG = { stiffness: 0.2, damping: 0.8 } as const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configuration options for perspective effects
|
* Configuration options for perspective effects
|
||||||
*/
|
*/
|
||||||
@@ -93,10 +99,7 @@ export class PerspectiveManager {
|
|||||||
* Spring animation state
|
* Spring animation state
|
||||||
* Animates between 0 (front) and 1 (back) with configurable physics
|
* Animates between 0 (front) and 1 (back) with configurable physics
|
||||||
*/
|
*/
|
||||||
spring = new Spring(0, {
|
spring = new Spring(0, PERSPECTIVE_SPRING_CONFIG);
|
||||||
stiffness: 0.2,
|
|
||||||
damping: 0.8,
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reactive state: true when in back position
|
* Reactive state: true when in back position
|
||||||
|
|||||||
@@ -167,17 +167,33 @@ $effect(() => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throttle for visible-items change callbacks. Lower = more responsive
|
||||||
|
* downstream UI; higher = fewer recomputes during scroll.
|
||||||
|
*/
|
||||||
|
const VISIBLE_CHANGE_THROTTLE_MS = 150;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throttle for near-bottom callbacks (typically used to prefetch next page).
|
||||||
|
*/
|
||||||
|
const NEAR_BOTTOM_THROTTLE_MS = 200;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throttle for jump callbacks (programmatic scroll-to-index).
|
||||||
|
*/
|
||||||
|
const JUMP_THROTTLE_MS = 200;
|
||||||
|
|
||||||
const throttledVisibleChange = throttle((visibleItems: T[]) => {
|
const throttledVisibleChange = throttle((visibleItems: T[]) => {
|
||||||
onVisibleItemsChange?.(visibleItems);
|
onVisibleItemsChange?.(visibleItems);
|
||||||
}, 150); // 150ms throttle
|
}, VISIBLE_CHANGE_THROTTLE_MS);
|
||||||
|
|
||||||
const throttledNearBottom = throttle((lastVisibleIndex: number) => {
|
const throttledNearBottom = throttle((lastVisibleIndex: number) => {
|
||||||
onNearBottom?.(lastVisibleIndex);
|
onNearBottom?.(lastVisibleIndex);
|
||||||
}, 200); // 200ms throttle
|
}, NEAR_BOTTOM_THROTTLE_MS);
|
||||||
|
|
||||||
const throttledOnJump = throttle((targetIndex: number) => {
|
const throttledOnJump = throttle((targetIndex: number) => {
|
||||||
onJump?.(targetIndex);
|
onJump?.(targetIndex);
|
||||||
}, 200);
|
}, JUMP_THROTTLE_MS);
|
||||||
|
|
||||||
// Calculate top/bottom padding for spacer elements
|
// Calculate top/bottom padding for spacer elements
|
||||||
// In CSS Grid, gap creates space BETWEEN elements.
|
// In CSS Grid, gap creates space BETWEEN elements.
|
||||||
|
|||||||
@@ -50,6 +50,26 @@ interface Props {
|
|||||||
|
|
||||||
let { isSidebarOpen = false, class: className }: Props = $props();
|
let { isSidebarOpen = false, class: className }: Props = $props();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spring tuning for the comparison slider thumb. Lower stiffness = slower
|
||||||
|
* follow; higher damping = less overshoot.
|
||||||
|
*/
|
||||||
|
const SLIDER_SPRING_CONFIG = { stiffness: 0.2, damping: 0.7 } as const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Debounce wait before persisting the slider position to the store.
|
||||||
|
* High frequency during drag → batched writes.
|
||||||
|
*/
|
||||||
|
const SLIDER_PERSIST_DEBOUNCE_MS = 100;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Horizontal layout padding subtracted from container width before laying
|
||||||
|
* out the comparison text. Different per breakpoint to match the gutters
|
||||||
|
* around the slider track.
|
||||||
|
*/
|
||||||
|
const SLIDER_PADDING_MOBILE_PX = 48;
|
||||||
|
const SLIDER_PADDING_DESKTOP_PX = 96;
|
||||||
|
|
||||||
const fontA = $derived(comparisonStore.fontA);
|
const fontA = $derived(comparisonStore.fontA);
|
||||||
const fontB = $derived(comparisonStore.fontB);
|
const fontB = $derived(comparisonStore.fontB);
|
||||||
const isLoading = $derived(comparisonStore.isLoading || !comparisonStore.isReady);
|
const isLoading = $derived(comparisonStore.isLoading || !comparisonStore.isReady);
|
||||||
@@ -89,10 +109,7 @@ $effect(() => {
|
|||||||
return () => observer.disconnect();
|
return () => observer.disconnect();
|
||||||
});
|
});
|
||||||
|
|
||||||
const sliderSpring = new Spring(50, {
|
const sliderSpring = new Spring(50, SLIDER_SPRING_CONFIG);
|
||||||
stiffness: 0.2,
|
|
||||||
damping: 0.7,
|
|
||||||
});
|
|
||||||
const sliderPos = $derived(sliderSpring.current);
|
const sliderPos = $derived(sliderSpring.current);
|
||||||
|
|
||||||
function handleMove(e: PointerEvent) {
|
function handleMove(e: PointerEvent) {
|
||||||
@@ -115,7 +132,7 @@ function startDragging(e: PointerEvent) {
|
|||||||
|
|
||||||
const storeSliderPosition = debounce((value: number) => {
|
const storeSliderPosition = debounce((value: number) => {
|
||||||
comparisonStore.sliderPosition = value;
|
comparisonStore.sliderPosition = value;
|
||||||
}, 100);
|
}, SLIDER_PERSIST_DEBOUNCE_MS);
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
storeSliderPosition(sliderPos);
|
storeSliderPosition(sliderPos);
|
||||||
@@ -172,7 +189,7 @@ $effect(() => {
|
|||||||
const fontAStr = getPretextFontString(_weight, _size, fontA.name);
|
const fontAStr = getPretextFontString(_weight, _size, fontA.name);
|
||||||
const fontBStr = getPretextFontString(_weight, _size, fontB.name);
|
const fontBStr = getPretextFontString(_weight, _size, fontB.name);
|
||||||
|
|
||||||
const padding = _isMobile ? 48 : 96;
|
const padding = _isMobile ? SLIDER_PADDING_MOBILE_PX : SLIDER_PADDING_DESKTOP_PX;
|
||||||
const availableWidth = Math.max(0, _width - padding);
|
const availableWidth = Math.max(0, _width - padding);
|
||||||
const lineHeight = _size * _height;
|
const lineHeight = _size * _height;
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,12 @@ const SAMPLER_CONTENT_PADDING_X = 32;
|
|||||||
// Matches the previous hardcoded itemHeight={220} value to avoid regressions.
|
// Matches the previous hardcoded itemHeight={220} value to avoid regressions.
|
||||||
const SAMPLER_FALLBACK_HEIGHT = 220;
|
const SAMPLER_FALLBACK_HEIGHT = 220;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throttle for the `checkPosition` scroll observer. Trades responsiveness
|
||||||
|
* of the perspective tilt against scroll-handler cost.
|
||||||
|
*/
|
||||||
|
const CHECK_POSITION_THROTTLE_MS = 100;
|
||||||
|
|
||||||
let text = $state('The quick brown fox jumps over the lazy dog...');
|
let text = $state('The quick brown fox jumps over the lazy dog...');
|
||||||
let wrapper = $state<HTMLDivElement | null>(null);
|
let wrapper = $state<HTMLDivElement | null>(null);
|
||||||
// Binds to the actual window height
|
// Binds to the actual window height
|
||||||
@@ -54,7 +60,7 @@ const checkPosition = throttle(() => {
|
|||||||
const viewportMiddle = innerHeight / 2;
|
const viewportMiddle = innerHeight / 2;
|
||||||
|
|
||||||
isAboveMiddle = rect.top < viewportMiddle;
|
isAboveMiddle = rect.top < viewportMiddle;
|
||||||
}, 100);
|
}, CHECK_POSITION_THROTTLE_MS);
|
||||||
|
|
||||||
// Resolver recreated when typography values change. The returned closure reads
|
// Resolver recreated when typography values change. The returned closure reads
|
||||||
// fontLifecycleManager.statuses (a SvelteMap) on every call, so any font status
|
// fontLifecycleManager.statuses (a SvelteMap) on every call, so any font status
|
||||||
|
|||||||
Reference in New Issue
Block a user