adds Illustrator screenshot for reference
This commit is contained in:
parent
1ee82f4f5a
commit
b354ce267b
7 changed files with 219 additions and 73 deletions
|
|
@ -2,6 +2,8 @@ import { Meta } from '@storybook/blocks';
|
|||
|
||||
import * as HorizontalScrollerStories from './HorizontalScroller.stories.svelte';
|
||||
|
||||
import IllustratorScreenshot from './assets/illustrator.png';
|
||||
|
||||
<Meta of={HorizontalScrollerStories} />
|
||||
|
||||
# HorizontalScroller
|
||||
|
|
@ -161,7 +163,10 @@ respect_height: true
|
|||
allow_overflow: true
|
||||
```
|
||||
|
||||
This can be useful to even transition tagged content inside the ai2svelte graphic as part of the horizontal scrolling experience. For example, caption boxes exported as `htext` tagged layers can be animated to fade in/out or move in/out of view based on the scroll progress. Or one could even use tagged `png` layers to create parallax effects.
|
||||
<img
|
||||
src={IllustratorScreenshot}
|
||||
alt="Screenshot showing Illustrator document with artboard panel"
|
||||
/>
|
||||
|
||||
[Demo](?path=/story/components-graphics-horizontalscroller--scrollable-ai-2-svelte)
|
||||
|
||||
|
|
@ -170,9 +175,85 @@ This can be useful to even transition tagged content inside the ai2svelte graphi
|
|||
import { HorizontalScroller } from '@reuters-graphics/graphics-components';
|
||||
import AiGraphic from './ai2svelte/ai-graphic.svelte';
|
||||
import { sineInOut } from 'svelte/easing';
|
||||
// If using with the graphics kit
|
||||
import { assets } from '$app/paths';
|
||||
</script>
|
||||
|
||||
<HorizontalScroller
|
||||
width="fluid"
|
||||
height="800lvh"
|
||||
direction="right"
|
||||
easing={sineInOut}
|
||||
showDebugInfo
|
||||
>
|
||||
<AiGraphic assetsPath={assets} />
|
||||
</HorizontalScroller>
|
||||
```
|
||||
|
||||
## With ai2svelte components (advanced)
|
||||
|
||||
Binding the scrollProgress can be useful to even transition tagged content inside the ai2svelte graphic as part of the horizontal scrolling experience. For example, caption boxes exported as `htext` tagged layers can be animated to fade in/out or move in/out of view based on the scroll progress. Or one could even use tagged `png` layers to create parallax effects.
|
||||
|
||||
[Demo](?path=/story/components-graphics-horizontalscroller--scrollable-ai-2-svelte-advanced)
|
||||
|
||||
```svelte
|
||||
<script lang="ts">
|
||||
import { HorizontalScroller } from '@reuters-graphics/graphics-components';
|
||||
import AiGraphic from './ai2svelte/ai-graphic.svelte';
|
||||
import { sineInOut } from 'svelte/easing';
|
||||
// If using with the graphics kit
|
||||
import { assets } from '$app/paths';
|
||||
|
||||
// bind scrollProgress for advanced interactivity
|
||||
let scrollProgress: number = $state(0);
|
||||
let pngLayer: HTMLElement | null;
|
||||
let captions: HTMLElement[] | null;
|
||||
let threshold = 0.8;
|
||||
let screenWidth: number = $state(0);
|
||||
|
||||
function handleScroll() {
|
||||
// to create the parallax movement
|
||||
if (pngLayer) {
|
||||
pngLayer.style.transform = `translateX(${map(scrollProgress, 0, 1, -400, 400)}px)`;
|
||||
}
|
||||
|
||||
// for each caption, checks if position of the caption < 80vw
|
||||
// if it is, show it
|
||||
// if not, hide it
|
||||
if (captions?.length) {
|
||||
captions.forEach((caption) => {
|
||||
let captionWidth = caption.getBoundingClientRect().width;
|
||||
let captionMidpoint =
|
||||
caption.getBoundingClientRect().left + captionWidth / 2;
|
||||
|
||||
if (
|
||||
captionMidpoint < screenWidth * threshold &&
|
||||
caption.style.opacity !== '1'
|
||||
) {
|
||||
caption.style.opacity = '1';
|
||||
} else if (
|
||||
captionMidpoint > screenWidth * threshold &&
|
||||
caption.style.opacity !== '0'
|
||||
) {
|
||||
caption.style.opacity = '0';
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// refetch new captions and png image
|
||||
// every time the artboard changes
|
||||
function onArtboardChange(artboard: HTMLElement) {
|
||||
pngLayer = artboard.querySelector('.g-png-layer-overlay');
|
||||
captions = Array.from(artboard.querySelectorAll('.g-captions'));
|
||||
|
||||
if (pngLayer) {
|
||||
window.removeEventListener('scroll', handleScroll);
|
||||
window.addEventListener('scroll', handleScroll, {
|
||||
passive: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<HorizontalScroller
|
||||
|
|
@ -183,17 +264,16 @@ This can be useful to even transition tagged content inside the ai2svelte graphi
|
|||
easing={sineInOut}
|
||||
showDebugInfo
|
||||
>
|
||||
<AiGraphic />
|
||||
<AiGraphic assetsPath={assets} />
|
||||
</HorizontalScroller>
|
||||
|
||||
<style lang="scss">
|
||||
#horizontal-stack {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
gap: 10vw;
|
||||
height: 100%;
|
||||
:global(.scroller-caption) {
|
||||
padding: 1rem;
|
||||
margin: 0;
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
border-radius: 8px;
|
||||
filter: drop-shadow(0px 2px 16px rgba(0, 0, 0, 0.2));
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
import DemoSnippetBlock from './demo/DemoSnippet.svelte';
|
||||
import CustomChildrenBlock from './demo/CustomChildrenSnippet.svelte';
|
||||
import ScrollableGraphic from './demo/ScrollableGraphic.svelte';
|
||||
import AdvancedScrollableGraphic from './demo/AdvancedScrollableGraphic.svelte';
|
||||
import WithScrollerBaseComponent from './demo/withScrollerBase.svelte';
|
||||
import Block from '../Block/Block.svelte';
|
||||
|
||||
|
|
@ -43,6 +44,22 @@
|
|||
{/snippet}
|
||||
</Story>
|
||||
|
||||
<Story
|
||||
name="Extended demo"
|
||||
args={{
|
||||
children: DemoSnippet,
|
||||
height: '200lvh',
|
||||
clampedProgress: true,
|
||||
clampStart: -1,
|
||||
clampEnd: 2,
|
||||
showDebugInfo: true,
|
||||
}}
|
||||
>
|
||||
{#snippet children(args)}
|
||||
<DemoComponent {...args}></DemoComponent>
|
||||
{/snippet}
|
||||
</Story>
|
||||
|
||||
<Story
|
||||
name="With stops"
|
||||
args={{
|
||||
|
|
@ -85,6 +102,10 @@
|
|||
<ScrollableGraphic />
|
||||
</Story>
|
||||
|
||||
<Story name="Scrollable ai2svelte (advanced)">
|
||||
<AdvancedScrollableGraphic />
|
||||
</Story>
|
||||
|
||||
<Story name="With ScrollerBase">
|
||||
<WithScrollerBaseComponent />
|
||||
</Story>
|
||||
|
|
|
|||
|
|
@ -99,15 +99,22 @@
|
|||
|
||||
let translateX: number = $derived.by(() => {
|
||||
let processedProgress = progressTween.current;
|
||||
let normalisedProgress = processedProgress;
|
||||
if (clampedProgress) {
|
||||
processedProgress = Math.min(
|
||||
Math.max(progressTween.current, clampStart),
|
||||
clampEnd
|
||||
);
|
||||
}
|
||||
|
||||
const normalisedProgress =
|
||||
direction === 'right' ? processedProgress : 1 - processedProgress;
|
||||
processedProgress = map(processedProgress, 0, 1, clampStart, clampEnd);
|
||||
normalisedProgress =
|
||||
direction === 'right' ? processedProgress : (
|
||||
clampEnd - processedProgress
|
||||
);
|
||||
} else {
|
||||
normalisedProgress =
|
||||
direction === 'right' ? processedProgress : 1 - processedProgress;
|
||||
}
|
||||
|
||||
const translate = -(contentWidth - containerWidth) * normalisedProgress;
|
||||
|
||||
|
|
|
|||
BIN
src/components/HorizontalScroller/assets/illustrator.png
Normal file
BIN
src/components/HorizontalScroller/assets/illustrator.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 MiB |
|
|
@ -0,0 +1,97 @@
|
|||
<script lang="ts">
|
||||
import Demo from './graphic/ai2svelte/demo.svelte';
|
||||
import BodyText from '../../BodyText/BodyText.svelte';
|
||||
import HorizontalScroller from '../HorizontalScroller.svelte';
|
||||
import { map } from '../utils/index';
|
||||
import { sineInOut } from 'svelte/easing';
|
||||
|
||||
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 someone’s weekend. And thus began the saga nobody asked for, yet every developer somehow relates to.';
|
||||
|
||||
let scrollProgress: number = $state(0);
|
||||
let pngLayer: HTMLElement | null;
|
||||
let captions: HTMLElement[] | null;
|
||||
let threshold = 0.8;
|
||||
let screenWidth: number = $state(0);
|
||||
|
||||
function handleScroll() {
|
||||
if (pngLayer) {
|
||||
pngLayer.style.transform = `translateX(${map(scrollProgress, 0, 1, -400, 400)}px)`;
|
||||
}
|
||||
|
||||
if (captions?.length) {
|
||||
captions.forEach((caption) => {
|
||||
let captionWidth = caption.getBoundingClientRect().width;
|
||||
let captionMidpoint =
|
||||
caption.getBoundingClientRect().left + captionWidth / 2;
|
||||
|
||||
if (
|
||||
captionMidpoint < screenWidth * threshold &&
|
||||
caption.style.opacity !== '1'
|
||||
) {
|
||||
caption.style.opacity = '1';
|
||||
} else if (
|
||||
captionMidpoint > screenWidth * threshold &&
|
||||
caption.style.opacity !== '0'
|
||||
) {
|
||||
caption.style.opacity = '0';
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function onArtboardChange(artboard: HTMLElement) {
|
||||
pngLayer = artboard.querySelector('.g-png-layer-overlay');
|
||||
captions = Array.from(artboard.querySelectorAll('.g-captions'));
|
||||
|
||||
if (pngLayer) {
|
||||
window.removeEventListener('scroll', handleScroll);
|
||||
window.addEventListener('scroll', handleScroll, {
|
||||
passive: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window bind:innerWidth={screenWidth} />
|
||||
|
||||
<BodyText text={foobarText} />
|
||||
|
||||
<HorizontalScroller
|
||||
height="800lvh"
|
||||
direction="right"
|
||||
bind:scrollProgress
|
||||
easing={sineInOut}
|
||||
showDebugInfo
|
||||
>
|
||||
<Demo
|
||||
{onArtboardChange}
|
||||
debugTaggedText
|
||||
taggedText={{
|
||||
htext: {
|
||||
captions: {
|
||||
caption1:
|
||||
'<div class="scroller-caption"><strong>Destruction!</strong><br/>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</div>',
|
||||
caption2:
|
||||
'<div class="scroller-caption"><strong>Destruction!</strong><br/>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</div>',
|
||||
caption3:
|
||||
'<div class="scroller-caption"><strong>Destruction!</strong><br/>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</div>',
|
||||
caption4:
|
||||
'<div class="scroller-caption"><strong>Destruction!</strong><br/>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</div>',
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</HorizontalScroller>
|
||||
|
||||
<BodyText text={foobarText} />
|
||||
|
||||
<style lang="scss">
|
||||
:global(.scroller-caption) {
|
||||
padding: 1rem;
|
||||
margin: 0;
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
border-radius: 8px;
|
||||
filter: drop-shadow(0px 2px 16px rgba(0, 0, 0, 0.2));
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
<div style="width: 400vw; height: 100lvh;">
|
||||
<div style="width: 400vw; height: 100lvh; border: 2px solid red;">
|
||||
<img
|
||||
src="https://picsum.photos/1200/640?t=1"
|
||||
alt="Sample"
|
||||
|
|
|
|||
|
|
@ -2,80 +2,21 @@
|
|||
import Demo from './graphic/ai2svelte/demo.svelte';
|
||||
import BodyText from '../../BodyText/BodyText.svelte';
|
||||
import HorizontalScroller from '../HorizontalScroller.svelte';
|
||||
import { map } from '../utils/index';
|
||||
import { sineInOut } from 'svelte/easing';
|
||||
|
||||
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 someone’s weekend. And thus began the saga nobody asked for, yet every developer somehow relates to.';
|
||||
|
||||
let scrollProgress: number = $state(0);
|
||||
let pngLayer: HTMLElement | null;
|
||||
let captions: HTMLElement[] | null;
|
||||
let threshold = 0.8;
|
||||
let screenWidth: number = $state(0);
|
||||
|
||||
function handleScroll() {
|
||||
if (pngLayer) {
|
||||
pngLayer.style.transform = `translateX(${map(scrollProgress, 0, 1, -400, 400)}px)`;
|
||||
}
|
||||
|
||||
if (captions?.length) {
|
||||
captions.forEach((caption) => {
|
||||
let captionWidth = caption.getBoundingClientRect().width;
|
||||
let captionMidpoint =
|
||||
caption.getBoundingClientRect().left + captionWidth / 2;
|
||||
|
||||
if (captionMidpoint < screenWidth * threshold) {
|
||||
caption.style.opacity = '1';
|
||||
} else {
|
||||
caption.style.opacity = '0';
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function onArtboardChange(artboard: HTMLElement) {
|
||||
pngLayer = artboard.querySelector('.g-png-layer-overlay');
|
||||
captions = Array.from(artboard.querySelectorAll('.g-captions'));
|
||||
|
||||
if (pngLayer) {
|
||||
window.removeEventListener('scroll', handleScroll);
|
||||
window.addEventListener('scroll', handleScroll, {
|
||||
passive: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window bind:innerWidth={screenWidth} />
|
||||
|
||||
<BodyText text={foobarText} />
|
||||
|
||||
<HorizontalScroller
|
||||
height="800lvh"
|
||||
direction="right"
|
||||
bind:scrollProgress
|
||||
easing={sineInOut}
|
||||
showDebugInfo
|
||||
>
|
||||
<Demo
|
||||
{onArtboardChange}
|
||||
debugTaggedText
|
||||
taggedText={{
|
||||
htext: {
|
||||
captions: {
|
||||
caption1:
|
||||
'<div class="scroller-caption"><strong>Destruction!</strong><br/>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</div>',
|
||||
caption2:
|
||||
'<div class="scroller-caption"><strong>Destruction!</strong><br/>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</div>',
|
||||
caption3:
|
||||
'<div class="scroller-caption"><strong>Destruction!</strong><br/>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</div>',
|
||||
caption4:
|
||||
'<div class="scroller-caption"><strong>Destruction!</strong><br/>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</div>',
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<Demo />
|
||||
</HorizontalScroller>
|
||||
|
||||
<BodyText text={foobarText} />
|
||||
|
|
|
|||
Loading…
Reference in a new issue