Skip to content

Commit 91a81cb

Browse files
committed
Allow cinemagraph option for looping video
1 parent e91b611 commit 91a81cb

File tree

11 files changed

+151
-90
lines changed

11 files changed

+151
-90
lines changed

dotcom-rendering/src/components/Card/Card.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -890,9 +890,14 @@ export const Card = ({
890890
<MediaWrapper
891891
mediaSize={mediaSize}
892892
mediaType={media.type}
893+
isCinemagraph={
894+
media.mainMedia?.type === 'LoopVideo'
895+
? !!media.mainMedia.isCinemagraph
896+
: false
897+
}
893898
mediaPositionOnDesktop={mediaPositionOnDesktop}
894899
mediaPositionOnMobile={mediaPositionOnMobile}
895-
hideImageOverlay={media.type === 'slideshow'}
900+
hideMediaOverlay={media.type === 'slideshow'}
896901
padMedia={isMediaCardOrNewsletter && isBetaContainer}
897902
isBetaContainer={isBetaContainer}
898903
isSmallCard={isSmallCard}
@@ -946,6 +951,9 @@ export const Card = ({
946951
uniqueId={uniqueId}
947952
height={media.mainMedia.height}
948953
width={media.mainMedia.width}
954+
isCinemagraph={
955+
!!media.mainMedia.isCinemagraph
956+
}
949957
posterImage={media.mainMedia.image ?? ''}
950958
fallbackImage={media.mainMedia.image ?? ''}
951959
fallbackImageSize={mediaSize}

dotcom-rendering/src/components/Card/components/CardWrapper.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ const baseCardStyles = css`
4242
`;
4343

4444
const hoverStyles = css`
45-
:hover .image-overlay {
45+
:hover .media-overlay {
4646
width: 100%;
4747
height: 100%;
4848
background-color: ${palette('--card-background-hover')};
@@ -53,8 +53,10 @@ const hoverStyles = css`
5353
text-decoration: underline;
5454
}
5555
56-
/** We want to prevent the general hover styles applying when
57-
a click won't result in navigating to the main article */
56+
/**
57+
* We want to prevent the general hover styles applying when
58+
* a click won't result in navigating to the main article
59+
*/
5860
:has(
5961
ul.sublinks:hover,
6062
.loop-video-container:hover,
@@ -64,7 +66,7 @@ const hoverStyles = css`
6466
.card-headline .show-underline {
6567
text-decoration: none;
6668
}
67-
.image-overlay {
69+
.media-overlay {
6870
background-color: transparent;
6971
}
7072
}
@@ -95,7 +97,7 @@ const onwardContentStyles = css`
9597
border-radius: ${space[2]}px;
9698
overflow: hidden;
9799
98-
:hover .image-overlay {
100+
:hover .media-overlay {
99101
border-radius: ${space[2]}px;
100102
}
101103
`;

dotcom-rendering/src/components/Card/components/MediaWrapper.tsx

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { SerializedStyles } from '@emotion/react';
22
import { css } from '@emotion/react';
33
import { between, from, space, until } from '@guardian/source/foundations';
44
import type { CardMediaType } from '../../../types/layout';
5+
import { getZIndex } from '../../../lib/getZIndex';
56

67
const mediaFixedSize = {
78
tiny: 86,
@@ -29,25 +30,29 @@ type Props = {
2930
children: React.ReactNode;
3031
mediaSize: MediaSizeType;
3132
mediaType?: CardMediaType;
33+
isCinemagraph?: boolean;
3234
mediaPositionOnDesktop: MediaPositionType;
3335
mediaPositionOnMobile: MediaPositionType;
3436
/**
3537
* Forces hiding the image overlay added to pictures & slideshows on hover.
3638
* This is to allow hiding the overlay on slideshow carousels where we don't
3739
* want it to be shown whilst retaining it for existing slideshows.
3840
*/
39-
hideImageOverlay?: boolean;
41+
hideMediaOverlay?: boolean;
4042
isBetaContainer: boolean;
4143
isSmallCard: boolean;
4244
padMedia?: boolean;
4345
};
4446

45-
const imageOverlayContainerStyles = css`
47+
const mediaOverlayContainerStyles = css`
4648
position: absolute;
4749
top: 0;
4850
left: 0;
4951
width: 100%;
5052
height: 100%;
53+
z-index: ${getZIndex('mediaOverlay')};
54+
cursor: pointer;
55+
pointer-events: none;
5156
`;
5257

5358
/**
@@ -201,9 +206,10 @@ export const MediaWrapper = ({
201206
children,
202207
mediaSize,
203208
mediaType,
209+
isCinemagraph,
204210
mediaPositionOnDesktop,
205211
mediaPositionOnMobile,
206-
hideImageOverlay,
212+
hideMediaOverlay: hideMediaOverlay,
207213
isBetaContainer,
208214
isSmallCard,
209215
padMedia,
@@ -268,11 +274,13 @@ export const MediaWrapper = ({
268274
<>
269275
{children}
270276
{/* This image overlay is styled when the CardLink is hovered */}
271-
{(mediaType === 'picture' || mediaType === 'slideshow') &&
272-
!hideImageOverlay && (
277+
{(mediaType === 'picture' ||
278+
mediaType === 'slideshow' ||
279+
(mediaType === 'loop-video' && isCinemagraph)) &&
280+
!hideMediaOverlay && (
273281
<div
274282
css={[
275-
imageOverlayContainerStyles,
283+
mediaOverlayContainerStyles,
276284
padMedia &&
277285
mediaPaddingStyles(
278286
mediaPositionOnDesktop,
@@ -282,7 +290,7 @@ export const MediaWrapper = ({
282290
>
283291
{/* This child div is needed as the hover background colour covers the padded
284292
area around the image when the hover styles are applied to the top-level div */}
285-
<div className="image-overlay" />
293+
<div className="media-overlay" />
286294
</div>
287295
)}
288296
</>

dotcom-rendering/src/components/FeatureCard.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ const baseCardStyles = css`
7373
`;
7474

7575
const hoverStyles = css`
76-
:hover .image-overlay {
76+
:hover .media-overlay {
7777
position: absolute;
7878
top: 0;
7979
left: 0;
@@ -94,7 +94,7 @@ const sublinkHoverStyles = css`
9494
.card-headline .show-underline {
9595
text-decoration: none;
9696
}
97-
.image-overlay {
97+
.media-overlay {
9898
background-color: transparent;
9999
}
100100
}
@@ -537,7 +537,7 @@ export const FeatureCard = ({
537537
)}
538538

539539
{/* This image overlay is styled when the CardLink is hovered */}
540-
<div className="image-overlay" />
540+
<div className="media-overlay" />
541541

542542
<div
543543
css={[

dotcom-rendering/src/components/LoopVideo.importable.tsx

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ const videoContainerStyles = css`
3333
position: relative;
3434
`;
3535

36+
const cinemagraphContainerStyles = css`
37+
pointer-events: none;
38+
`;
39+
3640
/**
3741
* Dispatches a custom play audio event so that other videos listening
3842
* for this event will be muted.
@@ -116,6 +120,11 @@ type Props = {
116120
uniqueId: string;
117121
height: number;
118122
width: number;
123+
/**
124+
* All controls on the video are hidden: the video looks like a GIF.
125+
* This includes but may not be limited to: audio icon, play/pause icon, subtitles, progress bar.
126+
*/
127+
isCinemagraph: boolean;
119128
posterImage: string;
120129
fallbackImage: CardPictureProps['mainImage'];
121130
fallbackImageSize: CardPictureProps['imageSize'];
@@ -133,6 +142,7 @@ export const LoopVideo = ({
133142
uniqueId,
134143
height,
135144
width,
145+
isCinemagraph,
136146
posterImage,
137147
fallbackImage,
138148
fallbackImageSize,
@@ -642,8 +652,13 @@ export const LoopVideo = ({
642652
return (
643653
<figure
644654
ref={setNode}
645-
css={videoContainerStyles}
646-
className="loop-video-container"
655+
css={[
656+
videoContainerStyles,
657+
isCinemagraph && cinemagraphContainerStyles,
658+
]}
659+
className={`loop-video-container ${
660+
isCinemagraph ? 'cinemagraph' : ''
661+
}`}
647662
data-component="gu-video-loop"
648663
>
649664
<LoopVideoPlayer
@@ -663,17 +678,20 @@ export const LoopVideo = ({
663678
handleLoadedMetadata={handleLoadedMetadata}
664679
handleLoadedData={handleLoadedData}
665680
handleCanPlay={handleCanPlay}
666-
handlePlayPauseClick={handlePlayPauseClick}
667-
handleAudioClick={handleAudioClick}
668-
handleKeyDown={handleKeyDown}
669-
handlePause={handlePause}
681+
handlePlayPauseClick={
682+
!isCinemagraph ? handlePlayPauseClick : undefined
683+
}
684+
handleAudioClick={!isCinemagraph ? handleAudioClick : undefined}
685+
handleKeyDown={!isCinemagraph ? handleKeyDown : undefined}
686+
handlePause={!isCinemagraph ? handlePause : undefined}
670687
onError={onError}
671-
AudioIcon={hasAudio ? AudioIcon : null}
688+
AudioIcon={hasAudio && !isCinemagraph ? AudioIcon : null}
672689
preloadPartialData={preloadPartialData}
673-
showPlayIcon={showPlayIcon}
674-
subtitleSource={subtitleSource}
675-
subtitleSize={subtitleSize}
690+
showPlayIcon={!isCinemagraph ? showPlayIcon : false}
691+
subtitleSize={!isCinemagraph ? subtitleSize : undefined}
692+
subtitleSource={!isCinemagraph ? subtitleSource : undefined}
676693
activeCue={activeCue}
694+
isCinemagraph={isCinemagraph}
677695
/>
678696
</figure>
679697
);

dotcom-rendering/src/components/LoopVideo.stories.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,14 @@ export const Without5to4Ratio: Story = {
7171
},
7272
};
7373

74+
export const WithCinemagraph: Story = {
75+
name: 'With cinemagraph',
76+
args: {
77+
...Default.args,
78+
isCinemagraph: true,
79+
},
80+
};
81+
7482
export const PausePlay: Story = {
7583
...Default,
7684
name: 'Pause and play interaction',

0 commit comments

Comments
 (0)