Skip to content

Commit

Permalink
add main page background videos #18
Browse files Browse the repository at this point in the history
  • Loading branch information
ukorvl committed Aug 3, 2023
1 parent 5cc6fa3 commit 3950dd6
Show file tree
Hide file tree
Showing 21 changed files with 184 additions and 45 deletions.
2 changes: 1 addition & 1 deletion app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const bodyCn = clsx(
'bg-fixed',
);

const mainCn = clsx('flex', 'flex-col', 'items-center', 'justify-center', 'p-24');
const mainCn = clsx('flex', 'flex-col', 'items-center', 'justify-center', 'p-24', 'grow');

/**
* @param {{children}} props Props.
Expand Down
43 changes: 39 additions & 4 deletions app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,47 @@
import Title from '@/components/Title/Title';
import clsx from 'clsx';
import Link from 'next/link';
import AppearInViewport from '@/components/AppearInViewport/AppearInViewport';
import MainPageVideoPlayer from '@/components/MainPageVideoPlayer/MainPageVideoPlayer';

const containerCn = clsx('relative', 'z-10', 'flex', 'flex-col', 'items-center', 'grow');
const titleCn = clsx('font-light', 'text-8xl');
const subtitleCn = clsx('text-1.5xl', 'font-light', 'tracking-widest', 'text-center');
const titleContainerCn = clsx('font-sans', 'select-none');
const exploreCn = clsx('text-center', 'text-2xl', 'mt-auto');
const exploreLinkCn = clsx('tracking-wider', 'animated-link');

const exploreLinkVariants = {
visible: {opacity: 1, y: 0, scale: 1},
hidden: {opacity: 0, y: '2rem', scale: 0.9},
};

/**
* @returns React component.
*/
export default function Home() {
return (
<div className="container">
<Title />
</div>
<>
<div className={containerCn}>
<AppearInViewport className={titleContainerCn}>
<h1 className={titleCn}>
SALSA<span className="font-extrabold">VIVA</span>
</h1>
<h6 className={subtitleCn}>SOCIAL DANCE SCHOOL</h6>
</AppearInViewport>
<AppearInViewport
transition={{delay: 1.2}}
className={exploreCn}
variants={exploreLinkVariants}
>
<Link
href="/about"
className={exploreLinkCn}
>
EXPLORE
</Link>
</AppearInViewport>
</div>
<MainPageVideoPlayer />
</>
);
}
1 change: 1 addition & 0 deletions app/template.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export default function Template({children}: {children: React.ReactNode}) {
<SocialIcons />
<AnimatePresence mode="wait">
<m.div
className="h-full grow flex"
initial={{x: 300, opacity: 0}}
animate={{x: 0, opacity: 1}}
exit={{x: 300, opacity: 0}}
Expand Down
8 changes: 4 additions & 4 deletions components/AppearInViewport/AppearInViewport.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {ReactNode, forwardRef} from 'react';
// eslint-disable-next-line jsdoc/require-jsdoc
type SafeHTMLMotionProps = Omit<
HTMLMotionProps<'div'>,
'variants' | 'initial' | 'whileInView' | 'viewport' | 'ref'
'initial' | 'whileInView' | 'viewport' | 'ref'
>;

/**
Expand All @@ -16,7 +16,7 @@ type AppearOnScreenProps = {
children: ReactNode;
} & SafeHTMLMotionProps;

const variants: Variants = {
const defaultVariants: Variants = {
visible: {opacity: 1},
hidden: {opacity: 0},
};
Expand All @@ -28,7 +28,7 @@ const defaultTransition = {duration: 0.5, delay: 0.3};
* @returns React component.
*/
const AppearInViewport = forwardRef<HTMLDivElement, AppearOnScreenProps>(function AppearInViewport(
{children, transition, ...rest},
{children, variants, transition, ...rest},
ref,
) {
return (
Expand All @@ -37,7 +37,7 @@ const AppearInViewport = forwardRef<HTMLDivElement, AppearOnScreenProps>(functio
whileInView="visible"
viewport={{once: true}}
transition={{...defaultTransition, ...transition}}
variants={variants}
variants={variants ?? defaultVariants}
ref={ref}
{...rest}
>
Expand Down
2 changes: 1 addition & 1 deletion components/Loader/Loader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ type LoaderProps = {
*/
export default function Loader({size = 'md', grow = false}: LoaderProps) {
const containerClassName = clsx(
'flex justify-content-center align-items-center p-4',
'absolute flex justify-content-center align-items-center p-4',
grow && 'min-h-screen w-full',
);

Expand Down
35 changes: 35 additions & 0 deletions components/MainPageVideoPlayer/MainPageVideoPlayer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
'use client';

import clsx from 'clsx';
import {AnimatePresence} from 'framer-motion';
import {useState} from 'react';
import bgVideoConfig from './bgVideosConfig';
import VideoBackground from './VideoBackground';

const containerCn = clsx('fixed', 'top-0', 'left-0', 'w-full', 'h-full');

/**
* @returns React component.
*/
export default function MainPageVideoPlayer() {
const [vidIndex, setVidIndex] = useState(0);
// eslint-disable-next-line jsdoc/require-jsdoc
const incrementVidIndex = () =>
vidIndex < bgVideoConfig.length - 1 ? setVidIndex(vidIndex + 1) : setVidIndex(0);
const {src, type} = bgVideoConfig[vidIndex];

return (
<div className={containerCn}>
<AnimatePresence mode="wait">
{
<VideoBackground
key={src}
src={src}
type={type}
onBeforeEnded={incrementVidIndex}
/>
}
</AnimatePresence>
</div>
);
}
52 changes: 52 additions & 0 deletions components/MainPageVideoPlayer/VideoBackground.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import clsx from 'clsx';
import {m} from 'framer-motion';
import {usePageVisibility} from 'react-page-visibility';
import {useEffect} from 'react';
import useVideoBeforeEnd from './useVideoBeforeEnd';

/**
* Props.
*/
type VideoBackgroundProps = {
src: string;
type: string;
onBeforeEnded: () => void;
};

const videoCn = clsx('w-full', 'h-full', 'object-cover');

/**
* @param {VideoBackgroundProps} props Props.
* @returns React component.
*/
export default function VideoBackground({src, type, onBeforeEnded}: VideoBackgroundProps) {
const [ref] = useVideoBeforeEnd(onBeforeEnded, 1.5);
const isVisible = usePageVisibility();

useEffect(() => {
if (isVisible) {
ref.current?.play();
} else {
ref.current?.pause();
}
}, [isVisible, ref]);

return (
<m.video
muted
autoPlay
className={videoCn}
initial={{opacity: 0, scale: 1.5}}
animate={{opacity: 1, scale: 1}}
exit={{opacity: 0, scale: 1.3}}
transition={{duration: 0.75, type: 'easeOut'}}
ref={ref}
>
<source
src={src}
type={type}
/>
<track kind="captions" />
</m.video>
);
}
12 changes: 12 additions & 0 deletions components/MainPageVideoPlayer/bgVideosConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* Config for the background videos.
*/
const bgVideoConfig = [
{src: '/videos/main.0.mp4', type: 'video/mp4'},
{src: '/videos/main.1.mp4', type: 'video/mp4'},
{src: '/videos/main.2.mp4', type: 'video/mp4'},
{src: '/videos/main.3.mp4', type: 'video/mp4'},
{src: '/videos/main.4.mp4', type: 'video/mp4'},
] as const;

export default bgVideoConfig;
30 changes: 30 additions & 0 deletions components/MainPageVideoPlayer/useVideoBeforeEnd.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {useEffect, useRef} from 'react';

/**
* Run callback when video is about to end.
* @param onBeforeEnd Callback to run when video is about to end.
* @param timeBeforeEnd Time before end in seconds.
* @returns Ref to the video element.
*/
export default function useVideoBeforeEnd(onBeforeEnd: () => void, timeBeforeEnd: number) {
const ref = useRef<HTMLVideoElement>(null);

useEffect(() => {
const video = ref.current!;
// eslint-disable-next-line jsdoc/require-jsdoc
const handleTimeUpdate = () => {
if (video.currentTime > video.duration - timeBeforeEnd) {
onBeforeEnd();
}
};

if (video) {
video.addEventListener('timeupdate', handleTimeUpdate);
return () => {
video.removeEventListener('timeupdate', handleTimeUpdate);
};
}
}, [ref, onBeforeEnd, timeBeforeEnd]);

return [ref];
}
2 changes: 1 addition & 1 deletion components/Menu/MenuDynamicBg.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import useNetworkSpeed from '@/lib/shared/useNetworkSpeed';
import {MenuContext} from './MenuContext';

// eslint-disable-next-line jsdoc/require-jsdoc
const dynamicBgCn = clsx('absolute', 'top-0', 'left-0', 'w-full', 'h-full', 'z-20');
const dynamicBgCn = clsx('absolute', 'top-0', 'left-0', 'w-full', 'h-full', 'z-30');

/**
* @returns React component.
Expand Down
2 changes: 1 addition & 1 deletion components/Menu/MenuItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const variants = {
},
};

const itemCn = clsx('text-5xl', 'm-4', 'text-center', 'z-30', 'relative');
const itemCn = clsx('text-5xl', 'm-4', 'text-center', 'z-40', 'relative');

/**
* @param {MenuItemProps} props Props.
Expand Down
2 changes: 1 addition & 1 deletion components/Menu/MenuList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ const navCn = clsx(
'left-0',
'w-full',
'h-full',
'z-10',
'z-20',
'bg-gradient-to-bl from-accent0 to-alternate',
'flex',
'flex-col',
Expand Down
2 changes: 1 addition & 1 deletion components/Menu/MenuToggle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ type MenuToggleProps = {
className?: string;
};

const containerCn = clsx('absolute', 'z-30');
const containerCn = clsx('absolute', 'z-40');
const btnCn = clsx('rounded-full');

/**
Expand Down
5 changes: 4 additions & 1 deletion components/ScrollToTop/ScrollToTop.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {CSSProperties, useCallback, useState} from 'react';
import {m} from 'framer-motion';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {faCircleArrowUp} from '@fortawesome/free-solid-svg-icons';
import clsx from 'clsx';
import useScroll, {ScrollHandler} from '@/lib/shared/useScroll';
import isPrefersReducedMotion from '@/utils/isPrefersReducedMotion';

Expand All @@ -20,6 +21,8 @@ const variants = {
hidden: {opacity: 0, y: '2rem'},
};

const btnCn = clsx('fixed');

/**
* @param {ScrollToTopButtonProps} props Props.
* @returns React component.
Expand All @@ -38,7 +41,7 @@ export default function ScrollToTopButton({offset = 1000, ...position}: ScrollTo

return (
<m.button
className="fixed"
className={btnCn}
initial="hidden"
animate={visible ? 'visible' : 'hidden'}
variants={variants}
Expand Down
1 change: 1 addition & 0 deletions components/SocialIcons/SocialIcons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const iconsCn = clsx(
'left-4',
'-translate-y-2/4',
'top-2/4',
'z-10',
);
const iconCn = clsx('hover:scale-105 hover:-translate-y-0.5 transition-transform');

Expand Down
30 changes: 0 additions & 30 deletions components/Title/Title.tsx

This file was deleted.

Binary file added public/videos/main.0.mp4
Binary file not shown.
Binary file added public/videos/main.1.mp4
Binary file not shown.
Binary file added public/videos/main.2.mp4
Binary file not shown.
Binary file added public/videos/main.3.mp4
Binary file not shown.
Binary file added public/videos/main.4.mp4
Binary file not shown.

0 comments on commit 3950dd6

Please sign in to comment.