Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ node_modules
/package
.env
.env.*
!.env.example
!.env.example
141 changes: 101 additions & 40 deletions dist-wc/svelte-scrollyteller.js

Large diffs are not rendered by default.

17 changes: 16 additions & 1 deletion dist/Panel.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,20 @@ onMount(() => {
<div class="st-panel" use:children={nodes}></div>
</div>

<style>.st-panel-root {
<style>@media (max-width: 62rem) {
:global(.scrollyteller--mobile-row-variant) {
/* Mobile row variant doesn't need scrims etc */
/* Mobile row variant needs less spacing between panels */
}
:global(.scrollyteller--mobile-row-variant) .st-panel::before {
opacity: 0 !important;
}
:global(.scrollyteller--mobile-row-variant) .st-panel-root {
margin: 40vh auto;
}
}

.st-panel-root {
--panel-radius: 0.75rem;
--panel-background: var(--color-panel-background, rgba(255, 255, 255, 0.95));
--panel-color: var(--color-panel-text, #000);
Expand Down Expand Up @@ -92,6 +105,8 @@ onMount(() => {
color: var(--panel-color);
border-radius: var(--panel-radius);
padding: var(--panel-padding);
max-width: 640px;
margin: auto;
}
.st-panel::before {
content: "";
Expand Down
42 changes: 24 additions & 18 deletions dist/Panels.svelte
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<script lang="ts">import Panel from './Panel.svelte';
export let panelRoot;
export let layout;
export let panels;
export let customPanel = null;
Expand Down Expand Up @@ -27,24 +28,29 @@ $: {
</script>

{#each panelGroups as group}
<div
class="content"
class:content--centre={group.align === 'centre'}
class:content--right={group.align === 'right'}
class:content--left={group.align === 'left'}
>
{#each group.panels as panel}
{#if customPanel}
<svelte:component this={customPanel} {...panel} {steps} />
{:else}
<Panel
{...panel}
align={panel.align || layout.align}
transparentFloat={layout.transparentFloat}
{steps}
/>
{/if}
{/each}
<div
class="panel-wrapper"
bind:this={panelRoot}
>
<div
class="content"
class:content--centre={group.align === 'centre'}
class:content--right={group.align === 'right'}
class:content--left={group.align === 'left'}
>
{#each group.panels as panel}
{#if customPanel}
<svelte:component this={customPanel} {...panel} {steps} />
{:else}
<Panel
{...panel}
align={panel.align || layout.align}
transparentFloat={layout.transparentFloat}
{steps}
/>
{/if}
{/each}
</div>
</div>
{/each}

Expand Down
1 change: 1 addition & 0 deletions dist/Panels.svelte.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> =
z_$$bindings?: Bindings;
}
declare const Panels: $$__sveltets_2_IsomorphicComponent<{
panelRoot: any;
layout: Style;
panels: PanelDefinition[];
customPanel?: ComponentType | null;
Expand Down
24 changes: 20 additions & 4 deletions dist/Scrollyteller.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { getScrollingPos, getScrollSpeed } from './Scrollyteller/Scrollyteller.u
import OnProgressHandler from './Scrollyteller/OnProgressHandler.svelte';
import PanelObserver from './Scrollyteller/PanelObserver.svelte';
import ScreenDimsStoreUpdater from './Scrollyteller/ScreenDimsStoreUpdater.svelte';
import { setSteps, setMargin, setVizDims, setGraphicRootDims, setRatio, setScreenDims, setGlobalAlign, setIsSplitScreen, setMaxScrollytellerWidth, setMaxGraphicWidth, setCurrentPanel } from './stores';
import { setSteps, setMargin, setVizDims, setGraphicRootDims, setRatio, setScreenDims, setGlobalAlign, setMobileVariant, setIsSplitScreen, setIsMobileRowMode, setMaxScrollytellerWidth, setMaxGraphicWidth, setCurrentPanel } from './stores';
import Panels from './Panels.svelte';
import Viz from './Viz.svelte';
const dispatch = createEventDispatcher();
Expand All @@ -16,7 +16,9 @@ const graphicRootDimsStore = setContext('graphicRootDims', setGraphicRootDims())
const ratioStore = setContext('ratio', setRatio());
const screenDimsStore = setContext('screenDims', setScreenDims());
const globalAlignStore = setContext('globalAlign', setGlobalAlign());
const mobileVariantStore = setContext('mobileVariant', setMobileVariant());
const isSplitScreenStore = setContext('isSplitScreen', setIsSplitScreen([screenDimsStore, globalAlignStore]));
const isMobileRowMode = setContext('isMobileRowMode', setIsMobileRowMode([screenDimsStore, mobileVariantStore]));
const maxScrollytellerWidthStore = setContext('maxScrollytellerWidth', setMaxScrollytellerWidth([isSplitScreenStore]));
const maxGraphicWidthStore = setContext('maxGraphicWidth', setMaxGraphicWidth([
isSplitScreenStore,
Expand Down Expand Up @@ -48,9 +50,14 @@ export let discardSlot = false;
export let layout = {};
$: _layout = {
align: layout.align || 'centre',
mobileVariant: layout.mobileVariant || 'blocks', // or rows
resizeInteractive: layout.resizeInteractive ?? true,
transparentFloat: layout.transparentFloat ?? ['left', 'right'].includes(layout.align)
};
$: _observerOptions = {
rootMargin: _layout.mobileVariant === 'rows' ? "-50% 0% 0% 0%" : undefined,
...(observerOptions || {})
};
export let ratio = 1;
$: $ratioStore = ratio;
/**
Expand All @@ -72,6 +79,7 @@ let scrollingPos;
let isInViewport = false;
let scrollSpeed = 0;
let deferUntilScrollSettlesActions = [];
let panelRoot;
const scrollytellerObserver = new IntersectionObserver(([scrollytellerEntry]) => deferUntilScrollSettles(() => {
isInViewport = scrollytellerEntry.isIntersecting;
}));
Expand Down Expand Up @@ -114,8 +122,8 @@ $: isDebug = typeof location !== 'undefined' && location.hash === '#debug=true';
<OnProgressHandler {scrollytellerRef} {onProgress} />
{/if}

<ScreenDimsStoreUpdater align={_layout.align} />
<PanelObserver bind:marker {observerOptions} {isDebug} {vizMarkerThreshold} />
<ScreenDimsStoreUpdater align={_layout.align} mobileVariant={_layout.mobileVariant} />
<PanelObserver bind:marker observerOptions={_observerOptions} {isDebug} {vizMarkerThreshold} />

<svelte:head>
{#if isOdyssey}
Expand All @@ -138,14 +146,15 @@ $: isDebug = typeof location !== 'undefined' && location.hash === '#debug=true';
class:scrollyteller--resized={_layout.resizeInteractive}
class:scrollyteller--debug={isDebug}
class:scrollyteller--columns={['left', 'right'].includes(_layout.align)}
class:scrollyteller--mobile-row-variant={['rows'].includes(_layout.mobileVariant)}
style:--maxScrollytellerWidthPx={$maxScrollytellerWidthStore + 'px'}
style:--rightColumnWidth={`min(calc(var(--maxScrollytellerWidth) * var(--vizMaxWidth)), ${$maxGraphicWidthStore}px)`}
bind:this={scrollytellerRef}
>
{#if _layout.resizeInteractive}
<Viz layout={_layout} {isInViewport} {discardSlot} {onLoad}><slot /></Viz>
{/if}
<Panels layout={_layout} {panels} {customPanel} />
<Panels layout={_layout} {panels} {customPanel} bind:panelRoot />
</div>
</div>

Expand All @@ -161,6 +170,13 @@ $: isDebug = typeof location !== 'undefined' && location.hash === '#debug=true';
max-width: calc(var(--maxScrollytellerWidth) - var(--marginOuter) * 2);
--vizMaxWidth: 1;
--vizMarginOuter: 1.5rem;
/* Force full width when using the mobile row variant */
}
@media (max-width: 62rem) {
.scrollyteller.scrollyteller--mobile-row-variant {
--marginOuter: 0;
--vizMarginOuter: 0;
}
}
@media (min-width: 46.5rem) {
.scrollyteller {
Expand Down
40 changes: 25 additions & 15 deletions dist/Scrollyteller/PanelObserver.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<script lang="ts">import { getContext, onMount } from 'svelte';
const vizDims = getContext('vizDims');
const isSplitScreen = getContext('isSplitScreen');
const isMobileRowMode = getContext('isMobileRowMode');
const screenDims = getContext('screenDims');
const steps = getContext('steps');
const currentPanel = getContext('currentPanel');
Expand All @@ -15,27 +16,36 @@ export let vizMarkerThreshold = 20;
*/
$: vizMarkerThresholdMarginDecimal = (100 - vizMarkerThreshold * 2) / 100;
/** Intersection observer root margin */
$: rootMargin = $isSplitScreen
? // For split screens, trigger the intersection observer when the block is
let rootMargin;
$: {
if ($isMobileRowMode) {
// For row layout on small portrait screens, block out space taken up by the viz at the top
const threshold = ($vizDims.dims[1] / $screenDims[1]) * 100;
// console.log($vizDims, $screenDims, threshold);
rootMargin = `-${threshold}% 0px -30% 0px`;
}
else if ($isSplitScreen) {
// For split screens, trigger the intersection observer when the block is
// over {vizMarkerThreshold}% of the interactive.
Math.round(($screenDims[1] - ($vizDims.dims[1] || $screenDims[1]) * vizMarkerThresholdMarginDecimal) /
2)
: // Otherwise 10% of the screen height.
Math.round($screenDims[1] / 8);
const threshold = Math.round(($screenDims[1] - ($vizDims.dims[1] || $screenDims[1]) * vizMarkerThresholdMarginDecimal) / 2);
rootMargin = `-${threshold}px 0px -${threshold}px 0px`;
}
else {
// Otherwise 10% of the screen height (on top and bottom).
const threshold = Math.round($screenDims[1] / 8);
rootMargin = `-${threshold}px 0px -${threshold}px 0px`;
}
}
/**
* When observerOptions isn't set, default to either 0.5 for centred blocks
* or a 20% margin on the interactive.
*/
let _observerOptions = observerOptions;
let _observerOptions;
$: {
if (observerOptions) {
_observerOptions = observerOptions;
}
else {
_observerOptions = {
rootMargin: `-${rootMargin}px 0px -${rootMargin}px 0px`
};
}
_observerOptions = {
...(observerOptions || {}),
rootMargin,
};
}
// Set up observer for panel position ======================================
let panelObserver;
Expand Down
3 changes: 3 additions & 0 deletions dist/Scrollyteller/ScreenDimsStoreUpdater.svelte
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
<script lang="ts">import { getContext } from 'svelte';
const globalAlign = getContext('globalAlign');
const screenDims = getContext('screenDims');
const globalMobileVariant = getContext('mobileVariant');
export let align = 'centre';
export let mobileVariant = 'blocks';
let innerWidth = 0;
let innerHeight = 0;
$: $screenDims = [innerWidth, innerHeight];
$: $globalAlign = align;
$: $globalMobileVariant = mobileVariant;
</script>

<svelte:window bind:innerWidth bind:innerHeight />
1 change: 1 addition & 0 deletions dist/Scrollyteller/ScreenDimsStoreUpdater.svelte.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> =
}
declare const ScreenDimsStoreUpdater: $$__sveltets_2_IsomorphicComponent<{
align?: string;
mobileVariant?: string;
}, {
[evt: string]: CustomEvent<any>;
}, {}, {}, string>;
Expand Down
21 changes: 20 additions & 1 deletion dist/Viz.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,26 @@ $: if (graphicRootEl) {
{/if}
</div>

<style>.viz {
<style>:global(.scrollyteller--mobile-row-variant) {
--marginOuter: 0;
--vizMarginOuter: 0;
}
@media (max-width: 62rem) {
:global(.scrollyteller--mobile-row-variant) .viz--resized {
z-index: 10;
top: 0;
margin: 0;
background: white;
width: 100% !important;
max-height: calc(45vh + 50px);
aspect-ratio: 1;
container-type: normal;
padding-bottom: 40px;
background: linear-gradient(to bottom, white 90%, transparent 100%);
}
}

.viz {
transform: translate3d(0, 0, 0);
height: 100dvh;
position: sticky;
Expand Down
2 changes: 2 additions & 0 deletions dist/stores.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ export declare function setGraphicRootDims(): import("svelte/store").Writable<{
export declare function setRatio(): import("svelte/store").Writable<number>;
export declare function setScreenDims(): import("svelte/store").Writable<number[]>;
export declare function setGlobalAlign(): import("svelte/store").Writable<string>;
export declare function setMobileVariant(): import("svelte/store").Writable<string>;
/** Split screen mode happens when left/right aligned + not mobile */
export declare function setIsSplitScreen([screenDims, globalAlign]: [any, any]): import("svelte/store").Readable<boolean>;
export declare function setIsMobileRowMode([screenDims, mobileVariant]: [any, any]): import("svelte/store").Readable<boolean>;
export declare function setMaxScrollytellerWidth([isSplitScreen]: [any]): import("svelte/store").Readable<2040 | 1000000>;
export declare function setMaxGraphicWidth([isSplitScreen, graphicRootDims, screenDims, ratio, maxScrollytellerWidth]: [any, any, any, any, any]): import("svelte/store").Readable<number>;
export declare function setCurrentPanel(): import("svelte/store").Writable<number>;
6 changes: 6 additions & 0 deletions dist/stores.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,16 @@ export function setScreenDims() {
export function setGlobalAlign() {
return writable('centre');
}
export function setMobileVariant() {
return writable('blocks');
}
const LARGE_TABLET_BREAKPOINT = 992;
/** Split screen mode happens when left/right aligned + not mobile */
export function setIsSplitScreen([screenDims, globalAlign]) {
return derived([screenDims, globalAlign], ([$screenDims, $globalAlign]) => ['left', 'right'].includes($globalAlign) && $screenDims[0] >= LARGE_TABLET_BREAKPOINT);
}
export function setIsMobileRowMode([screenDims, mobileVariant]) {
return derived([screenDims, mobileVariant], ([$screenDims, $mobileVariant]) => $mobileVariant === 'rows' && $screenDims[0] < LARGE_TABLET_BREAKPOINT);
/** The max width when the scrollyteller centres itself in the page */
}
export function setMaxScrollytellerWidth([isSplitScreen]) {
Expand Down
7 changes: 7 additions & 0 deletions dist/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ export type Style = {
* Resize the interactive to fit the left/right dimensions
*/
resizeInteractive?: boolean;
/**
* How to arrange the elements on mobile. (< LargeTablet)
*
* - `rows` - stack the viz and text on top of each other
* - `blocks` - panels use scrims and slide over the top of the viz (odyssey block style)
*/
mobileVariant?: string;
};
export interface PanelRef extends Element {
scrollyData?: any;
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"lint": "prettier --ignore-path .gitignore --check --plugin-search-dir=. .",
"format": "prettier --ignore-path .gitignore --write --plugin-search-dir=. .",
"release": "np",
"build": "vite build",
"version": "npm run package"
},
"np": {
Expand Down
12 changes: 11 additions & 1 deletion src/TestExample/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@

let background = 'night';
let align = 'centre';
let mobileVariant = 'blocks';
let transparentFloat = undefined;
let resizeInteractive = undefined;
let vizMarkerThreshold = 20;
let ratio = 1;

$: layout = { align, transparentFloat, resizeInteractive };
$: layout = { align, transparentFloat, resizeInteractive, mobileVariant };
onMount(() => {
status = 'ready';
});
Expand Down Expand Up @@ -56,6 +57,15 @@
</select>
</label>

<label>
mobileVariant:
<select bind:value={mobileVariant}>
<option value={undefined}>undefined</option>
<option value={"blocks"}>Blocks</option>
<option value={"rows"}>Rows</option>
</select>
</label>

<label>
Viz ratio:
<select bind:value={ratio}>
Expand Down
1 change: 1 addition & 0 deletions src/TestExample/ScrollyAppTest.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
let stProgress;

const onMarker = (detail) => {
console.log(detail);
number = detail.number;
};

Expand Down
Loading