scratch/src/components/ScrollerAnimate/ScrollerAnimate.svelte
2026-05-12 02:33:24 -04:00

186 lines
4.5 KiB
Svelte
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script lang="ts">
import ScrollerBase from '../ScrollerBase/ScrollerBase.svelte';
import Squash from './Squash.svelte';
import GraphicBlock from '../GraphicBlock/GraphicBlock.svelte';
import Block from '../Block/Block.svelte';
import type {
ContainerWidth,
ForegroundPosition,
} from '../@types/global';
type Graphic = {
src: string;
srcset: string;
alt: string;
title?: string;
description?: string;
notes?: string;
width?: ContainerWidth;
};
interface Props {
graphics: Graphic[];
/** Duration of the squash animation in milliseconds. */
animationDuration?: number;
/** When to swap the image during the animation (01). E.g. 0.8 = swap at 80% through. */
animationPeak?: number;
/** Width of the background */
backgroundWidth?: ContainerWidth;
/** Position of the foreground */
foregroundPosition?: ForegroundPosition;
/** Threshold prop passed to svelte-scroller */
threshold?: number;
/** Top prop passed to svelte-scroller */
top?: number;
/** Bottom prop passed to svelte-scroller */
bottom?: number;
/** Parallax prop passed to svelte-scroller */
parallax?: boolean;
/** ID of the scroller container */
id?: string;
/** Set a class to target with SCSS */
class?: string;
/** The currently active section */
index?: number;
/** How far the section has scrolled past the threshold */
offset?: number;
/** How far the foreground has travelled */
progress?: number;
}
let {
graphics,
animationDuration = 350,
animationPeak = 0.8,
backgroundWidth = 'fluid',
foregroundPosition = 'middle',
threshold = 0.5,
top = 0,
bottom = 1,
parallax = false,
id = '',
class: cls = '',
index = $bindable(0),
offset = $bindable(0),
progress = $bindable(0),
}: Props = $props();
</script>
<Block width="fluid" class="scroller-container {cls}" {id}>
<ScrollerBase
bind:index
bind:offset
bind:progress
{threshold}
{top}
{bottom}
{parallax}
query="div.step-foreground-container"
>
{#snippet backgroundSnippet()}
<div
class="background min-h-screen relative p-0 flex justify-center"
class:right={foregroundPosition === 'left opposite'}
class:left={foregroundPosition === 'right opposite'}
aria-hidden="true"
>
<div class="scroller-graphic-well w-full">
<Block
width={backgroundWidth}
class="background-container my-0 min-h-screen flex justify-center items-center relative"
>
<Squash
{graphics}
{index}
{offset}
{animationDuration}
{animationPeak}
{backgroundWidth}
/>
</Block>
</div>
</div>
{/snippet}
{#snippet foregroundSnippet()}
<div class="foreground {foregroundPosition} w-full">
{#each graphics as graphic}
<div class="step-foreground-container">
<GraphicBlock
title={graphic.title}
description={graphic.description}
notes={graphic.notes}
>
<!-- GraphicBlock requires the children snippet to be defined, but currently, our image is in the background snippet. -->
{''}
</GraphicBlock>
</div>
{/each}
</div>
{/snippet}
</ScrollerBase>
</Block>
<style lang="scss">
div.background {
&.left {
width: 50%;
float: left;
@media (max-width: 1200px) {
justify-content: center;
width: 100%;
float: initial;
}
}
&.right {
width: 50%;
float: right;
@media (max-width: 1200px) {
justify-content: center;
width: 100%;
float: initial;
}
}
div.scroller-graphic-well {
padding: 0 15px;
}
}
div.foreground {
&.right {
width: 50%;
float: right;
@media (max-width: 1200px) {
width: 100%;
float: initial;
}
}
&.left {
width: 50%;
float: left;
@media (max-width: 1200px) {
width: 100%;
float: initial;
}
}
}
.step-foreground-container {
height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
padding: 1em;
margin: 0 auto;
max-width: var(--normal-column-width, 660px);
position: relative;
pointer-events: none;
> :global(*) {
pointer-events: auto;
}
}
</style>