Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add main page background videos #42

Merged
merged 4 commits into from
Aug 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.