delets Test, starts video migration
This commit is contained in:
parent
fd5df38dcf
commit
eaa458a0fa
15 changed files with 201 additions and 624 deletions
|
|
@ -1,44 +0,0 @@
|
|||
<script module lang="ts">
|
||||
import TestForSvelte5 from './TestForSvelte5.svelte';
|
||||
// Don't lose the "?raw" in markdown imports!
|
||||
// @ts-ignore raw
|
||||
import componentDocs from './stories/docs/component.md?raw';
|
||||
|
||||
import { withComponentDocs } from '$docs/utils/withParams.js';
|
||||
|
||||
export const meta = {
|
||||
title: 'Components/Graphics/TestForSvelte5',
|
||||
component: TestForSvelte5,
|
||||
...withComponentDocs(componentDocs),
|
||||
// https://storybook.js.org/docs/svelte/essentials/controls
|
||||
argTypes: {
|
||||
width: {
|
||||
control: 'select',
|
||||
options: ['normal', 'wide', 'wider', 'widest', 'fluid'],
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { Template, Story } from '@storybook/addon-svelte-csf';
|
||||
|
||||
// 🖼️ You can import images you need in stories directly in code!
|
||||
// @ts-ignore img
|
||||
import SharkImg from './stories/shark.jpg';
|
||||
</script>
|
||||
|
||||
<Template >
|
||||
{#snippet children({ ...args })}
|
||||
<TestForSvelte5 {...args} />
|
||||
{/snippet}
|
||||
</Template>
|
||||
|
||||
<Story
|
||||
name="Default"
|
||||
args="{{
|
||||
width: 'normal',
|
||||
src: SharkImg,
|
||||
altText: "Duh dum! It's a shark!!",
|
||||
}}"
|
||||
/>
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
<!-- @migration-task Error while migrating Svelte code: Cannot set properties of undefined (setting 'next') -->
|
||||
<!-- @component `TestForSvelte5` [Read the docs.](https://reuters-graphics.github.io/graphics-components/?path=/docs/components-graphics-testforsvelte5--docs) -->
|
||||
<script lang="ts">
|
||||
/** ✏️ DOCUMENT your chart's props using TypeScript and JSDoc comments like below! */
|
||||
|
||||
/**
|
||||
* A source for the image.
|
||||
* @required
|
||||
*/
|
||||
export let src: string;
|
||||
|
||||
/**
|
||||
* AltText for the image.
|
||||
* @required
|
||||
*/
|
||||
export let altText: string;
|
||||
|
||||
/** Height of the image. */
|
||||
export let height: number = 500;
|
||||
|
||||
// You can declare custom types to help users implement your component.
|
||||
type ContainerWidth = 'normal' | 'wide' | 'wider' | 'widest' | 'fluid';
|
||||
|
||||
/** Width of the component within the text well. */
|
||||
export let width: ContainerWidth = 'normal';
|
||||
|
||||
/** Add an ID to target with SCSS. */
|
||||
export let id: string = '';
|
||||
|
||||
/** Add a class to target with SCSS. */
|
||||
let cls: string = '';
|
||||
export { cls as class };
|
||||
|
||||
import Block from '../Block/Block.svelte';
|
||||
</script>
|
||||
|
||||
<Block {width} {id} class="photo {cls}">
|
||||
<div
|
||||
style:background-image="{`url(${src})`}"
|
||||
style:height="{`${height}px`}"
|
||||
></div>
|
||||
<p class="visually-hidden">{altText}</p>
|
||||
</Block>
|
||||
|
||||
<style lang="scss">
|
||||
div {
|
||||
width: 100%;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
}
|
||||
.visually-hidden {
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
width: 1px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
> **Welcome to your new component!** Use this template to build your component and customise its storybook.
|
||||
|
||||
- Build your component in `TestForSvelte5/TestForSvelte5.svelte`.
|
||||
- Write your component's storybook in `TestForSvelte5/TestForSvelte5.stories.svelte`.
|
||||
- Don't forget to add your component to `src/index.js`:
|
||||
|
||||
```javascript
|
||||
// ...
|
||||
export { default as TestForSvelte5 } from './components/TestForSvelte5/TestForSvelte5.svelte';
|
||||
```
|
||||
|
||||
- Commit your component to a new branch and push it to GitHub! 🏁
|
||||
|
||||
---
|
||||
|
||||
```html
|
||||
<script>
|
||||
import { TestForSvelte5 } from '@reuters-graphics/graphics-components';
|
||||
</script>
|
||||
|
||||
|
||||
<TestForSvelte5 />
|
||||
```
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 237 KiB |
25
src/components/Video/Video.mdx
Normal file
25
src/components/Video/Video.mdx
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import { Meta, Canvas } from '@storybook/blocks';
|
||||
|
||||
import * as VideoStories from './Video.stories.svelte';
|
||||
|
||||
<Meta of={VideoStories} />
|
||||
|
||||
# Video
|
||||
|
||||
The `Video` component adds video to your page. Can play on load or when the video comes into view and has play/pause controls. Supports videos with or without audio.
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import { Video } from '@reuters-graphics/graphics-components';
|
||||
import { assets } from '$app/paths'; // If using local video in the Graphics Kit
|
||||
</script>
|
||||
|
||||
<Video
|
||||
ariaDescription={'Compulsory description of your video for screen readers.'}
|
||||
src={`${assets}/videos/myVideo.mp4`}
|
||||
width={'wide'}
|
||||
caption={'Optional caption for your video.'}
|
||||
/>
|
||||
```
|
||||
|
||||
<Canvas of={VideoStories.Demo} />
|
||||
|
|
@ -1,55 +1,32 @@
|
|||
<script module lang="ts">
|
||||
// @ts-ignore raw
|
||||
import componentDocs from './stories/docs/component.md?raw';
|
||||
// @ts-ignore raw
|
||||
import playAndLoopDocs from './stories/docs/playAndLoop.md?raw';
|
||||
// @ts-ignore raw
|
||||
import controlsDocs from './stories/docs/controls.md?raw';
|
||||
// @ts-ignore raw
|
||||
// import withSoundDocs from './stories/docs/withSound.md?raw';
|
||||
|
||||
import { defineMeta } from '@storybook/addon-svelte-csf';
|
||||
import Video from './Video.svelte';
|
||||
|
||||
import {
|
||||
withComponentDocs,
|
||||
withStoryDocs,
|
||||
} from '$lib/docs/utils/withParams.js';
|
||||
|
||||
export const meta = {
|
||||
const { Story } = defineMeta({
|
||||
title: 'Components/Multimedia/Video',
|
||||
component: Video,
|
||||
...withComponentDocs(componentDocs),
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<script>
|
||||
import { Template, Story } from '@storybook/addon-svelte-csf';
|
||||
|
||||
// @ts-ignore raw
|
||||
import SilentVideo from './stories/videos/silent-video.mp4';
|
||||
// @ts-ignore raw
|
||||
import SoundVideo from './stories/videos/sound-video.mp4';
|
||||
import SilentVideo from './demo/videos/silent-video.mp4';
|
||||
import SoundVideo from './demo/videos/sound-video.mp4';
|
||||
</script>
|
||||
|
||||
<Template >
|
||||
{#snippet children({ args })}
|
||||
<Video {...args} />
|
||||
{/snippet}
|
||||
</Template>
|
||||
|
||||
<Story
|
||||
name="Default"
|
||||
args="{{
|
||||
name="Demo"
|
||||
args={{
|
||||
ariaDescription: 'Compulsory description of your video for screen readers.',
|
||||
src: SilentVideo,
|
||||
width: 'wide',
|
||||
notes: 'Optional caption for your video.',
|
||||
}}"
|
||||
}}
|
||||
/>
|
||||
|
||||
<Story
|
||||
name="Playing and looping"
|
||||
args="{{
|
||||
exportName="PlayingAndLooping"
|
||||
args={{
|
||||
ariaDescription: 'Compulsory description of your video for screen readers.',
|
||||
src: SilentVideo,
|
||||
width: 'normal',
|
||||
|
|
@ -57,13 +34,12 @@
|
|||
notes:
|
||||
"World's longest glass bridge opens to public in Vietnam. (c) 2022 Thomson Reuters",
|
||||
playVideoThreshold: 0.9,
|
||||
}}"
|
||||
{...withStoryDocs(playAndLoopDocs)}
|
||||
}}
|
||||
/>
|
||||
|
||||
<Story
|
||||
name="Controls"
|
||||
args="{{
|
||||
args={{
|
||||
ariaDescription: 'Compulsory description of your video for screen readers.',
|
||||
src: SilentVideo,
|
||||
width: 'normal',
|
||||
|
|
@ -76,13 +52,13 @@
|
|||
separateReplayIcon: true,
|
||||
loopVideo: false,
|
||||
hoverToSeeControls: true,
|
||||
}}"
|
||||
{...withStoryDocs(controlsDocs)}
|
||||
}}
|
||||
/>
|
||||
|
||||
<Story
|
||||
name="Videos with sound"
|
||||
args="{{
|
||||
exportName="WithSound"
|
||||
args={{
|
||||
ariaDescription: 'Compulsory description of your video for screen readers.',
|
||||
src: SoundVideo,
|
||||
width: 'normal',
|
||||
|
|
@ -94,6 +70,5 @@
|
|||
muteVideo: false,
|
||||
playVideoWhenInView: true,
|
||||
allowSoundToAutoplay: true,
|
||||
}}"
|
||||
{...withStoryDocs(controlsDocs)}
|
||||
}}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,124 +1,114 @@
|
|||
<!-- @migration-task Error while migrating Svelte code: Cannot set properties of undefined (setting 'next') -->
|
||||
<!-- @component `Video` [Read the docs.](https://reuters-graphics.github.io/graphics-components/?path=/docs/components-multimedia-video--docs) -->
|
||||
<script lang="ts">
|
||||
import IntersectionObserver from 'svelte-intersection-observer';
|
||||
import Controls from './Controls.svelte';
|
||||
import Controls from './components/Controls.svelte';
|
||||
import GraphicBlock from '../GraphicBlock/GraphicBlock.svelte';
|
||||
import type { ContainerWidth } from '../@types/global';
|
||||
import type { Snippet } from 'svelte';
|
||||
|
||||
/// //////////////////////////////////
|
||||
/// /////////// Props ////////////////
|
||||
/// //////////////////////////////////
|
||||
interface Props {
|
||||
/** Video source */
|
||||
src: string;
|
||||
/** Image to be shown while the video is downloading */
|
||||
poster?: string;
|
||||
/** Whether to wrap the graphic with an aria hidden tag. */
|
||||
hidden?: boolean;
|
||||
/** ARIA description, passed in as a markdown string. */
|
||||
ariaDescription?: string;
|
||||
/** Add extra classes to the block tag to target it with custom CSS. */
|
||||
class?: string;
|
||||
/** Title of the graphic */
|
||||
title?: string;
|
||||
/** Notes to the graphic, passed in as a markdown string OR a custom snippet. */
|
||||
notes: string | Snippet;
|
||||
/** Description of the graphic, passed in as a markdown string. */
|
||||
description?: string;
|
||||
/** Width of the block within the article well. */
|
||||
width?: ContainerWidth;
|
||||
/** Set a different width for the text within the text well, for example, "normal" to keep the title, description and notes inline with the rest of the text well. Can't ever be wider than `width`. */
|
||||
textWidth?: ContainerWidth;
|
||||
/** Preload options. `auto` is ignored if `autoplay` is true. Can also be `none` or `metadata`. */
|
||||
preloadVideo?: 'auto' | 'none' | 'metadata';
|
||||
/** Whether the video should loop. */
|
||||
loopVideo?: boolean;
|
||||
/** Whether video should have sound or not. */
|
||||
muteVideo?: boolean;
|
||||
/** If `true`, this allow videos with sound to autoplay if the user has previously interacted with DOM */
|
||||
sountAutoplay?: boolean;
|
||||
/** Whether the video should play when it comes into view or just on page load */
|
||||
playVideoWhenInView?: boolean;
|
||||
/** If video plays with intersection observer, how much of it should be into view to start playing */
|
||||
playVideoThreshold?: number;
|
||||
/** Whether to have the option to pause and play video */
|
||||
possibleToPlayPause?: boolean;
|
||||
/** Whether to show the play / pause buttons */
|
||||
showControls?: boolean;
|
||||
/** Whether you need to hover over the video to see the controls */
|
||||
hoverToSeeControls?: boolean;
|
||||
/** Whether to use a separate replay icon or use the play icon for replay as well */
|
||||
separateReplayIcon?: boolean;
|
||||
/** Change the colour of the play/pause button */
|
||||
controlsColour?: string;
|
||||
/** Change the opacity of the play/pause button */
|
||||
controlsOpacity?: number;
|
||||
/** Have four options for controls position - top right, top left, bottom right, bottom left */
|
||||
controlsPosition?:
|
||||
| 'top right'
|
||||
| 'top left'
|
||||
| 'bottom right'
|
||||
| 'bottom left';
|
||||
}
|
||||
|
||||
/**
|
||||
* Video src
|
||||
* @type {string}
|
||||
* @required
|
||||
*/
|
||||
export let src: string;
|
||||
|
||||
/**
|
||||
* Image to be shown while the video is downloading
|
||||
*/
|
||||
export let poster: string = '';
|
||||
|
||||
/**
|
||||
* Whether to wrap the graphic with an aria hidden tag.
|
||||
*/
|
||||
export let hidden: boolean = true;
|
||||
|
||||
/**
|
||||
* ARIA description, passed in as a markdown string.
|
||||
* @type {string}
|
||||
*/
|
||||
export let ariaDescription: string | null = null;
|
||||
|
||||
/** Add extra classes to the block tag to target it with custom CSS. */
|
||||
let cls: string = '';
|
||||
export { cls as class };
|
||||
|
||||
/**
|
||||
* Title of the graphic
|
||||
* @type {string}
|
||||
*/
|
||||
export let title: string | null = null;
|
||||
|
||||
/**
|
||||
* Notes to the graphic, passed in as a markdown string.
|
||||
* @type {string}
|
||||
*/
|
||||
export let notes: string | null = null;
|
||||
|
||||
/**
|
||||
* Description of the graphic, passed in as a markdown string.
|
||||
* @type {string}
|
||||
*/
|
||||
export let description: string | null = null;
|
||||
|
||||
/**
|
||||
* Width of the block within the article well.
|
||||
* @type {string}
|
||||
*/
|
||||
export let width: ContainerWidth = 'normal';
|
||||
|
||||
type PreloadOptions = 'auto' | 'none' | 'metadata';
|
||||
|
||||
/**
|
||||
* Set a different width for the text within the text well, for example,
|
||||
* "normal" to keep the title, description and notes inline with the rest
|
||||
* of the text well. Can't ever be wider than `width`.
|
||||
* @type {string}
|
||||
*/
|
||||
export let textWidth: ContainerWidth | null = 'normal';
|
||||
|
||||
/**
|
||||
* Preload options. `auto` is ignored if `autoplay` is true. Can also be `none` or `metadata`.
|
||||
* @type {string}
|
||||
*/
|
||||
export let preloadVideo: PreloadOptions = 'auto';
|
||||
/**
|
||||
* Whether the video should loop.
|
||||
*/
|
||||
export let loopVideo: boolean = true;
|
||||
/**
|
||||
* Whether video should have sound or not.
|
||||
*/
|
||||
export let muteVideo: boolean = true;
|
||||
export let allowSoundToAutoplay = false; // for video with sound, whether video should be allowed to autoplay if the user has previously interacted with DOM
|
||||
|
||||
export let playVideoWhenInView = true; // whether the video should play when it comes into view or just on page load
|
||||
export let playVideoThreshold = 0.5; // if video plays with intersection observer, how much of it should be into view to start playing
|
||||
export let possibleToPlayPause = true; // whether to have the option to pause and play video
|
||||
|
||||
export let showControls = true; // whetner to show the play / pause buttons
|
||||
export let hoverToSeeControls = false; // whether you need to hover over the video to see the controls
|
||||
export let separateReplayIcon = false; // whether to use a separate replay icon or use the play icon for replay as well
|
||||
export let controlsColour = '#333'; // change the colour of the play/pause button
|
||||
export let controlsOpacity = 0.5; // change the opacity of the play/pause button
|
||||
$: interactiveControlsOpacity = 0;
|
||||
export let controlsPosition = 'top left'; // have four options for controls position - top right, top left, bottom right, bottom left
|
||||
let {
|
||||
src,
|
||||
poster = '',
|
||||
hidden = true,
|
||||
ariaDescription,
|
||||
class: cls = '',
|
||||
title,
|
||||
notes,
|
||||
description,
|
||||
width = 'normal',
|
||||
textWidth = 'normal',
|
||||
preloadVideo = 'auto',
|
||||
loopVideo = false,
|
||||
muteVideo = true,
|
||||
sountAutoplay = false,
|
||||
playVideoWhenInView = true,
|
||||
playVideoThreshold = 0.5,
|
||||
possibleToPlayPause = true,
|
||||
showControls = true,
|
||||
hoverToSeeControls = false,
|
||||
separateReplayIcon = false,
|
||||
controlsColour = '#333',
|
||||
controlsOpacity = 0.5,
|
||||
controlsPosition = 'top left',
|
||||
}: Props = $props();
|
||||
|
||||
/// //////////////////////////////////
|
||||
/// /////// Internal Logic ///////////
|
||||
/// //////////////////////////////////
|
||||
// If it's not possible to play/pause, then hide the controls
|
||||
if (!possibleToPlayPause) showControls = false;
|
||||
|
||||
// Internal props
|
||||
let time = 0;
|
||||
let duration;
|
||||
let paused = true;
|
||||
let clickedOnPauseBtn = false; // special variable to track if user clicked on 'pause' btn to help with audio logic
|
||||
$: resetCondition = time >= duration; // - 0.1;
|
||||
let time = $state(0);
|
||||
let duration = $state(0);
|
||||
let paused = $state(true);
|
||||
let clickedOnPauseBtn = $state(false); // special variable to track if user clicked on 'pause' btn to help with audio logic
|
||||
let resetCondition = $derived(time >= duration); // - 0.1;
|
||||
|
||||
// Dimensions etc other useful things
|
||||
let heightVideo;
|
||||
let widthVideo;
|
||||
let heightVideoContainer;
|
||||
let widthVideoContainer;
|
||||
let heightVideo = $state(0);
|
||||
let widthVideo = $state(0);
|
||||
let heightVideoContainer = $state(0);
|
||||
let widthVideoContainer = $state(0);
|
||||
const controlsBorderOffset = 50;
|
||||
|
||||
// For intersection observer
|
||||
let intersecting;
|
||||
let element;
|
||||
let videoElement;
|
||||
let intersecting = $state(false);
|
||||
let element: HTMLElement = $state(new HTMLElement()); // ; | null
|
||||
let videoElement: HTMLVideoElement = $state(new HTMLVideoElement());
|
||||
|
||||
// For video with sound, check if there has been an interaction with the DOM
|
||||
let interactedWithDom = false;
|
||||
|
|
@ -126,27 +116,29 @@
|
|||
interactedWithDom = true;
|
||||
};
|
||||
|
||||
// Play the video (with no sound) if it's intersecting; pause when it's no longer intersecting
|
||||
$: if (playVideoWhenInView && intersecting && muteVideo) paused = false;
|
||||
$: if (playVideoWhenInView && !intersecting) paused = true;
|
||||
// Special case for video with sound
|
||||
// Only ff you've clicked on play button or interacted with DOM in any way previously, video with audio will play
|
||||
$: if (
|
||||
allowSoundToAutoplay &&
|
||||
playVideoWhenInView &&
|
||||
intersecting &&
|
||||
!muteVideo &&
|
||||
interactedWithDom &&
|
||||
!clickedOnPauseBtn // so video doesn't autoplay when coming into view again if paused previously
|
||||
) {
|
||||
paused = false;
|
||||
}
|
||||
let interactiveControlsOpacity = $state(controlsOpacity);
|
||||
|
||||
$: if (allowSoundToAutoplay && !muteVideo && !interactedWithDom) {
|
||||
paused = true;
|
||||
}
|
||||
$effect(() => {
|
||||
// Play the video (with no sound) if it's intersecting; pause when it's no longer intersecting
|
||||
if (playVideoWhenInView && intersecting && muteVideo) paused = false;
|
||||
|
||||
$: if (!possibleToPlayPause) showControls = true;
|
||||
// Pause the video if it's no longer intersecting
|
||||
if (playVideoWhenInView && !intersecting) paused = true;
|
||||
|
||||
// Special case for video with sound
|
||||
// Only ff you've clicked on play button or interacted with DOM in any way previously, video with audio will play
|
||||
if (
|
||||
sountAutoplay &&
|
||||
playVideoWhenInView &&
|
||||
intersecting &&
|
||||
!muteVideo &&
|
||||
interactedWithDom &&
|
||||
!clickedOnPauseBtn // so video doesn't autoplay when coming into view again if paused previously
|
||||
)
|
||||
paused = false;
|
||||
|
||||
if (sountAutoplay && !muteVideo && !interactedWithDom) paused = true;
|
||||
});
|
||||
|
||||
// To get the pause state passed up from the Controls
|
||||
const pausePlayEvent = (e) => {
|
||||
|
|
@ -165,32 +157,32 @@
|
|||
</script>
|
||||
|
||||
<svelte:window
|
||||
on:click="{setInteractedWithDom}"
|
||||
on:touchstart="{setInteractedWithDom}"
|
||||
on:click={setInteractedWithDom}
|
||||
on:touchstart={setInteractedWithDom}
|
||||
/>
|
||||
|
||||
<GraphicBlock
|
||||
{textWidth}
|
||||
{title}
|
||||
{description}
|
||||
{notes}
|
||||
notes={typeof notes === 'string' ? notes : null}
|
||||
{width}
|
||||
class="video {cls}"
|
||||
>
|
||||
<div
|
||||
role="figure"
|
||||
on:mouseover="{() => {
|
||||
onmouseover={() => {
|
||||
interactiveControlsOpacity = controlsOpacity;
|
||||
}}"
|
||||
on:focus="{() => {
|
||||
}}
|
||||
onfocus={() => {
|
||||
interactiveControlsOpacity = controlsOpacity;
|
||||
}}"
|
||||
on:mouseout="{() => {
|
||||
}}
|
||||
onmouseout={() => {
|
||||
interactiveControlsOpacity = 0;
|
||||
}}"
|
||||
on:blur="{() => {
|
||||
}}
|
||||
onblur={() => {
|
||||
interactiveControlsOpacity = 0;
|
||||
}}"
|
||||
}}
|
||||
>
|
||||
{#if (hidden && ariaDescription) || !hidden}
|
||||
{#if ariaDescription}
|
||||
|
|
@ -202,25 +194,25 @@
|
|||
<IntersectionObserver
|
||||
{element}
|
||||
bind:intersecting
|
||||
threshold="{playVideoThreshold}"
|
||||
once="{false}"
|
||||
threshold={playVideoThreshold}
|
||||
once={false}
|
||||
>
|
||||
<div
|
||||
bind:this="{element}"
|
||||
bind:this={element}
|
||||
class="video-wrapper relative block"
|
||||
aria-hidden="{hidden}"
|
||||
bind:clientWidth="{widthVideoContainer}"
|
||||
bind:clientHeight="{heightVideoContainer}"
|
||||
aria-hidden={hidden}
|
||||
bind:clientWidth={widthVideoContainer}
|
||||
bind:clientHeight={heightVideoContainer}
|
||||
>
|
||||
{#if possibleToPlayPause}
|
||||
{#if showControls}
|
||||
<Controls
|
||||
on:pausePlayEvent="{pausePlayEvent}"
|
||||
on:pausePlayEvent={pausePlayEvent}
|
||||
{paused}
|
||||
{clickedOnPauseBtn}
|
||||
controlsOpacity="{hoverToSeeControls ?
|
||||
controlsOpacity={hoverToSeeControls ?
|
||||
interactiveControlsOpacity
|
||||
: controlsOpacity}"
|
||||
: controlsOpacity}
|
||||
{controlsPosition}
|
||||
{widthVideoContainer}
|
||||
{heightVideoContainer}
|
||||
|
|
@ -232,32 +224,33 @@
|
|||
{:else}
|
||||
<button
|
||||
class="border-0 m-0 p-0 bg-transparent absolute"
|
||||
on:click="{() => {
|
||||
aria-label="Play or pause video"
|
||||
onclick={() => {
|
||||
if (paused === true) {
|
||||
paused = false;
|
||||
} else {
|
||||
paused = true;
|
||||
}
|
||||
}}"
|
||||
}}
|
||||
style="top: 0; left: 0; width: {widthVideoContainer}px; height: {heightVideoContainer}px;"
|
||||
></button>
|
||||
{/if}
|
||||
{/if}
|
||||
<video
|
||||
bind:this="{videoElement}"
|
||||
bind:this={videoElement}
|
||||
{src}
|
||||
{poster}
|
||||
class="pointer-events-none relative"
|
||||
width="100%"
|
||||
muted="{muteVideo}"
|
||||
muted={muteVideo}
|
||||
playsinline
|
||||
preload="{preloadVideo}"
|
||||
loop="{loopVideo}"
|
||||
bind:currentTime="{time}"
|
||||
preload={preloadVideo}
|
||||
loop={loopVideo}
|
||||
bind:currentTime={time}
|
||||
bind:duration
|
||||
bind:paused
|
||||
bind:clientWidth="{widthVideo}"
|
||||
bind:clientHeight="{heightVideo}"
|
||||
bind:clientWidth={widthVideo}
|
||||
bind:clientHeight={heightVideo}
|
||||
>
|
||||
<track kind="captions" />
|
||||
</video>
|
||||
|
|
@ -267,14 +260,14 @@
|
|||
<!-- Video element without Intersection observer -->
|
||||
<div
|
||||
class="video-wrapper relative"
|
||||
aria-hidden="{hidden}"
|
||||
bind:clientWidth="{widthVideoContainer}"
|
||||
bind:clientHeight="{heightVideoContainer}"
|
||||
aria-hidden={hidden}
|
||||
bind:clientWidth={widthVideoContainer}
|
||||
bind:clientHeight={heightVideoContainer}
|
||||
>
|
||||
{#if possibleToPlayPause}
|
||||
{#if showControls}
|
||||
<Controls
|
||||
on:pausePlayEvent="{pausePlayEvent}"
|
||||
on:pausePlayEvent={pausePlayEvent}
|
||||
{paused}
|
||||
{clickedOnPauseBtn}
|
||||
{controlsOpacity}
|
||||
|
|
@ -289,33 +282,34 @@
|
|||
{:else}
|
||||
<button
|
||||
class="border-0 m-0 p-0 bg-transparent absolute"
|
||||
on:click="{() => {
|
||||
aria-label="Play or pause video"
|
||||
onclick={() => {
|
||||
if (paused === true) {
|
||||
paused = false;
|
||||
} else {
|
||||
paused = true;
|
||||
}
|
||||
}}"
|
||||
}}
|
||||
style="top: 0; left: 0; width: {widthVideoContainer}px; height: {heightVideoContainer}px;"
|
||||
></button>
|
||||
{/if}
|
||||
{/if}
|
||||
<video
|
||||
bind:this="{videoElement}"
|
||||
bind:this={videoElement}
|
||||
{src}
|
||||
{poster}
|
||||
class="pointer-events-none relative"
|
||||
width="100%"
|
||||
muted="{muteVideo}"
|
||||
muted={muteVideo}
|
||||
playsinline
|
||||
preload="{preloadVideo}"
|
||||
loop="{loopVideo}"
|
||||
bind:currentTime="{time}"
|
||||
preload={preloadVideo}
|
||||
loop={loopVideo}
|
||||
bind:currentTime={time}
|
||||
bind:duration
|
||||
bind:paused
|
||||
autoplay
|
||||
bind:clientWidth="{widthVideo}"
|
||||
bind:clientHeight="{heightVideo}"
|
||||
bind:clientWidth={widthVideo}
|
||||
bind:clientHeight={heightVideo}
|
||||
>
|
||||
<track kind="captions" />
|
||||
</video>
|
||||
|
|
@ -323,7 +317,7 @@
|
|||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
{#if $$slots.notes}
|
||||
{#if notes}
|
||||
<!-- Custom notes and source slot -->
|
||||
<slot name="notes" />
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -1,275 +0,0 @@
|
|||
---
|
||||
title: Video
|
||||
description: General-purpose video component. Can play on load or when the video comes into view and has play/pause controls. Supports videos with or without audio.
|
||||
slug: video
|
||||
---
|
||||
|
||||
<script>
|
||||
import {assets} from '$app/paths'; import Video from './index.svelte'; import
|
||||
DemoContainer from '../_docs/DemoContainer/index.svelte';
|
||||
</script>
|
||||
|
||||
<section>
|
||||
|
||||
## {title}
|
||||
|
||||
{description}
|
||||
|
||||
</section>
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import { assets } from '$app/paths'; // helper if importing video from 'statics'
|
||||
import { Video } from '@reuters-graphics/graphics-svelte-components';
|
||||
</script>
|
||||
|
||||
<Video ariaDescription={"Compulsory description of your video for screen
|
||||
readers."} src={`${assets}/videos/myVideo.mp4`} // or a URL to an external video
|
||||
width={'wide'} // normal, wide, wider, widest or fluid caption={'Optional
|
||||
caption for your video.'} />
|
||||
```
|
||||
|
||||
<DemoContainer>
|
||||
<Video
|
||||
ariaDescription="Description of your video for screen readers."
|
||||
src={`${assets}/videos/Sequence silent video_4.mp4`}
|
||||
width="wide"
|
||||
caption="World's longest glass bridge opens to public in Vietnam. (c) 2022 Thomson Reuters"
|
||||
/>
|
||||
</DemoContainer>
|
||||
|
||||
<section>
|
||||
|
||||
#### When to play and whether to loop
|
||||
|
||||
`playVideoWhenInView`, `playVideoThreshold`
|
||||
|
||||
- By default, the video will **start playing when 50% of the video element's height is visible on the page**.
|
||||
To control the threshold of visibility at which the video starts playing, add the prop `playVideoThreshold` and set it to a value between 0 and 1,
|
||||
where 0 means that the video will start playing as soon as its top enters the viewport, while 1 means it will start when the whole video is in the viewport.
|
||||
|
||||
- If you don't want the video to play when you scroll to it, but **on page load**, add the prop `playVideoWhenInView={false}`. The default of the prop is `true`,
|
||||
which corresponds to the behaviour described above.
|
||||
|
||||
`loopVideo`
|
||||
|
||||
- By default, the video will **loop**. If you don't want that, add the prop `loopVideo={false}`.
|
||||
|
||||
Here is an example of what the same video would look like with a visibility threshold of 0.9 and not looping. Scroll down slowly to observe the behaviour.
|
||||
|
||||
</section>
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import { Video } from '@reuters-graphics/graphics-svelte-components';
|
||||
</script>
|
||||
|
||||
<video
|
||||
ariaDescription="{'Compulsory description of your video for screen readers.'}"
|
||||
src="{'path-to-video-or-external-url'}"
|
||||
width="{'normal'}"
|
||||
loopVideo="{false}"
|
||||
playVideoThreshold="{0.9}"></video>
|
||||
```
|
||||
|
||||
<DemoContainer>
|
||||
<Video
|
||||
ariaDescription="Description of your video for screen readers."
|
||||
src={`${assets}/videos/Sequence silent video_4.mp4`}
|
||||
width="normal"
|
||||
caption="World's longest glass bridge opens to public in Vietnam. (c) 2022 Thomson Reuters"
|
||||
loopVideo={false}
|
||||
playVideoThreshold={0.9}
|
||||
/>
|
||||
</DemoContainer>
|
||||
|
||||
<section>
|
||||
|
||||
#### Controls (play / pause)
|
||||
|
||||
`showControls`
|
||||
|
||||
- By default, the video has a play/pause button, which corresponds to
|
||||
`showControls={true}`. If you don't want these, just set `showControls={false}`.
|
||||
When you do that, the icons themselves will disappear but the functionality of playing and pausing remains and can be done by clicking on the video itself.
|
||||
If you don't want to enable any play/pause functionality, add `possibleToPlayPause={false}`.
|
||||
- If you want to show the controls only when the video is hovered, set `hoverToSeeControls={true}`.
|
||||
|
||||
`controlsColour`, `controlsOpacity`, `controlsPosition`, `separateReplayIcon`
|
||||
|
||||
If you do want to leave the controls, you have a couple of options to style them:
|
||||
|
||||
- Set `controlsColour` to a colour of your choosing.
|
||||
- Set `controlsOpacity` to a value between `0` and `1` to control the opacity. The default is `0.5`.
|
||||
- Change the placement of the controls to one of: `top right`, `top left`, `bottom right`, `bottom left` by setting `controlsPosition`.
|
||||
- Change the play button to a replay button at the end of the video with the option `separateReplayIcon={true}`.
|
||||
|
||||
Here is an example with bottom right corner white opaque controls, with a replay button, where you have to hover on the video to see the controls.
|
||||
|
||||
</section>
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import { Video } from '@reuters-graphics/graphics-svelte-components';
|
||||
</script>
|
||||
|
||||
<Video ariaDescription={"Compulsory description of your video for screen
|
||||
readers."} src={'path-to-video-or-external-url'} size={'normal'}
|
||||
caption={'Optional caption for your video.'} playVideoThreshold={0.1}
|
||||
controlsColour={'white'} controlsOpacity={1} controlsPosition={'bottom right'}
|
||||
separateReplayIcon={true} loopVideo={false} // If you don't set loopVideo to
|
||||
false, you won't see the loop button hoverToSeeControls={true} />
|
||||
```
|
||||
|
||||
<DemoContainer>
|
||||
<Video
|
||||
ariaDescription="Description of your video for screen readers."
|
||||
src={`${assets}/videos/Sequence silent video_4.mp4`}
|
||||
size="normal"
|
||||
caption="World's longest glass bridge opens to public in Vietnam. (c) 2022 Thomson Reuters"
|
||||
playVideoThreshold={0.1}
|
||||
controlsColour={'white'}
|
||||
controlsOpacity={1}
|
||||
controlsPosition={'bottom right'}
|
||||
separateReplayIcon={true}
|
||||
loopVideo={false}
|
||||
hoverToSeeControls={true}
|
||||
/>
|
||||
</DemoContainer>
|
||||
|
||||
<section>
|
||||
|
||||
#### Videos with sound
|
||||
|
||||
If you've ever had to put sound on a page in recent years,
|
||||
you'll know that auto-playing sound is not allowed by browsers. The user will need to interact with the page first, and it will depend on your
|
||||
particular use case how and when you'd like this to happen. This component provides two options to deal with this.
|
||||
If you have a video with sound, make sure to add the prop `muteVideo={false}`.
|
||||
|
||||
Then you can either:
|
||||
|
||||
- `allowSoundToAutoplay={false}` (default) : Don't allow the video to autoplay under any circumstances other than when the user clicks the 'play' on the video. Note that this
|
||||
works whether or not you have the controls visible, i.e. with `showControls` being `true` or `false`, as long as you allow
|
||||
play/pause behaviour with `possibleToPlayPause={true}` (default).
|
||||
|
||||
- `allowSoundToAutoplay={true}` : Allow the video to autoplay when it comes into view as long as the user has interacted with the page preivously, i.e. they have clicked/tapped
|
||||
anywhere on the page.
|
||||
|
||||
You should keep `playVideoWhenInView={true}` (default). There is no option to autoplay video with sound when the user clicks on the page
|
||||
elsewhere if the video is not in view. In other words, you can't start playing sound for a video which is not in view with this component.
|
||||
This is probably not a behaviour you'd want anyway.
|
||||
|
||||
The example below allows for autoplay if the user has interacted with the page before the video comes into view. To see this, reload the page
|
||||
and go to the top. Click anywhere on the page before scrolling down to the video and you should see it autoplay when it comes into view.
|
||||
|
||||
</section>
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import { Video } from '@reuters-graphics/graphics-svelte-components';
|
||||
</script>
|
||||
|
||||
<video
|
||||
ariaDescription="{'Compulsory description of your video for screen readers.'}"
|
||||
src="{'path-to-video-or-external-url'}"
|
||||
width="{'normal'}"
|
||||
controlsOpacity="{1}"
|
||||
loopVideo="{false}"
|
||||
muteVideo="{false}"
|
||||
allowSoundToAutoplay="{true}"></video>
|
||||
```
|
||||
|
||||
<DemoContainer>
|
||||
<Video
|
||||
ariaDescription="Description of your video for screen readers."
|
||||
src={`${assets}/videos/Sequence sound video.mp4`}
|
||||
width="normal"
|
||||
caption="World's longest glass bridge opens to public in Vietnam. (c) 2022 Thomson Reuters"
|
||||
controlsOpacity={1}
|
||||
loopVideo={false}
|
||||
muteVideo={false}
|
||||
playVideoWhenInView={true}
|
||||
showControls={true}
|
||||
allowSoundToAutoplay={true}
|
||||
/>
|
||||
</DemoContainer>
|
||||
|
||||
<section>
|
||||
|
||||
### All the props
|
||||
|
||||
Here is a list of all the props that you can pass to the component for reference. Most of them are discussed above.
|
||||
|
||||
##### Required
|
||||
|
||||
- **src** (string) - Path to the video relative to the `statics` folder.
|
||||
- **ariaDescription** (string) and **ariaHidden** (bool) - Either write a description for screen readers in ariaDescription or set ariaHidden to false.
|
||||
|
||||
##### Optional
|
||||
|
||||
- **caption** (string) - default <span class='default'>**" " (none)**</span> <span class='separator'></span> options: Write a caption to go with the video.
|
||||
|
||||
- **size** (string) - default <span class='default'>**'normal'**</span> <span class='separator'></span> options: _'normal'_, _'wide'_, _'wider'_, _'widest'_, _'fluid'_
|
||||
|
||||
- **preloadVideo** (string) - default <span class='default'>**'auto'**</span> <span class='separator'></span>
|
||||
options: _'none'_ (Don't preload the video on page load),
|
||||
_'auto'_ (Preload the video),
|
||||
_'metadata'_ (Only preload the metadata)
|
||||
|
||||
- **loopVideo** (bool) - default <span class='default'>**true**</span> <span class='separator'></span> options: _true_, _false_
|
||||
|
||||
- **muteVideo** (bool) - default <span class='default'>**true**</span> <span class='separator'></span> options: _true_, _false_
|
||||
|
||||
- **allowSoundToAutoplay** (bool) - default <span class='default'>**false**</span><span class='separator'></span> options: _true_, _false_.
|
||||
For video with sound, whether video should be allowed to autoplay if the user has previously interacted with DOM. You need to set `muteVideo` to `true`
|
||||
for this to work.
|
||||
|
||||
- **playVideoWhenInView** (bool) - default <span class='default'>**true**</span> <span class='separator'></span>
|
||||
options: _true_ (Only start playing the video when it comes into view), _false_ (Start playing as soon as the page and video load)
|
||||
|
||||
- **playVideoThreshold** (float) - default <span class='default'>**0.5**</span> <span class='separator'></span> options: _float between 0 and 1_.
|
||||
How much of the video should be into view to start playing when playVideoWhenInView is true.
|
||||
|
||||
- **possibleToPlayPause** (bool) - default <span class='default'>**true**</span> <span class='separator'></span> options: _true_, _false_
|
||||
|
||||
- **showControls** (bool) - default <span class='default'>**true**</span> <span class='separator'></span> options: _true_, _false_.
|
||||
Whether to show the play/pause controls or not.
|
||||
|
||||
- **hoverToSeeControls** (bool) - default <span class='default'>**false**</span><span class='separator'></span> options: _true_, _false_.
|
||||
Whether you need to hover over the video to see the controls.
|
||||
|
||||
- **separateReplayIcon** (bool) - default <span class='default'>**false**</span> <span class='separator'></span> options: _true_, _false_.
|
||||
Whether to use a separate replay icon or use the play icon for replay as well.
|
||||
|
||||
- **controlsColour** (colour string) - default <span class='default'>**'#333'**</span> <span class='separator'></span> options: _any valid colour string_.
|
||||
Controls the colour of the play/pause buttons.
|
||||
|
||||
- **controlsOpacity** (float) - default <span class='default'>**0.5**</span> <span class='separator'></span> options: _float between 0 and 1_.
|
||||
Controls the opacity of the play/pause buttons.
|
||||
|
||||
- **controlsPosition** (string) - default <span class='default'>**'top left'**</span> <span class='separator'></span>
|
||||
options: _'top right'_, _'top left'_, _'bottom right'_, _'bottom left'_.
|
||||
Controls the position of the play/pause buttons.
|
||||
|
||||
</section>
|
||||
|
||||
<style>
|
||||
ul {
|
||||
list-style-type: none;
|
||||
}
|
||||
li {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
.default {
|
||||
color: #006d77;
|
||||
}
|
||||
.separator {
|
||||
display: inline-block;
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
background-color: #666;
|
||||
border-radius: 50%;
|
||||
margin: 0 5px 0 5px;
|
||||
transform: translateY(-2.5px);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
General-purpose video component. Can play on load or when the video comes into view and has play/pause controls. Supports videos with or without audio.
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import { Video } from '@reuters-graphics/graphics-components';
|
||||
import { assets } from '$app/paths'; // If using local video in the Graphics Kit
|
||||
</script>
|
||||
|
||||
<Video
|
||||
ariaDescription="{'Compulsory description of your video for screen readers.'}"
|
||||
src="{`${assets}/videos/myVideo.mp4`}"
|
||||
width="{'wide'}"
|
||||
caption="{'Optional caption for your video.'}"
|
||||
/>
|
||||
```
|
||||
Loading…
Reference in a new issue