Skip to content

Commit

Permalink
Create and use MutedVideoLoop component for convenience
Browse files Browse the repository at this point in the history
  • Loading branch information
yvonnetangsu committed Feb 18, 2025
1 parent 8ccb109 commit cb6b954
Show file tree
Hide file tree
Showing 8 changed files with 109 additions and 53 deletions.
18 changes: 6 additions & 12 deletions components/BlurryPoster/BlurryPoster.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Grid } from '@/components/Grid';
import {
Heading, Paragraph, Text, type HeadingType, SrOnlyText,
} from '@/components/Typography';
import { Video, VideoButton } from '@/components/Video';
import { MutedVideoLoop, VideoButton } from '@/components/Video';
import { getProcessedImage } from '@/utilities/getProcessedImage';
import {
accentBorderColors,
Expand Down Expand Up @@ -193,14 +193,12 @@ export const BlurryPoster = ({
</picture>
)}
{hasBgVideo && (
<Video
<MutedVideoLoop
ref={bgVideoRef}
webmSrc={bgVideoWebm}
mp4Src={bgVideoMp4}
autoPlay
playsInline
loop
muted
onPlay={() => setIsBgPlaying(true)}
onPause={() => setIsBgPlaying(false)}
poster={getProcessedImage(bgVideoPosterSrc, '1600x900', bgVideoPosterFocus)}
className="absolute inset-0 size-full object-cover"
/>
Expand Down Expand Up @@ -318,14 +316,10 @@ export const BlurryPoster = ({
{hasVideo && (
<div className={styles.videoWrapper}>
<div className={styles.videoPlayerWrapper(isTwoCol)}>
<Video
<MutedVideoLoop
ref={videoRef}
webmSrc={videoWebm}
mp4Src={videoMp4}
autoPlay
playsInline
loop
muted
onPlay={() => setIsPlaying(true)}
onPause={() => setIsPlaying(false)}
poster={getProcessedImage(videoPosterSrc, '1600x900', videoPosterFocus)}
Expand All @@ -335,7 +329,7 @@ export const BlurryPoster = ({
<VideoButton
isPause={isPlaying}
onClick={toggleVideo}
className="absolute block z-10 bottom-20 right-20"
className="block z-10 bottom-20 right-20"
/>
</div>
)}
Expand Down
8 changes: 2 additions & 6 deletions components/Hero/BasicHero.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { cnb } from 'cnbuilder';
import { AnimateInView } from '@/components/Animate';
import { Container } from '@/components/Container';
import { Heading, SrOnlyText, Text } from '@/components/Typography';
import { Video, VideoButton } from '@/components/Video';
import { MutedVideoLoop, VideoButton } from '@/components/Video';
import { getProcessedImage } from '@/utilities/getProcessedImage';
import {
gradientFroms,
Expand Down Expand Up @@ -124,14 +124,10 @@ export const BasicHero = ({
</picture>
)}
{hasVideo && (
<Video
<MutedVideoLoop
ref={videoRef}
webmSrc={videoWebm}
mp4Src={videoMp4}
autoPlay
playsInline
loop
muted
onPlay={() => setIsPlaying(true)}
onPause={() => setIsPlaying(false)}
poster={getProcessedImage(videoPosterSrc, '1600x900', videoPosterFocus)}
Expand Down
4 changes: 4 additions & 0 deletions components/Hero/StoryHeroMvp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@ export const StoryHeroMvp = ({
imageSrc={filename}
imageFocus={focus}
alt={alt}
videoWebm={webmFilename}
videoMp4={mp4Filename}
videoPosterSrc={posterFilename}
videoPosterFocus={posterFocus}
isLightHero={isLightHero}
hasCaption={hasCaption}
taxonomy={taxonomy}
Expand Down
6 changes: 5 additions & 1 deletion components/Hero/StoryHeroStacked.styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { cnb } from 'cnbuilder';

export const root = 'relative';

export const contentWrapper = 'mt-40 md:-mt-60 xl:mt-0';
export const contentWrapper = (hasVideo: boolean) => cnb('mt-40 md:-mt-60 xl:mt-0', hasVideo && 'rs-pb-3');
export const superhead = (isLightHero: boolean) => cnb('cc mb-04em w-full', !isLightHero && 'text-shadow-sm');
export const heading = (
isSmallHeading?: boolean,
Expand All @@ -23,3 +23,7 @@ export const mobileImage = 'size-full lg:hidden';
export const captionWrapper = 'mt-06em';
export const caption = 'caption *:leading-display mt-08em max-w-prose-wide';

export const videoWrapper = 'relative';
export const videoPlayerWrapper = 'aspect-w-16 aspect-h-9';
export const video = 'h-full w-full object-cover object-center pointer-events-none';

117 changes: 84 additions & 33 deletions components/Hero/StoryHeroStacked.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { useRef, useState } from 'react';
import { AnimateInView } from '@/components/Animate';
import { Container } from '@/components/Container';
import { CtaLink } from '@/components/Cta';
import { FlexBox } from '@/components/FlexBox';
import {
Heading, Paragraph, Text, SrOnlyText,
} from '@/components/Typography';
import { MutedVideoLoop, VideoButton } from '@/components/Video';
import { getProcessedImage } from '@/utilities/getProcessedImage';
import { getSbImageSize } from '@/utilities/getSbImageSize';
import { taxonomyMap, type TaxonomyType } from '@/utilities/taxonomyMaps';
Expand All @@ -22,6 +24,10 @@ export type StoryHeroStackedProps = {
imageSrc?: string;
imageFocus?: string;
alt?: string;
videoWebm?: string;
videoMp4?: string;
videoPosterSrc?: string;
videoPosterFocus?: string;
hasCaption?: boolean;
isLightHero?: boolean;
taxonomy?: TaxonomyType[];
Expand All @@ -39,6 +45,10 @@ export const StoryHeroStacked = ({
imageSrc,
imageFocus,
alt,
videoWebm,
videoMp4,
videoPosterSrc,
videoPosterFocus,
hasCaption,
isLightHero = false,
taxonomy,
Expand All @@ -56,14 +66,33 @@ export const StoryHeroStacked = ({
// We're using the date only (no time) version of the date picker so trimming the time off
const date = publishedDate?.slice(0, 10);

/**
* Image/video
*/
const hasVideo = !!videoWebm || !!videoMp4;
const hasMedia = !!imageSrc || hasVideo;
const videoRef = useRef<HTMLVideoElement>(null);
const [isPlaying, setIsPlaying] = useState(null);

const toggleVideo = () => {
if (videoRef.current) {
if (isPlaying) {
videoRef.current.pause();
} else {
videoRef.current.play();
}
setIsPlaying(!isPlaying);
}
};

return (
<Container
width="full"
className={styles.root}
pt={10}
style={{ backgroundColor: heroBgColor || '#888' }}
>
<Container className={styles.contentWrapper}>
<Container className={styles.contentWrapper(hasVideo)}>
{superhead && (
<AnimateInView animation="slideUp">
<Text
Expand Down Expand Up @@ -141,39 +170,61 @@ export const StoryHeroStacked = ({
</AnimateInView>
)}
</Container>
{imageSrc && (
{hasMedia && (
<AnimateInView animation="zoomSharpen" duration={1}>
<picture>
<source
srcSet={getProcessedImage(imageSrc, '2000x0', imageFocus)}
media="(min-width: 1500px)"
/>
<source
srcSet={getProcessedImage(imageSrc, '1500x0', imageFocus)}
media="(min-width: 1200px)"
/>
<source
srcSet={getProcessedImage(imageSrc, '1200x0', imageFocus)}
media="(min-width: 768px)"
/>
<source
srcSet={getProcessedImage(imageSrc, '800x0', imageFocus)}
media="(min-width: 576px)"
/>
<source
srcSet={getProcessedImage(imageSrc, '600x0', imageFocus)}
media="(max-width: 575px)"
/>
<img
src={getProcessedImage(imageSrc, '2000x0', imageFocus)}
alt={alt || ''}
aria-describedby={hasCaption ? 'story-hero-caption' : undefined}
fetchPriority="high"
width={imageWidth}
height={imageHeight}
className={styles.image}
/>
</picture>
{imageSrc && (
<picture>
<source
srcSet={getProcessedImage(imageSrc, '2000x0', imageFocus)}
media="(min-width: 1500px)"
/>
<source
srcSet={getProcessedImage(imageSrc, '1500x0', imageFocus)}
media="(min-width: 1200px)"
/>
<source
srcSet={getProcessedImage(imageSrc, '1200x0', imageFocus)}
media="(min-width: 768px)"
/>
<source
srcSet={getProcessedImage(imageSrc, '800x0', imageFocus)}
media="(min-width: 576px)"
/>
<source
srcSet={getProcessedImage(imageSrc, '600x0', imageFocus)}
media="(max-width: 575px)"
/>
<img
src={getProcessedImage(imageSrc, '2000x0', imageFocus)}
alt={alt || ''}
aria-describedby={hasCaption ? 'story-hero-caption' : undefined}
fetchPriority="high"
width={imageWidth}
height={imageHeight}
className={styles.image}
/>
</picture>
)}
{hasVideo && (
<div className={styles.videoWrapper}>
<div className={styles.videoPlayerWrapper}>
<MutedVideoLoop
ref={videoRef}
webmSrc={videoWebm}
mp4Src={videoMp4}
onPlay={() => setIsPlaying(true)}
onPause={() => setIsPlaying(false)}
poster={getProcessedImage(videoPosterSrc, '1600x900', videoPosterFocus)}
className={styles.video}
/>
</div>
<VideoButton
isPause={isPlaying}
onClick={toggleVideo}
className="absolute block z-10 bottom-20 right-20"
/>
</div>
)}
</AnimateInView>
)}
</Container>
Expand Down
6 changes: 6 additions & 0 deletions components/Video/MutedVideoLoop.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { forwardRef } from 'react';
import { Video, type VideoProps } from './Video';

export const MutedVideoLoop = forwardRef<HTMLVideoElement, VideoProps>((props, ref) => (
<Video ref={ref} role="presentation" muted autoPlay loop playsInline {...props} />
));
2 changes: 1 addition & 1 deletion components/Video/VideoButton.styles.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { cnb } from 'cnbuilder';

export const root = 'group z-10 size-36 md:size-44 bg-black-true/40 border-2 rounded-full border-black-20/50 transition-colors hocus-visible:bg-cardinal-red hocus-visible:border-white hocus-visible:text-white shrink-0';
export const root = 'group z-10 size-36 md:size-44 bg-black-true/40 border-2 rounded-full border-black-10/60 transition-colors hocus-visible:bg-cardinal-red hocus-visible:border-white hocus-visible:text-white shrink-0';
export const icon = (isPause: boolean) => cnb(
'text-black-20 size-16 md:size-20 group-hocus-within:scale-110 group-hocus-within:text-white',
!isPause && 'ml-1',
Expand Down
1 change: 1 addition & 0 deletions components/Video/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './MutedVideoLoop';
export * from './Video';
export * from './VideoButton';

0 comments on commit cb6b954

Please sign in to comment.