fixes component docs path issue + adds advanced usage story

This commit is contained in:
Sudev Kiyada 2025-07-23 20:49:50 +05:30
parent d76d9defaf
commit 92e850166e
Failed to extract signature
6 changed files with 326 additions and 20 deletions

View file

@ -130,7 +130,7 @@
z-index: 3;
margin: 0;
width: 50vmin;
min-width: 30vmin;
min-width: 50vmin;
padding: 8px;
border-radius: 8px;
overflow: auto;
@ -178,7 +178,7 @@
display: grid;
width: 100%;
padding: 8px 8px 16px 8px;
grid-template-columns: 10vmin 1fr;
grid-template-columns: 20vmin 1fr;
align-items: center;
gap: 0.75rem 0.25rem;
background-color: #1e1e1e;

View file

@ -2,7 +2,7 @@ import { Meta } from '@storybook/blocks';
import * as ScrollyVideoStories from './ScrollyVideo.stories.svelte';
<Meta of={ScrollyVideoStories} />;
<Meta of={ScrollyVideoStories} />
# ScrollyVideo
@ -31,7 +31,7 @@ When using the `ScrollyVideo` component, minimise the video file size and ensure
To optimise your video for the web, you can use `ffmpeg` to convert the video to a suitable format. Here is an example terminal command that converts a video to H.264 format with a resolution of 720p and a frame rate of 24 FPS:
```bash
npx ffmpeg -y -i <input_video_src>.mp4 -c:v libx264 -movflags faststart -crf 20 -r 24 -vf scale=720:-1 -profile:v high -preset veryslow -level:v 4.1 -color_primaries 1 -color_trc 1 -colorspace 1 -an <output_video>.mp4
npx ffmpeg -y -i <input_video_src>.mp4 -c:v libx264 -movflags faststart -crf 20 -r 24 -vf scale=720:-1 -profile:v baseline -preset veryslow -level:v 4.1 -color_primaries 1 -color_trc 1 -colorspace 1 -an <output_video>.mp4
```
Adjust the `-crf` value to control the quality. Lower `-crf` value means higher quality, with 20 being a generally good balance. See [Testing Media Capabilities](https://cconcolato.github.io/media-mime-support/mediacapabilities.html) for more.
@ -126,9 +126,7 @@ With the graphics kit, you'll likely get your text and prop values from an Archi
position: bottom center # Position of the text. Optional; defaults to 'center center'. Must be a combination of `top/bottom/center center/left/right`
backgroundColour: rgba(0, 0, 0, 0.8) # Optional; defaults to white
text: #### The Alps
The Alps stretch across eight countries: France, Switzerland, Italy, Monaco, Liechtenstein, Austria, Germany, and Slovenia, covering about 1,200 kilometers (750 miles).
:end
startTime: 9
@ -137,7 +135,6 @@ With the graphics kit, you'll likely get your text and prop values from an Archi
position: bottom center
backgroundColour: rgba(0, 0, 0, 0.8)
text: Mont Blanc, standing at 4,809 meters (15,777 feet), is the highest peak in the Alps and Western Europe, though there's ongoing debate between France and Italy about exactly where the summit lies.
:end
startTime: 16
@ -146,9 +143,7 @@ With the graphics kit, you'll likely get your text and prop values from an Archi
position: bottom center
backgroundColour: rgba(0, 0, 0, 0.8)
text: #### History
The Alps were formed around **65 million years** ago when the African and Eurasian tectonic plates collided, pushing the land upward.Over 14 million people live in the Alpine region, with tourism supporting approximately 120 million visitors annually.
:end
[]
[]
@ -392,14 +387,176 @@ The `ScrollyVideo` component can be used inside the [ScrollerBase](?path=/story/
For advanced use cases such as looping a particular section of the video, or jumping to a specific time in the video, you can bind `scrollyVideo` prop and take benefits of methods such as `setVideoPercentage` or bindable methods such as `onReady` and `onChange`. This allows for fine-grained control over the video playback and interaction with the scroll position.
[Demo](?path=/story/components-graphics-scrollyvideo--advanced)
```js
scrollyVideo.setVideoPercentage(0.5, {
jump: false,
transitionSpeed: 12,
easing: d3.easeLinear,
jump: true,
});
```
This will set the video to 50% progress, with a smooth transition at a playback rate of 12, using a linear easing function.
This will seek the video to 50% progress at a single instant. Setting `jump` to `false` will make smooth transition to the provided progress value.
[Demo](?path=/story/components-graphics-scrollyvideo--advanced)
```svelte
<script lang="ts">
import {
ScrollyVideo,
ScrollerBase,
} from '@reuters-graphics/graphics-components';
import { onDestroy } from 'svelte';
let progress = $state(0);
let scrollyVideo = $state();
let now;
let then = 0;
let time = 0;
let currentProgress = 0; // holds progress value for dynamic looping
let loopCutoff = 0.33; // value between 0-1 to loop the video by
let totalTime = 9 * 1000; // milliseconds
let animationId;
// clamps n value between low and high
function constrain(n, low, high) {
return Math.max(Math.min(n, high), low);
}
// maps n value between two ranges
function map(n, start1, stop1, start2, stop2, withinBounds = true) {
const newval =
((n - start1) / (stop1 - start1)) * (stop2 - start2) + start2;
if (!withinBounds) {
return newval;
}
if (start2 < stop2) {
return constrain(newval, start2, stop2);
} else {
return constrain(newval, stop2, start2);
}
}
// loops the video between 0 and loopCutoff
function renderVideo() {
if (progress < loopCutoff) {
now = Date.now();
const elapsed = now - then;
// if (elapsed > fpsInterval) {
time += elapsed;
currentProgress = map(time, 0, totalTime, 0, 1);
scrollyVideo.setVideoPercentage(currentProgress, { jump: true });
if (currentProgress > loopCutoff) {
currentProgress = 0;
time = 0;
scrollyVideo.setVideoPercentage(0, { jump: true });
}
then = now;
// }
} else {
scrollyVideo.setVideoPercentage(progress, { jump: true });
}
animationId = requestAnimationFrame(renderVideo);
}
// initializes video autoplay
// when it's ready to play
function initAutoplay() {
then = Date.now();
renderVideo();
}
// cancel RAF on destroy
onDestroy(() => {
if (animationId) {
cancelAnimationFrame(animationId);
}
});
</script>
<ScrollerBase bind:progress query="div.step-foreground-container" visible>
{#snippet backgroundSnippet()}
<ScrollyVideo
bind:scrollyVideo
src="my-video.mp4"
height="100svh"
trackScroll={false}
showDebugInfo
onReady={initAutoplay}
/>
<!-- Only for debugging -->
<div id="progress-bar">
<p>ScrollerBase progress: {progress.toPrecision(2)}</p>
<progress class="mb-4" value={progress}></progress>
</div>
{/snippet}
{#snippet foregroundSnippet()}
<!-- Add custom foreground HTML or component -->
<div class="step-foreground-container">
<h3 class="text-center">Step 1</h3>
</div>
<div class="step-foreground-container">
<h3 class="text-center">Step 2</h3>
</div>
<div class="step-foreground-container">
<h3 class="text-center">Step 3</h3>
</div>
{/snippet}
</ScrollerBase>
<style lang="scss">
@use '../../../scss/mixins' as mixins;
// svelte-scroller-background
#progress-bar {
background-color: rgba(0, 0, 0, 0.8);
position: absolute;
z-index: 4;
right: 0;
padding: 1rem;
top: 0;
progress {
height: 6px;
background-color: #ff000044; /* Background color of the entire bar */
margin: 0;
}
progress::-webkit-progress-value {
background-color: white;
border-radius: 10px;
}
progress::-webkit-progress-bar {
background-color: #444444;
border-radius: 10px;
}
p {
font-family: var(--theme-font-family-sans-serif);
color: white;
font-size: var(--theme-font-size-xs);
padding: 0;
margin: 0;
}
}
.step-foreground-container {
height: 100vh;
width: 50%;
padding: 1em;
margin: auto;
h3 {
// align center
display: flex;
align-items: center;
justify-content: center;
height: 100%;
color: white;
}
}
</style>
```

View file

@ -8,9 +8,6 @@
const { Story } = defineMeta({
title: 'Components/Graphics/ScrollyVideo',
component: ScrollyVideo,
parameters: {
docsTools: { remount: true },
},
argTypes: {
autoplay: {
control: 'boolean',

View file

@ -1,5 +1,157 @@
<script lang="ts">
/** Sudev -- Can you add a demo for the advanced use cases? I'm not sure what the use case would be and seeing an example would help*/
import ScrollyVideo from '../ScrollyVideo.svelte';
import ScrollerBase from '../../ScrollerBase/ScrollerBase.svelte';
import Tennis from '../videos/tennis.mp4';
import { onDestroy } from 'svelte';
let progress = $state(0);
let scrollyVideo = $state();
let now;
let then = 0;
let time = 0;
let currentProgress = 0; // holds progress value for dynamic looping
let loopCutoff = 0.35; // value between 0-1 to loop the video by
let totalTime = 9 * 1000; // milliseconds
let animationId;
// clamps n value between low and high
function constrain(n, low, high) {
return Math.max(Math.min(n, high), low);
}
// maps n value between two ranges
function map(n, start1, stop1, start2, stop2, withinBounds = true) {
const newval =
((n - start1) / (stop1 - start1)) * (stop2 - start2) + start2;
if (!withinBounds) {
return newval;
}
if (start2 < stop2) {
return constrain(newval, start2, stop2);
} else {
return constrain(newval, stop2, start2);
}
}
// loops the video between 0 and loopCutoff
function renderVideo() {
if (progress < loopCutoff) {
now = Date.now();
const elapsed = now - then;
time += elapsed;
currentProgress = map(time, 0, totalTime, 0, 1);
scrollyVideo.setVideoPercentage(currentProgress, { jump: true });
if (currentProgress > loopCutoff) {
currentProgress = 0;
time = 0;
scrollyVideo.setVideoPercentage(0, { jump: true });
}
then = now;
} else {
scrollyVideo.setVideoPercentage(progress, { jump: true });
}
animationId = requestAnimationFrame(renderVideo);
}
// initializes video autoplay
// when it's ready to play
function initAutoplay() {
then = Date.now();
renderVideo();
}
// cancel RAF on destroy
onDestroy(() => {
if (animationId) {
cancelAnimationFrame(animationId);
}
});
</script>
TKTK
<ScrollerBase bind:progress query="div.step-foreground-container" visible>
{#snippet backgroundSnippet()}
<ScrollyVideo
bind:scrollyVideo
src={Tennis}
height="100svh"
trackScroll={false}
showDebugInfo
onReady={initAutoplay}
/>
<div id="progress-bar">
<p>ScrollerBase progress: {progress.toPrecision(2)}</p>
<progress class="mb-4" value={progress}></progress>
</div>
{/snippet}
{#snippet foregroundSnippet()}
<!-- Add custom foreground HTML or component -->
<div class="step-foreground-container">
<h3 class="text-center">Step 1</h3>
</div>
<div class="step-foreground-container">
<h3 class="text-center">Step 2</h3>
</div>
<div class="step-foreground-container">
<h3 class="text-center">Step 3</h3>
</div>
{/snippet}
</ScrollerBase>
<style lang="scss">
@use '../../../scss/mixins' as mixins;
// svelte-scroller-background
#progress-bar {
background-color: rgba(0, 0, 0, 0.8);
position: absolute;
z-index: 4;
right: 0;
padding: 1rem;
top: 0;
progress {
height: 6px;
background-color: #ff000044; /* Background color of the entire bar */
margin: 0;
}
progress::-webkit-progress-value {
background-color: white;
border-radius: 10px;
}
progress::-webkit-progress-bar {
background-color: #444444;
border-radius: 10px;
}
p {
font-family: var(--theme-font-family-sans-serif);
color: white;
font-size: var(--theme-font-size-xs);
padding: 0;
margin: 0;
}
}
.step-foreground-container {
height: 100vh;
width: 50%;
padding: 1em;
margin: auto;
h3 {
// align center
display: flex;
align-items: center;
justify-content: center;
height: 100%;
color: white;
}
}
</style>

Binary file not shown.

Binary file not shown.