adds toggle scrub demo button

This commit is contained in:
MinamiFunakoshiTR 2026-01-12 10:08:09 -05:00
parent 0b33ffa7c3
commit 3ae074ab03
Failed to extract signature
4 changed files with 76 additions and 55 deletions

View file

@ -8,16 +8,17 @@ import IllustratorScreenshot from './assets/illustrator.png';
# HorizontalScroller
The `HorizontalScroller` component is helpful in making horizontal scrolling sections that respond to vertical scroll input. It is flexible in a way that it can horizontally scroll any children content wider than 100vw from one end to the other.
The `HorizontalScroller` component creates a horizontal scrolling section that scrolls through any child content wider than `100vw`.
To scroll any DOM layout wider than the viewport, wrap the content inside the `HorizontalScroller` component. The component will take care of the rest.
To use `HorizontalScroller`, wrap it around the content that you want to horizontally scroll through. The scroll length is controlled by the height of the `HorizontalScroller` container, which is set by the prop `height`. `height` defaults to `200lvh`, but you can adjust this to any valid CSS height value such as `1200px` or `400lvh`.
## Basic demo
To use the `HorizontalScroller` component, import it and provide the children content to scroll. The scroll height defaults to `200lvh`, but you can adjust this to any valid CSS height value such as `1200px` or `200lvh` with the `height` prop.
The child content inside the `HorizontalScroller` must be wider than `100vw` so that there is overflow to horizontal scroll through. By default, only the top `100lvh` of the child content is visible. You can use CSS `transform: translate()` on the child content to adjust its vertical positioning within the visible area.
> 💡TIP: Use `lvh` or `svh` units instead of `vh` unit for the height, as [these units](https://www.w3.org/TR/css-values-4/#large-viewport-size) are more reliable on mobile or other devices where elements such as the address bar toggle between being shown and hidden.
Set the `showDebugInfo` prop to `true` to visualise the scroll progress and other useful information.
[Demo](?path=/story/components-graphics-horizontalscroller--demo)
```svelte
@ -25,13 +26,12 @@ To use the `HorizontalScroller` component, import it and provide the children co
import { HorizontalScroller } from '@reuters-graphics/graphics-components';
</script>
<!-- Optionally set `height` to adjust scroll height -->
<HorizontalScroller height="400lvh">
<div style="width: 200vw; height: 100lvh;">
<!-- Content wider than 100vw -->
<!-- Only the top 100lvh will be visible -->
<!-- Optionally set `height` prop to adjust scroll length. Defaults to `200lvh` -->
<HorizontalScroller>
<!-- Child content wider than 100vw -->
<div style="width: 400vw; height: 100lvh;">
<img
src="path/to/wide-image.jpg"
src="my-wide-image.jpg"
alt="alt text"
style="width: 100%; height: 100%; object-fit: cover; padding: 0; margin: 0;"
/>
@ -39,22 +39,33 @@ To use the `HorizontalScroller` component, import it and provide the children co
</HorizontalScroller>
```
## With stops
## Controlling scroll with stops and easing
The `HorizontalScroller` also allows you to define a set of points to stop or slow down the scrolling at specific intervals using the `stops` prop. This is useful for creating step-based horizontal scrolling experiences.
The `HorizontalScroller` allows you to control the horizontal scroll experience using various props.
The `scrubbed` prop can be used to define whether the scrolling experience should be smooth or tied directly to the scroll position. Setting `scrubbed` to `true` will make the horizontal scroll position directly correspond to the vertical scroll position, while setting it to `false` will create a smooth scrolling effect.
**`stops`:**
If `scrubbed` is set to `false` and `stops` are defined, the scroller will transition smoothly to the next stop when the `Mapped Progress` reaches the midpoint between the two stops. The transition speed is controlled by the `duration` prop (in milliseconds) and the `easing` prop (which accepts any easing function from `svelte/easing` or a custom function based on signature `(t: number) => number`).
`stops` is an optional prop that accepts an array of numbers between `0` and `1` — which corresponds to the scroll `progress` — at which to stop or slow down the scrolling. This is useful for creating progress-based horizontal scrolling experiences.
For example, as shown in the demo below, if you define `stops` as `[0.2, 0.5, 0.6, 0.7]`, the scrolling will pause or slow down at these `progress` values as the user scrolls through the `HorizontalScroller` section.
**`scrubbed`, `duration`, and `easing`:**
The `scrubbed` prop controls whether the scrolling tied exactly to the scroll position (`scrubbed: true`) or is smoothed out (`scrubbed: false`). The prop defaults to `true`.
If `scrubbed` is set to `false` and `stops` are defined, `HorizontalScroller` transitions smoothly between the stop values.
when the `Mapped Progress` reaches the midpoint between the two stops. The transition speed is controlled by the `duration` prop (in milliseconds) and the `easing` prop (which accepts any easing function from `svelte/easing` or a custom function based on signature `(t: number) => number`).
If `scrubbed` is set to `true` and `stops` are defined, all the stops are traversed at equal distance but based on the easing function provided.
Use `showDebugInfo` prop to visualize the scroll progress and other useful debug information. The `Progress` indicates the vertical progress with values in the range 0...1 indicating the content being locked or a user-fed value to control the horizontal scroll position. The `Mapped Progress` value indicates the vertical progress mapped to mappedStart and mappedEnd values. By default these are 0 and 1 respectively. Finally, the `Eased Progress` value indicates the horizontal scroll progress after applying stops and easing (if any). `Eased Progress` accurately reflects the transition of horizontal scroll position.
Feel free to toggle `scrubbed` prop here to see the difference.
`Progress` indicates the scroll progress value between `0` and `1`. The `Mapped Progress` indicates the vertical progress mapped to `mappedStart` and `mappedEnd` values. By default these are 0 and 1 respectively. Finally, the `Eased Progress` value indicates the horizontal scroll progress after applying stops and easing (if any). `Eased Progress` accurately reflects the transition of horizontal scroll position.
[Demo](?path=/story/components-graphics-horizontalscroller--demo)
[Demo](?path=/story/components-graphics-horizontalscroller--with-stops)
```svelte
<script lang="ts">
import { HorizontalScroller } from '@reuters-graphics/graphics-components';

View file

@ -19,9 +19,6 @@
let width: number = $state(0);
</script>
<script>
</script>
<svelte:window bind:innerWidth={width} />
{#snippet DemoSnippet()}
@ -32,36 +29,24 @@
<CustomChildrenBlock />
{/snippet}
<Story
name="Demo"
args={{
children: DemoSnippet,
height: '200lvh',
}}
>
{#snippet children(args)}
<DemoComponent {...args}></DemoComponent>
{/snippet}
<Story name="Demo">
<DemoComponent>
<DemoSnippetBlock />
</DemoComponent>
</Story>
<Story
name="With stops"
args={{
children: DemoSnippet,
height: '200lvh',
stops: [0.2, 0.5, 0.6, 0.7],
duration: 400,
scrubbed: true,
easing: quartInOut,
showDebugInfo: true,
direction: 'left',
}}
>
{#snippet children(args)}
<Story name="With stops and easing" exportName="WithStops">
<Block width="fluid">
<DemoComponent {...args}></DemoComponent>
<DemoComponent
stops={[0.2, 0.5, 0.6, 0.7]}
duration={400}
toggleScrub={true}
easing={quartInOut}
direction="left"
>
<DemoSnippetBlock />
</DemoComponent>
</Block>
{/snippet}
</Story>
<Story

View file

@ -1,8 +1,8 @@
<script lang="ts">
import { onMount, type Snippet } from 'svelte';
import { Tween } from 'svelte/motion';
import type { Action } from 'svelte/action';
import { clamp, map } from './utils/index';
import type { Action } from 'svelte/action';
import Debug from './Debug.svelte';
interface Props {

View file

@ -1,15 +1,40 @@
<script lang="ts">
import ScrollerHorizontal from '../HorizontalScroller.svelte';
import HorizontalScroller from '../HorizontalScroller.svelte';
import BodyText from '../../BodyText/BodyText.svelte';
import Block from '../../Block/Block.svelte';
let { ...args } = $props();
const foobarText: string =
'In the mystical land of Foobaristan, the legendary hero Foo set out on an epic quest to find his missing semicolon, only to discover that Bar had accidentally used it as a bookmark inside a JSON file. Naturally, the entire kingdom crashed immediately. As the villagers panicked, Foo and Bar tried to fix the situation by turning everything off and on again, but all that did was anger the ancient deity known as “The Build System,” which now demanded three sacrifices: a clean cache, a fresh node_modules folder, and someones weekend. And thus began the saga nobody asked for, yet every developer somehow relates to.';
// For the `scrubbed` demo
let scrubbed: boolean = $state(true);
</script>
<BodyText text={foobarText} />
<ScrollerHorizontal {...args} />
{#if args.toggleScrub}
<Block>
<button onclick={() => (scrubbed = !scrubbed)}>
Toggle scrubbed: {scrubbed}
</button>
</Block>
{/if}
<HorizontalScroller showDebugInfo={true} {...args} {scrubbed} />
<BodyText text={foobarText} />
<style lang="scss">
button {
cursor: pointer;
font-family: 'Geist Mono', monospace;
margin-bottom: 10px;
display: block;
padding: 0.5rem 1rem;
background-color: #f0f0f0;
border: 2px solid #ccc;
border-radius: 4px;
}
</style>