251 lines
6.7 KiB
Text
251 lines
6.7 KiB
Text
import { Meta } from '@storybook/blocks';
|
|
|
|
import * as ScrollyVideoStories from './ScrollyVideo.stories.svelte';
|
|
|
|
<Meta of={ScrollyVideoStories} />
|
|
|
|
# ScrollyVideo
|
|
|
|
The `ScrollyVideo` component is a powerful tool for creating engaging, interactive video experiences that respond to user scrolling. It allows for precise control over video playback, including the ability to sync video progress with scroll position.
|
|
|
|
The `ScrollyVideo` component is built on top of the [ScrollyVideo.js](https://scrollyvideo.js.org/), and is designed to work seamlessly with Svelte's reactivity model.
|
|
|
|
## Basic usage
|
|
|
|
To use the `ScrollyVideo` component, simply import it and provide the video source. Set height prop to any valid CSS height value, such as `1200px`, `50vh`, or `500svh` to set the scrolling height.
|
|
|
|
```svelte
|
|
<script lang="ts">
|
|
import { ScrollyVideo } from '@reuters-graphics/graphics-components';
|
|
</script>
|
|
|
|
<ScrollyVideo
|
|
src={`${assets}/videos/goldengate.mp4`}
|
|
trackScroll={true}
|
|
height="500svh"
|
|
/>
|
|
```
|
|
|
|
## Mutliple videos
|
|
|
|
It can also be used to display multiple different videos based on the viewport width. This is useful for responsive designs where your subject in the video is not in the center. After Effects can be usd to maintain a crop with a focus on the subject, and the `ScrollyVideo` component can be used to display the appropriate video based on the viewport width.
|
|
|
|
```svelte
|
|
<script lang="ts">
|
|
import { ScrollyVideo } from '@reuters-graphics/graphics-components';
|
|
</script>
|
|
|
|
{#if width < 600}
|
|
<!-- Aspect ratio 9:16 -->
|
|
<ScrollyVideo
|
|
src={`${assets}/videos/v_9_16.mp4`}
|
|
trackScroll={true}
|
|
height="500svh"
|
|
/>
|
|
{:else if width < 1200}
|
|
<!-- Aspect ratio 1:1 -->
|
|
<ScrollyVideo
|
|
src={`${assets}/videos/v_1_1.mp4`}
|
|
trackScroll={true}
|
|
height="500svh"
|
|
/>
|
|
{:else}
|
|
<!-- Aspect ratio 16:9 -->
|
|
<ScrollyVideo
|
|
src={`${assets}/videos/v_16_9.mp4`}
|
|
trackScroll={true}
|
|
height="500svh"
|
|
/>
|
|
{/if}
|
|
```
|
|
|
|
## Autoplay
|
|
|
|
Autoplay can be enabled by setting the `autoplay` prop to `true`. This will start the video playback automatically when the component is mounted. Upon interruption by manual scroll, the video will resume playback on scroll. The height is altered to remaining portion of the video.
|
|
|
|
```svelte
|
|
<script lang="ts">
|
|
import { ScrollyVideo } from '@reuters-graphics/graphics-components';
|
|
</script>
|
|
|
|
<ScrollyVideo
|
|
src={`${assets}/videos/goldengate.mp4`}
|
|
trackScroll={true}
|
|
autoplay={true}
|
|
height="500svh"
|
|
/>
|
|
```
|
|
|
|
## Inside ScrollerBase
|
|
|
|
The `ScrollyVideo` component can also be used inside the `ScrollerBase` component to create a scroller-based layout. This allows for more complex interactions and layouts, such as combining ai2svelte components with scrolly video content.
|
|
|
|
```svelte
|
|
<script lang="ts">
|
|
import {
|
|
Headline,
|
|
GraphicBlock,
|
|
ScrollyVideo,
|
|
ScrollerBase,
|
|
} from '@reuters-graphics/graphics-components';
|
|
import AiMap from './ai2svelte/ai-chart.svelte';
|
|
|
|
// ScrollerBase props
|
|
let count = $state(1);
|
|
let index = $state(0);
|
|
let offset = $state(0);
|
|
let progress = $state(0);
|
|
let top = $state(0);
|
|
let threshold = $state(0);
|
|
let bottom = $state(1);
|
|
</script>
|
|
|
|
<div class="scroller-demo-container">
|
|
<ScrollerBase
|
|
{top}
|
|
{threshold}
|
|
{bottom}
|
|
bind:count
|
|
bind:index
|
|
bind:offset
|
|
bind:progress
|
|
query="div.step-foreground-container"
|
|
visible
|
|
>
|
|
{#snippet backgroundSnippet()}
|
|
<!-- Add custom background HTML or component -->
|
|
<div id="progress-bar">
|
|
<p>
|
|
Current step: <strong>{index + 1}/{count}</strong>
|
|
</p>
|
|
<progress value={(index + 1) / count}></progress>
|
|
|
|
<p>Offset in current step</p>
|
|
<progress value={offset}></progress>
|
|
|
|
<p>Total progress</p>
|
|
<progress value={progress}></progress>
|
|
</div>
|
|
|
|
<ScrollyVideo
|
|
src={`src/components/ScrollyVideo/videos/goldengate.mp4`}
|
|
height="100svh"
|
|
trackScroll={false}
|
|
videoPercentage={progress}
|
|
objectFit="cover"
|
|
showDebugInfo
|
|
/>
|
|
{/snippet}
|
|
{#snippet foregroundSnippet()}
|
|
<!-- Add custom foreground HTML or component -->
|
|
<div class="step-foreground-container">
|
|
<div class="fg-child-container">
|
|
<Headline
|
|
class="custom-headline"
|
|
hed="ScrollyVideo inside ScrollerBase"
|
|
dek="This is a demo of ScrollyVideo inside ScrollerBase component."
|
|
section={'Reuters Graphics'}
|
|
authors={['Jane Doe']}
|
|
publishTime={new Date('2020-01-01').toISOString()}
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div class="step-foreground-container"><p>Step 2</p></div>
|
|
<div class="step-foreground-container">
|
|
<div class="fg-child-container">
|
|
<GraphicBlock
|
|
title="Earthquake in Haiti"
|
|
description="The 7.2-magnitude earthquake struck at 8:29 a.m. EST, Aug. 14, 2021."
|
|
notes="Note: A shakemap represents the ground shaking produced by an earthquake."
|
|
>
|
|
<AiMap />
|
|
</GraphicBlock>
|
|
</div>
|
|
</div>
|
|
<div class="step-foreground-container"><p>Step 4</p></div>
|
|
<div class="step-foreground-container"><p>Step 5</p></div>
|
|
{/snippet}
|
|
</ScrollerBase>
|
|
</div>
|
|
|
|
<style lang="scss">
|
|
@use '../../../scss/mixins' as mixins;
|
|
|
|
#progress-bar {
|
|
background-color: rgba(0, 0, 0, 0.8);
|
|
position: absolute;
|
|
z-index: 2;
|
|
right: 0;
|
|
padding: 1rem;
|
|
|
|
progress {
|
|
height: 6px;
|
|
background-color: #ff000044; /* Background color of the entire bar */
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
#debugger {
|
|
width: 100%;
|
|
height: 100vh;
|
|
background-color: hotpink;
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
z-index: 1;
|
|
|
|
img {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
padding: 0;
|
|
margin: 0;
|
|
}
|
|
}
|
|
|
|
.step-foreground-container {
|
|
height: 100vh;
|
|
width: 100%;
|
|
padding: 1em;
|
|
position: relative;
|
|
border: 1px solid red;
|
|
|
|
.fg-child-container {
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
text-align: center;
|
|
width: 100%;
|
|
}
|
|
|
|
p {
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
margin: 0;
|
|
font-size: 2em;
|
|
color: white;
|
|
font-family: var(--theme-font-family-sans-serif);
|
|
}
|
|
}
|
|
</style>
|
|
```
|