diff --git a/src/components/ScrollerVideo/ScrollerVideo.mdx b/src/components/ScrollerVideo/ScrollerVideo.mdx index a38b8a76..c5903c5c 100644 --- a/src/components/ScrollerVideo/ScrollerVideo.mdx +++ b/src/components/ScrollerVideo/ScrollerVideo.mdx @@ -10,9 +10,9 @@ The `ScrollerVideo` component creates interactive video experiences that respond ## Basic demo -To use the `ScrollerVideo` component, import it and provide the video source. Set the `height` prop to any valid CSS height value such as `1200px`, `150vh`, or `500lvh` to set the scroll height. + To use the `ScrollerVideo` component, import it and provide the video source. The scroll height defaults to `200lvh`, but you can adjust this to any valid CSS height value such as `1200px` or `200lvh` with the `height` prop. -> It is advisable to use 'lvh' or 'svh' units instead of 'vh' unit for the height, as these units are more reliable across different devices. +> 💡TIP: Use `lvh` or `svh` units instead of `vh` unit for the height, as [these units](https://www.w3.org/TR/css-values-4/#large-viewport-size) are more reliable on mobile or other devices where elements such as the address bar toggle between being shown and hidden. [Demo](?path=/story/components-graphics-scrollervideo--demo) @@ -21,6 +21,7 @@ To use the `ScrollerVideo` component, import it and provide the video source. Se import { ScrollerVideo } from '@reuters-graphics/graphics-components'; + ``` @@ -69,7 +70,7 @@ To show different videos based on the screen width, use the `ScrollerVideo` comp ## Embeds -Setting `embedded` will autoplay the entire `ScrollerVideo` component like a video. +Setting `embedded` to `true` will turn `ScrollerVideo` into an embeddable version, where the video autoplays when the user scrolls upon it. Optionally, you can control the embed video behaviour by passing `embeddedProps` to control the autoplay `delay`, `threshold` for triggering autoplay, and the `duration` of the video. > 💡**TIP:** Another way to recreate the ScrollerVideo experience for embeds is to record the desktop screen with [Scroll Capture](https://chromewebstore.google.com/detail/scroll-capture/egmhoeaacclmanaimofoooiamhpkimkk?hl=en) while scrolling through the video and use that video instead as an HTML video component. @@ -78,19 +79,17 @@ Setting `embedded` will autoplay the entire `ScrollerVideo` component like a vid ```svelte ``` diff --git a/src/components/ScrollerVideo/ScrollerVideo.stories.svelte b/src/components/ScrollerVideo/ScrollerVideo.stories.svelte index 3a3b12cf..ffdacf57 100644 --- a/src/components/ScrollerVideo/ScrollerVideo.stories.svelte +++ b/src/components/ScrollerVideo/ScrollerVideo.stories.svelte @@ -137,17 +137,7 @@ }; const args = { - trackScroll: true, - height: '500lvh', showDebugInfo: true, - autoplay: false, - full: true, - sticky: true, - objectFit: 'cover', - transitionSpeed: 8, - frameThreshold: 0.1, - useWebCodecs: true, - lockScroll: true, }; @@ -168,21 +158,11 @@ - - - + + import { onDestroy } from 'svelte'; - import ScrollerVideo from './ts/ScrollerVideo.js'; + import ScrollerVideo from './ts/ScrollerVideo'; import Debug from './Debug.svelte'; import type { Snippet } from 'svelte'; import { setContext } from 'svelte'; @@ -15,7 +15,7 @@ /** Bindable instance of ScrollerVideo */ scrollerVideo?: ScrollerVideo; /** Video source URL */ - src?: string; + src: string; /** Bindable percentage value to control video playback. **Ranges from 0 to 1** */ videoPercentage?: number; /** Sets the maximum playbackRate for this video */ @@ -52,8 +52,6 @@ embeddedProps?: { /** When to start the playback in terms of the component's position */ threshold?: number; - /** Height of embedded component */ - height?: string; /** Duration of ScrollerVideo experience as a video */ duration?: number; /** Delay before the playback */ @@ -66,7 +64,6 @@ /** Default properties for embedded videos */ const defaultEmbedProps = { threshold: 0.5, - height: '80lvh', delay: 200, }; @@ -75,14 +72,15 @@ * Handles instantiation, prop changes, and cleanup. */ let { + class: cls = '', + id = '', + src, scrollerVideo = $bindable(), videoPercentage, onReady = $bindable(() => {}), onChange = $bindable(() => {}), height = '200lvh', showDebugInfo = false, - class: cls = '', - id = '', embedded = false, embeddedProps, children, @@ -136,6 +134,7 @@ if (scrollerVideo && scrollerVideo.destroy) scrollerVideo.destroy(); scrollerVideo = new ScrollerVideo({ + src, scrollerVideoContainer, onReady, onChange, @@ -246,7 +245,6 @@ {#if embedded}
{ @@ -260,18 +258,8 @@ } }} > - - -
-
+
+
{@render supportingElements()}
@@ -292,6 +280,11 @@ .scroller-video-container { width: 100%; + // Needs to be >= 100lvh to allow child element to scroll + // 200lvh provides smoother scrolling experience --> + &.embedded { + height: 200lvh; + } &:not(.embedded) { min-height: 100lvh; } diff --git a/src/components/ScrollerVideo/demo/Embedded.svelte b/src/components/ScrollerVideo/demo/Embedded.svelte index d762b740..d8cff44f 100644 --- a/src/components/ScrollerVideo/demo/Embedded.svelte +++ b/src/components/ScrollerVideo/demo/Embedded.svelte @@ -3,6 +3,7 @@ import ScrollerVideoForeground from '../ScrollerVideoForeground.svelte'; import Goldengate from '../videos/goldengate.mp4'; import BodyText from '../../BodyText/BodyText.svelte'; + import Block from '../../Block/Block.svelte'; import type { ContainerWidth } from '../../@types/global'; @@ -46,22 +47,24 @@ const dummyText = 'Reprehenderit hamburger pork bresaola, dolore chuck sirloin landjaeger ham hock tempor meatball alcatra nostrud pork belly. Culpa pork belly doner ea jowl, elit deserunt leberkas cow shoulder ham hock dolore.'; const scrollerVideoBlock = content.blocks[0]; + + let embedded = $state(true); + + + + + + + + + diff --git a/src/components/ScrollerVideo/ts/ScrollerVideo.ts b/src/components/ScrollerVideo/ts/ScrollerVideo.ts index b1457d00..d8db3863 100644 --- a/src/components/ScrollerVideo/ts/ScrollerVideo.ts +++ b/src/components/ScrollerVideo/ts/ScrollerVideo.ts @@ -4,7 +4,7 @@ import { debounce, isScrollPositionAtTarget, map, constrain } from './utils'; import { createComponentState, type ScrollerVideoState } from './state.svelte'; interface ScrollerVideoArgs { - src?: string; + src: string; scrollerVideoContainer: HTMLElement | string; objectFit?: string; sticky?: boolean; @@ -192,7 +192,7 @@ class ScrollerVideo { * @param {ScrollerVideoArgs} args - The arguments for initialization. */ constructor({ - src = 'https://scrollyvideo.js.org/goldengate.mp4', + src, scrollerVideoContainer, objectFit = 'cover', sticky = true, @@ -202,8 +202,8 @@ class ScrollerVideo { transitionSpeed = 8, frameThreshold = 0.1, useWebCodecs = true, - onReady = () => {}, - onChange = (_percentage?: number) => {}, + onReady = () => { }, + onChange = (_percentage?: number) => { }, debug = false, autoplay = false, }: ScrollerVideoArgs) { @@ -240,24 +240,8 @@ class ScrollerVideo { this.totalTime = 0; // The total time of the video, used for calculating percentage this.transitioningRaf = null; this.componentState = createComponentState(); - this.componentState.willAutoPlay = autoplay; - // Make sure that we have a DOM - if (typeof document !== 'object') { - console.error('ScrollerVideo must be initiated in a DOM context'); - return; - } - - // Make sure the basic arguments are set for scrollervideo - if (!scrollerVideoContainer) { - console.error('scrollerVideoContainer must be a valid DOM object'); - return; - } - if (!src) { - console.error('Must provide valid video src to ScrollerVideo'); - return; - } // Save the container. If the container is a string we get the element @@ -741,7 +725,7 @@ class ScrollerVideo { const hasPassedThreshold = isForwardTransition ? this.currentTime >= this.targetTime - : this.currentTime <= this.targetTime; + : this.currentTime <= this.targetTime; if (this.componentState.isAutoPlaying) { this.componentState.autoplayProgress = parseFloat( @@ -780,7 +764,7 @@ class ScrollerVideo { isForwardTransition ? startCurrentTime + easedProgress * Math.abs(distance) * transitionSpeed - : startCurrentTime - + : startCurrentTime - easedProgress * Math.abs(distance) * transitionSpeed; if (this.canvas) { @@ -869,7 +853,7 @@ class ScrollerVideo { const targetDuration = this.frames?.length && this.frameRate ? this.frames.length / this.frameRate - : this.video?.duration || 0; + : this.video?.duration || 0; // The time we want to transition to this.targetTime = Math.max(Math.min(percentage, 1), 0) * targetDuration;