fixes component docs path issue + adds advanced usage story
This commit is contained in:
parent
d76d9defaf
commit
92e850166e
6 changed files with 326 additions and 20 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
```
|
||||
|
|
|
|||
|
|
@ -8,9 +8,6 @@
|
|||
const { Story } = defineMeta({
|
||||
title: 'Components/Graphics/ScrollyVideo',
|
||||
component: ScrollyVideo,
|
||||
parameters: {
|
||||
docsTools: { remount: true },
|
||||
},
|
||||
argTypes: {
|
||||
autoplay: {
|
||||
control: 'boolean',
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
BIN
src/components/ScrollyVideo/videos/_tennis.mp4
Normal file
BIN
src/components/ScrollyVideo/videos/_tennis.mp4
Normal file
Binary file not shown.
BIN
src/components/ScrollyVideo/videos/tennis.mp4
Normal file
BIN
src/components/ScrollyVideo/videos/tennis.mp4
Normal file
Binary file not shown.
Loading…
Reference in a new issue