finished docs, demo updates for Time-based component foregrounds with ArchieML

This commit is contained in:
MinamiFunakoshiTR 2025-07-21 12:16:30 -04:00
parent 69b08e65c9
commit 346827ef65
Failed to extract signature
4 changed files with 201 additions and 189 deletions

View file

@ -118,7 +118,7 @@ The `autoplay` option combines the autoplay and scrollytelling experience. If se
## Time-based text foregrounds with ArchieML
The `ScrollyVideo` component can also be used to display text as foregrounds at specific times in the video. To do so, use the `ScrollyVideoForeground` component.
The `ScrollyVideo` component can also be used to display text as foregrounds at specific times in the video. To do so, use the `text` prop in `ScrollyVideoForeground` component.
[Demo](?path=/story/components-graphics-scrollyvideo--archie-ml-foregrounds)
@ -127,15 +127,6 @@ With the graphics kit, you'll likely get your text and prop values from an Archi
```yaml
# ArchieML doc
# Headline
hed: The Alps
[authors]
* Jane Doe
[]
publishTime: 2020-01-01T00:00:00Z
startTime: 0 # When in the video to start showing the headline
endTime: 2 # When to stop showing the headline
[blocks]
type: scrolly-video
id: alps-scrolly
@ -188,49 +179,91 @@ endTime: 2 # When to stop showing the headline
} from '@reuters-graphics/graphics-components';
import { assets } from '$app/paths'; // 👈 If using in the graphics kit...
</script>
{#if block.type == 'scrolly-video'}
<ScrollyVideo id={block.id} src={`${assets}/${block.src}}`} height={block.height}>
<!-- You can wrap components, such as `Headline`, inside `ScrollyVideoForeground` and make them appear/disappear at specific times -->
<ScrollyVideoForeground startTime={parseFloat(content.startTime)} endTime={parseFloat(content.endTime)}>
<Headline
hed={content.hed}
authors={content.authors}
publishTime={content.publishedDate}
/>
</ScrollyVideoForeground>
<!-- Loop through foregrounds to add text blurbs that appear/disappear at specific times -->
{#each block.foregrounds as foreground}
<ScrollyVideoForeground
startTime={parseFloat(foreground.startTime)}
endTime={parseFloat(foreground.endTime)}
width={foreground.width}
position={foreground.position}
backgroundColour={foreground.backgroundColour}
text={foreground.text}
/>
{/each}
</ScrollyVideo>
{/if}
{#each content.blocks as block}
<!-- Inside the content.blocks for loop... -->
{#if block.type == 'scrolly-video'}
<ScrollyVideo id={block.id} src={`${assets}/${block.src}}`} height={block.height}>
<!-- Loop through foregrounds to add text blurbs that appear/disappear at specific times -->
{#each block.foregrounds as foreground}
<ScrollyVideoForeground
startTime={parseFloat(foreground.startTime)}
endTime={parseFloat(foreground.endTime)}
width={foreground.width}
position={foreground.position}
backgroundColour={foreground.backgroundColour}
text={foreground.text}
/>
{/each}
</ScrollyVideo>
{/if}
{/each}
```
## Time-based component foregrounds with ArchieML
The `ScrollyVideo` component can also be used to display components, such as `Headline` or ai2svelte files, as foregrounds at specific times in the video. To do so, use the `ScrollyVideoForeground` component.
The `ScrollyVideo` component can also be used to display components, such as `Headline` or ai2svelte files, as foregrounds at specific times in the video. To do so, use the `Foreground` prop in `ScrollyVideoForeground` component.
> **IMPORTANT❗**: When layering ai2svelte files over a video, the aspect ratio of the ai2svelte graphics should match that of the video. If the ai2svelte graphic is responsive and has, for example, small, medium and large verisons — which is generally the case — make sure to also render small, medium and large versions of the video at the appropriate screen sizes. See [Responsive videos](#responsive-videos) for more details.
[Demo](?path=/story/components-graphics-scrollyvideo--component-archie-ml-foregrounds)
With the graphics kit, you'll likely get your text and prop values from an ArchieML doc...
```yaml
# ArchieML doc
# Headline
hed: Wind and waves
[authors]
* Jane Doe
[]
publishTime: 2020-01-01T00:00:00Z
startTime: 0 # When in the video to start showing the headline
endTime: 0.3 # When to stop showing the headline
[blocks]
type: scrolly-video
id: my-scrolly-video
height: 800svh
# Adjust prop names as needed
srcSm: videos/my-video-sm.mp4
srcMd: videos/my-video-md.mp4
srcLg: videos/my-video-lg.mp4
# Array of foregrounds
[.foregrounds]
startTime: 0.3 # When in the video to start showing the foreground
endTime: 2.2 # When to stop showing the foreground
width: fluid # foreground container width
Foreground: Foreground1 # Name of the ai2svelte component to render
startTime: 2.2
endTime: 3.2
width: fluid
Foreground: Foreground2
startTime: 3.2
endTime: 4.5
width: fluid
Foreground: Foreground3
startTime: 6.5
endTime: 8
width: fluid
Foreground: Foreground4
[]
[]
```
... which you'll parse out of a ArchieML block object before passing to the `ScrollyVideo` and `ScrollyVideoForeground` components.
```svelte
<script lang="ts">
// Graphics kit only
import { assets } from '$app/paths'; // 👈 If using in the graphics kit...
import { truthy } from '$utils/propValidators'; // 👈 If using in the graphics kit...
=
import {
Headline,
GraphicBlock,
@ -239,80 +272,72 @@ With the graphics kit, you'll likely get your text and prop values from an Archi
} from '@reuters-graphics/graphics-components';
// Foreground ai2svelte components
import Annotation1 from './ai2svelte/annotation1.svelte';
import Annotation2 from './ai2svelte/annotation2.svelte';
import Annotation3 from './ai2svelte/annotation3.svelte';
import Annotation4 from './ai2svelte/annotation4.svelte';
import Foreground1 from './ai2svelte/foreground1.svelte';
import Foreground2 from './ai2svelte/foreground2.svelte';
import Foreground3 from './ai2svelte/foreground3.svelte';
import Foreground4 from './ai2svelte/foreground4.svelte';
// Foreground ai2svelte graphics components
// Add your imported foreground ai2svelte charts to this object
const aiChartsForeground = {
Annotation1,
Annotation2,
Annotation3,
Annotation4,
Foreground1,
Foreground2,
Foreground3,
Foreground4,
};
// Window width for responsive videos
let width = $state(1);
</script>
<svelte:window bind:innerWidth={width} />
{#snippet ScrollForeground()}
<Foreground startTime={0} endTime={0.3}>
<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()}
/>
</Foreground>
<Foreground startTime={0.3} endTime={2.2}>
<GraphicBlock title="" description="" width="fluid">
<Annotation1 />
</GraphicBlock>
</Foreground>
<Foreground startTime={2.2} endTime={3.2}>
<GraphicBlock title="" description="" width="fluid">
<Annotation2 />
</GraphicBlock>
</Foreground>
<Foreground startTime={3.2} endTime={4.5}>
<GraphicBlock title="" description="" width="fluid">
<Annotation3 />
</GraphicBlock>
</Foreground>
<Foreground startTime={6.5} endTime={8}>
<GraphicBlock title="" description="" width="fluid">
<Annotation4 />
</GraphicBlock>
</Foreground>
{/snippet}
<!-- Loop through content blocks... -->
{#each content.blocks as block}
{#snippet ScrollVideo(height: string, VideoSrc: string)}
<ScrollyVideo {height} src={VideoSrc} useWebCodecs={true} showDebugInfo>
{@render ScrollForeground()}
</ScrollyVideo>
{/snippet}
{#if block.type == 'scrolly-video'}
{#if width < 600}
{@render ScrollVideo('800svh', `${assets}/videos/sm.mp4`)}
{:else if width < 1200}
{@render ScrollVideo('800svh', `${assets}/videos/md.mp4`)}
{:else}
{@render ScrollVideo('800svh', `${assets}/videos/lg.mp4`)}
{/if}
<!-- ScrollVideo snippet to render responsive videos -->
{#snippet ScrollVideo(height: string, src: string)}
<ScrollyVideo
id={block.id}
{height}
{src}
>
<!-- Headline component as foreground -->
<ScrollyVideoForeground
startTime={parseFloat(content.startTime)}
endTime={parseFloat(content.endTime)}
>
<Headline
hed={content.hed}
authors={content.authors}
publishTime={new Date(content.publishTime).toISOString()}
/>
</ScrollyVideoForeground>
<style lang="scss">
:global(.custom-headline *) {
color: white;
}
<!-- Loop through block.foregrounds to render each foreground component -->
{#each block.foregrounds as foreground}
<ScrollyVideoForeground
startTime={parseFloat(foreground.startTime)}
endTime={parseFloat(foreground.endTime)}
width={foreground.width}
Foreground={aiChartsForeground[
foreground.foreground as keyof typeof aiChartsForeground
]}
/>
{/each}
</ScrollyVideo>
{/snippet}
:global(.scrolly-video-foreground) {
filter: drop-shadow(0 0 4px rgba(0, 0, 0, 0.85));
}
</style>
<!-- Render the ScrollVideo snippet for different screen sizes -->
{#if width < 600}
{@render ScrollVideo({block.height}, `${assets}/${block.srcSm}`)}
{:else if width < 1200}
{@render ScrollVideo({block.height}, `${assets}/${block.srcMd}`)}
{:else}
{@render ScrollVideo({block.height}, `${assets}/${block.srcLg}`)}
{/if}
{/each}
```

View file

@ -21,7 +21,8 @@
backgroundColour?: string;
width?: ContainerWidth;
position?: ScrollyVideoForegroundPosition | string;
foreground?: string | Component;
text?: string;
Foreground?: Component;
}
let {
@ -33,7 +34,8 @@
backgroundColour = '#000',
width = 'normal',
position = 'center center',
foreground,
text,
Foreground,
}: ForegroundProps = $props();
let componentState: ScrollyVideoState = getContext('scrollyVideoState');
@ -46,27 +48,31 @@
in:fade={{ delay: 100, duration: 200 }}
out:fade={{ delay: 0, duration: 100 }}
>
<div class="scrolly-video-foreground-item">
{#if children}
{@render children()}
{/if}
</div>
{#if foreground}
{#if typeof foreground === 'string'}
<Block
class="scrolly-video-foreground-text {position.split(' ')[1]}"
{width}
<!-- Text blurb foreground -->
{#if text}
<Block
class="scrolly-video-foreground-text {position.split(' ')[1]}"
{width}
>
<div
style="background-color: {backgroundColour};"
class="foreground-text {position.split(' ')[0]}"
>
<div
style="background-color: {backgroundColour};"
class="foreground-text {position.split(' ')[0]}"
>
<Markdown source={foreground} />
</div>
<Markdown source={text} />
</div>
</Block>
<!-- Render children snippet -->
{:else if children}
<div class="scrolly-video-foreground-item">
{@render children()}
</div>
<!-- Render Foreground component -->
{:else if Foreground}
<div class="scrolly-video-foreground-item">
<Block width="fluid">
<Foreground />
</Block>
{:else}
{foreground}
{/if}
</div>
{/if}
</div>
{/if}

View file

@ -5,13 +5,20 @@
import MD from '../videos/waves_md.mp4';
import LG from '../videos/waves_lg.mp4';
import Headline from '../../Headline/Headline.svelte';
import Block from '../../Block/Block.svelte';
// Foreground ai2svelte components
import Annotation1 from './graphic/ai2svelte/annotation1.svelte';
import Annotation2 from './graphic/ai2svelte/annotation2.svelte';
import Annotation3 from './graphic/ai2svelte/annotation3.svelte';
import Annotation4 from './graphic/ai2svelte/annotation4.svelte';
import Foreground1 from './graphic/ai2svelte/annotation1.svelte';
import Foreground2 from './graphic/ai2svelte/annotation2.svelte';
import Foreground3 from './graphic/ai2svelte/annotation3.svelte';
import Foreground4 from './graphic/ai2svelte/annotation4.svelte';
// Foreground ai2svelte graphics components
const aiChartsForeground = {
Foreground1,
Foreground2,
Foreground3,
Foreground4,
};
const content = {
hed: 'Wind and waves',
@ -29,19 +36,25 @@
startTime: '0.3',
endTime: '2.2',
width: 'fluid',
foregroud: 'Annotation1',
foreground: 'Foreground1',
},
{
startTime: '7',
endTime: '12',
startTime: '2.2',
endTime: '3.2',
width: 'fluid',
foreground: 'Annotation2',
foreground: 'Foreground2',
},
{
startTime: '14',
endTime: '20',
startTime: '3.2',
endTime: '4.5',
width: 'fluid',
foreground: 'Annotation3',
foreground: 'Foreground3',
},
{
startTime: '6.5',
endTime: '8',
width: 'fluid',
foreground: 'Foreground4',
},
],
},
@ -50,62 +63,49 @@
const scrollyVideoBlock = content.blocks[0];
let width = $state(1);
let src = $derived(() => {
if (width < 600) return '../videos/waves_sm.mp4';
else if (width < 1200) return '../videos/waves_md.mp4';
else return '../videos/waves_lg.mp4';
});
</script>
<svelte:window bind:innerWidth={width} />
{#snippet ScrollVideo(height: string, VideoSrc: string)}
{#snippet ScrollVideo(height: string, src: string)}
<ScrollyVideo
class="surf-scrolly"
id={scrollyVideoBlock.id}
{height}
src={VideoSrc}
{src}
useWebCodecs={true}
showDebugInfo
>
<ScrollyVideoForeground startTime={0} endTime={0.3}>
<ScrollyVideoForeground
startTime={parseFloat(content.startTime)}
endTime={parseFloat(content.endTime)}
>
<Headline
class="custom-headline"
hed="Wind and waves"
authors={['Jane Doe']}
publishTime={new Date('2020-01-01').toISOString()}
hed={content.hed}
authors={content.authors}
publishTime={new Date(content.publishTime).toISOString()}
/>
</ScrollyVideoForeground>
{#each scrollyVideoBlock.foregrounds as foreground, idx}
{#if foreground.foreground}
<ScrollyVideoForeground
startTime={parseFloat(foreground.startTime)}
endTime={parseFloat(foreground.endTime)}
>
<Block width="fluid">
{#if idx === 0}
<Annotation1 />
{:else if idx === 1}
<Annotation2 />
{:else if idx === 2}
<Annotation3 />
{:else if idx === 3}
<Annotation4 />
{/if}
</Block>
</ScrollyVideoForeground>
{/if}
{#each scrollyVideoBlock.foregrounds as foreground}
<ScrollyVideoForeground
startTime={parseFloat(foreground.startTime)}
endTime={parseFloat(foreground.endTime)}
width={foreground.width as 'fluid'}
Foreground={aiChartsForeground[
foreground.foreground as keyof typeof aiChartsForeground
]}
/>
{/each}
</ScrollyVideo>
{/snippet}
{#if width < 600}
{@render ScrollVideo('800svh', SM)}
{@render ScrollVideo(scrollyVideoBlock.height, SM)}
{:else if width < 1200}
{@render ScrollVideo('800svh', MD)}
{@render ScrollVideo(scrollyVideoBlock.height, MD)}
{:else}
{@render ScrollVideo('800svh', LG)}
{@render ScrollVideo(scrollyVideoBlock.height, LG)}
{/if}
<style lang="scss">

View file

@ -2,15 +2,9 @@
import ScrollyVideo from '../ScrollyVideo.svelte';
import ScrollyVideoForeground from '../ScrollyVideoForeground.svelte';
import Drone from '../videos/drone.mp4';
import Headline from '../../Headline/Headline.svelte';
import type { ContainerWidth } from '../../@types/global';
const content = {
hed: 'The Alps',
authors: ['Jane Doe'],
publishTime: '2020-01-01T00:00:00Z',
startTime: '0',
endTime: '2',
blocks: [
{
type: 'scrolly-video',
@ -57,19 +51,6 @@
useWebCodecs={true}
showDebugInfo
>
<!-- <ScrollyVideoForeground
startTime={parseFloat(content.startTime)}
endTime={parseFloat(content.endTime)}
>
<Headline
class="custom-headline"
hed={content.hed}
authors={content.authors}
publishTime={new Date(content.publishTime).toISOString()}
hedSize="bigger"
/>
</ScrollyVideoForeground> -->
{#each scrollyVideoBlock.foregrounds as foreground}
<ScrollyVideoForeground
startTime={parseFloat(foreground.startTime)}
@ -77,7 +58,7 @@
width={foreground.width as ContainerWidth}
position={foreground.position}
backgroundColour={foreground.backgroundColour}
foreground={foreground.text}
text={foreground.text}
/>
{/each}
</ScrollyVideo>