improvements on navbar color shift functionality
This commit is contained in:
@@ -1,21 +0,0 @@
|
||||
import Link from 'next/link'
|
||||
import { InlineLogo } from './logo'
|
||||
|
||||
export default function Navbar() {
|
||||
return (
|
||||
<div id="navbar" className="w-full h-auto flex flex-nowrap justify-between px-8 py-4 bg-black text-white">
|
||||
<Link href="/">
|
||||
<InlineLogo />
|
||||
</Link>
|
||||
<Link href="/about">
|
||||
<p>About</p>
|
||||
</Link>
|
||||
<Link href="/projects">
|
||||
<p>Projects</p>
|
||||
</Link>
|
||||
<Link href="/contact">
|
||||
<p>Contact</p>
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
94
components/Navbar/index.tsx
Normal file
94
components/Navbar/index.tsx
Normal file
@@ -0,0 +1,94 @@
|
||||
import Link from 'next/link'
|
||||
import { InlineLogo, useColorShift } from '../logo'
|
||||
import { useEffect, useState } from 'react';
|
||||
import { UseColorShiftReturnType } from '../logo/useColorShift';
|
||||
|
||||
interface HoverState {
|
||||
about: boolean
|
||||
projects: boolean
|
||||
contact: boolean
|
||||
}
|
||||
|
||||
const SHIFT_INTERVAL = 3000;
|
||||
|
||||
export default function Navbar() {
|
||||
const navbarColorShift = useColorShift(SHIFT_INTERVAL);
|
||||
const { shift } = navbarColorShift;
|
||||
|
||||
const [colors, setColors] = useState<Partial<UseColorShiftReturnType>>({
|
||||
firstColor: 'bg-inherit',
|
||||
secondColor: 'bg-inherit',
|
||||
thirdColor: 'bg-inherit',
|
||||
});
|
||||
const [hoverState, setHoverState] = useState<HoverState>({
|
||||
about: false,
|
||||
projects: false,
|
||||
contact: false,
|
||||
})
|
||||
|
||||
function mouseOver(source: keyof HoverState) {
|
||||
const { colorKeys, actualColorReferences, activeIndex } = identifyActiveButton();
|
||||
|
||||
setColors({ ...colors, [colorKeys[activeIndex]]: actualColorReferences[activeIndex] });
|
||||
setHoverState({ ...hoverState, [source]: true })
|
||||
}
|
||||
|
||||
function mouseOut(source: keyof HoverState) {
|
||||
setHoverState({ ...hoverState, [source]: false })
|
||||
setColors({ firstColor: 'bg-inherit', secondColor: 'bg-inherit', thirdColor: 'bg-inherit' });
|
||||
}
|
||||
|
||||
function identifyActiveButton() {
|
||||
const buttonKeys: (keyof HoverState)[] = ['about', 'projects', 'contact'];
|
||||
const { firstColor, secondColor, thirdColor } = navbarColorShift;
|
||||
const colorKeys = ['firstColor', 'secondColor', 'thirdColor'];
|
||||
const actualColorReferences = [firstColor, secondColor, thirdColor];
|
||||
|
||||
const activeButton = buttonKeys.find(key => hoverState[key]);
|
||||
const activeIndex = buttonKeys.indexOf(activeButton as keyof HoverState);
|
||||
|
||||
return { colorKeys, actualColorReferences, activeIndex };
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(shift, SHIFT_INTERVAL);
|
||||
return () => clearInterval(interval);
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
const { colorKeys, actualColorReferences, activeIndex } = identifyActiveButton();
|
||||
|
||||
for (const key in hoverState) {
|
||||
if (hoverState[key as keyof HoverState]) {
|
||||
setColors({
|
||||
...colors,
|
||||
[colorKeys[activeIndex]]: actualColorReferences[activeIndex]
|
||||
});
|
||||
}
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, [shift])
|
||||
|
||||
return (
|
||||
<div id="navbar" className="w-full h-auto flex flex-nowrap items-center justify-between px-8 py-4 bg-black text-white">
|
||||
<Link href="/">
|
||||
<InlineLogo customHookInstance={navbarColorShift} />
|
||||
</Link>
|
||||
|
||||
<Link href="/about" onMouseOver={() => mouseOver('about')} onMouseOut={() => mouseOut('about')} className={`${colors.firstColor} rounded-lg transition-colors ease-out duration-${hoverState.about ? '[5000ms]' : '0'}`}>
|
||||
<p className='text-lg text-white text-opacity-80 hover:text-opacity-100 uppercase border-white border-2 p-2 rounded-lg border-opacity-50 hover:border-opacity-75'>About</p>
|
||||
</Link>
|
||||
|
||||
<Link href="/projects" onMouseOver={() => mouseOver("projects")} onMouseOut={() => mouseOut('projects')} className={`${colors.secondColor} rounded-lg transition-colors ease-out duration-${hoverState.projects ? '[5000ms]' : '0'}`}>
|
||||
<p className='text-lg text-white text-opacity-80 hover:text-opacity-100 hover:border-opacity-75 uppercase border-white border-2 p-2 rounded-lg border-opacity-50'>Projects</p>
|
||||
</Link>
|
||||
|
||||
<Link href="/contact" onMouseOver={() => mouseOver('projects')} onMouseOut={() => mouseOut('projects')} className={`${colors.thirdColor} rounded-lg transition-colors ease-out duration-${hoverState.contact ? '[5000ms]' : '0'}`}>
|
||||
<p className='text-lg text-white text-opacity-80 hover:text-opacity-100 uppercase border-white border-2 p-2 rounded-lg border-opacity-50 hover:border-opacity-75'>Contact</p>
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
import { FC } from "react";
|
||||
import useColorShift, { type ColorListType } from "./useColorShift";
|
||||
import useColorShift, { UseColorShiftReturnType, type ColorListType } from "./useColorShift";
|
||||
import { useRouter } from "next/navigation";
|
||||
export { default as useColorShift } from "./useColorShift";
|
||||
|
||||
@@ -10,33 +10,34 @@ interface LogoProps {
|
||||
shiftInterval?: number,
|
||||
customColorList?: ColorListType[],
|
||||
disableShift?: boolean,
|
||||
customHookInstance?: UseColorShiftReturnType
|
||||
}
|
||||
|
||||
export const StackedLogo: FC<LogoProps> = ({ shiftInterval, customColorList, disableShift = false }) => {
|
||||
export const StackedLogo: FC<LogoProps> = ({ shiftInterval, customColorList, customHookInstance, disableShift = false }) => {
|
||||
const hookProps = [
|
||||
shiftInterval ?? DEFAULT_SHIFT_INTERVAL,
|
||||
disableShift,
|
||||
customColorList,
|
||||
] as const;
|
||||
|
||||
const { firstColor, secondColor, thirdColor, handleHover } = useColorShift(...hookProps);
|
||||
const { firstColor, secondColor, thirdColor, shift } = useColorShift(...hookProps);
|
||||
|
||||
return (
|
||||
<div id="venn-diagram-logo-container" className="flex w-full h-auto justify-center">
|
||||
<div onMouseEnter={handleHover} onMouseOut={handleHover} className={`absolute flex flex-col z-40 items-center justify-center animate-logo-throw-left h-16 w-16 bg-opacity-75 ${firstColor} transition-colors duration-[5000ms] ease rounded-full`}>
|
||||
<div onMouseEnter={shift} onMouseOut={shift} className={`absolute flex flex-col z-40 items-center justify-center animate-logo-throw-left h-16 w-16 bg-opacity-75 ${firstColor} transition-colors duration-[5000ms] ease rounded-full`}>
|
||||
<p className="text-6xl font-bold opacity-100">M</p>
|
||||
</div>
|
||||
<div onMouseEnter={handleHover} onMouseOut={handleHover} className={`absolute flex flex-col items-center justify-center animate-logo-throw-down h-16 w-16 bg-opacity-75 ${secondColor} transition-colors duration-[5000ms] ease rounded-full`}>
|
||||
<div onMouseEnter={shift} onMouseOut={shift} className={`absolute flex flex-col items-center justify-center animate-logo-throw-down h-16 w-16 bg-opacity-75 ${secondColor} transition-colors duration-[5000ms] ease rounded-full`}>
|
||||
<p className="text-6xl font-bold opacity-100 z-50">C</p>
|
||||
</div>
|
||||
<div onMouseEnter={handleHover} onMouseOut={handleHover} className={`absolute flex flex-col items-center justify-center animate-logo-throw-right h-16 w-16 bg-opacity-75 ${thirdColor} transition-colors duration-[5000ms] ease rounded-full`}>
|
||||
<div onMouseEnter={shift} onMouseOut={shift} className={`absolute flex flex-col items-center justify-center animate-logo-throw-right h-16 w-16 bg-opacity-75 ${thirdColor} transition-colors duration-[5000ms] ease rounded-full`}>
|
||||
<p className="text-6xl font-bold opacity-100">D</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export const InlineLogo: FC<LogoProps> = ({ shiftInterval, customColorList, disableShift = false }) => {
|
||||
export const InlineLogo: FC<LogoProps> = ({ shiftInterval, customColorList, customHookInstance, disableShift = false }) => {
|
||||
const router = useRouter();
|
||||
|
||||
const hookProps = [
|
||||
@@ -45,19 +46,35 @@ export const InlineLogo: FC<LogoProps> = ({ shiftInterval, customColorList, disa
|
||||
customColorList,
|
||||
] as const;
|
||||
|
||||
const { firstColor, secondColor, thirdColor, handleHover } = useColorShift(...hookProps);
|
||||
const { firstColor, secondColor, thirdColor, shift } = useColorShift(...hookProps);
|
||||
|
||||
return (
|
||||
if (customHookInstance) return (
|
||||
<button onClick={() => router.push('/')} id="inline-logo-container" className="flex w-auto h-auto justify-center">
|
||||
<div onMouseEnter={handleHover} onMouseOut={handleHover} className={`flex flex-col items-center justify-center h-16 w-16 bg-opacity-75 ${firstColor} transition-colors duration-[5000ms] rounded-full`}>
|
||||
<div className={`flex flex-col items-center justify-center h-16 w-16 bg-opacity-75 ${customHookInstance.firstColor} transition-colors duration-[5000ms] rounded-full`}>
|
||||
<p className="text-2xl font-bold opacity-100">M</p>
|
||||
</div>
|
||||
|
||||
<div onMouseEnter={handleHover} onMouseOut={handleHover} className={`flex flex-col -ml-3 items-center justify-center h-16 w-16 bg-opacity-75 ${secondColor} transition-colors duration-[5000ms] rounded-full`}>
|
||||
<div className={`flex flex-col -ml-3 items-center justify-center h-16 w-16 bg-opacity-75 ${customHookInstance.secondColor} transition-colors duration-[5000ms] rounded-full`}>
|
||||
<p className="text-2xl font-bold opacity-100">C</p>
|
||||
</div>
|
||||
|
||||
<div onMouseEnter={handleHover} onMouseOut={handleHover} className={`flex flex-col -ml-3 items-center justify-center h-16 w-16 bg-opacity-75 ${thirdColor} transition-colors duration-[5000ms] rounded-full`}>
|
||||
<div className={`flex flex-col -ml-3 items-center justify-center h-16 w-16 bg-opacity-75 ${customHookInstance.thirdColor} transition-colors duration-[5000ms] rounded-full`}>
|
||||
<p className="text-2xl font-bold opacity-100">D</p>
|
||||
</div>
|
||||
</button>
|
||||
)
|
||||
|
||||
return (
|
||||
<button onClick={() => router.push('/')} id="inline-logo-container" className="flex w-auto h-auto justify-center">
|
||||
<div className={`flex flex-col items-center justify-center h-16 w-16 bg-opacity-75 ${firstColor} transition-colors duration-[5000ms] rounded-full`}>
|
||||
<p className="text-2xl font-bold opacity-100">M</p>
|
||||
</div>
|
||||
|
||||
<div className={`flex flex-col -ml-3 items-center justify-center h-16 w-16 bg-opacity-75 ${secondColor} transition-colors duration-[5000ms] rounded-full`}>
|
||||
<p className="text-2xl font-bold opacity-100">C</p>
|
||||
</div>
|
||||
|
||||
<div className={`flex flex-col -ml-3 items-center justify-center h-16 w-16 bg-opacity-75 ${thirdColor} transition-colors duration-[5000ms] rounded-full`}>
|
||||
<p className="text-2xl font-bold opacity-100">D</p>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
@@ -1,57 +1,63 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { DefaultColors } from "tailwindcss/types/generated/colors";
|
||||
|
||||
export type ColorListType = (`bg-${keyof DefaultColors}` | `bg-${keyof DefaultColors}-${string}`);
|
||||
export type ColorListType = (`bg-${keyof DefaultColors}` | `bg-${keyof DefaultColors}-${string}` | "");
|
||||
|
||||
const colorList: ColorListType[] = [
|
||||
"bg-purple-400",
|
||||
"bg-purple-700",
|
||||
"bg-sky-400",
|
||||
"bg-sky-700",
|
||||
"bg-blue-400",
|
||||
"bg-pink-400",
|
||||
"bg-pink-700",
|
||||
"bg-purple-500",
|
||||
"bg-purple-800",
|
||||
"bg-sky-500",
|
||||
"bg-sky-800",
|
||||
"bg-blue-500",
|
||||
"bg-pink-500",
|
||||
"bg-pink-800",
|
||||
];
|
||||
|
||||
const useColorShift = (shiftInterval: number, disableShift = false, customColorList?: ColorListType[]): {
|
||||
firstColor: ColorListType | "",
|
||||
secondColor: ColorListType | "",
|
||||
thirdColor: ColorListType | "",
|
||||
handleHover: () => void,
|
||||
} => {
|
||||
if (shiftInterval <= 0) throw new Error("shiftInterval must be greater than 0")
|
||||
export interface UseColorShiftReturnType {
|
||||
firstColor: ColorListType
|
||||
secondColor: ColorListType
|
||||
thirdColor: ColorListType
|
||||
shift: () => { firstColor: ColorListType, secondColor: ColorListType, thirdColor: ColorListType }
|
||||
shiftInterval: number | undefined
|
||||
}
|
||||
|
||||
const useColorShift = (shiftInterval?: number, disableShift = false, customColorList?: ColorListType[]): UseColorShiftReturnType => {
|
||||
if (shiftInterval && shiftInterval <= 0) throw new Error("shiftInterval must be greater than 0")
|
||||
|
||||
const randomColor = () => (customColorList ?? colorList)[Math.floor(Math.random() * colorList.length | 0)];
|
||||
|
||||
const [circleColors, setCircleColors] = useState<{
|
||||
firstColor: ColorListType | "",
|
||||
secondColor: ColorListType | "",
|
||||
thirdColor: ColorListType | "",
|
||||
}>({
|
||||
firstColor: "",
|
||||
secondColor: "",
|
||||
thirdColor: "",
|
||||
firstColor: colorList[0],
|
||||
secondColor: colorList[1],
|
||||
thirdColor: colorList[2],
|
||||
})
|
||||
|
||||
function handleHover() {
|
||||
const firstColor = colorList[Math.floor(Math.random() * colorList.length | 0)]
|
||||
const secondColor = colorList[Math.floor(Math.random() * colorList.length | 0)]
|
||||
const thirdColor = colorList[Math.floor(Math.random() * colorList.length | 0)]
|
||||
function shift() {
|
||||
const firstColor = randomColor();
|
||||
const secondColor = randomColor();
|
||||
const thirdColor = randomColor();
|
||||
|
||||
setCircleColors({ firstColor, secondColor, thirdColor });
|
||||
return { firstColor, secondColor, thirdColor };
|
||||
}
|
||||
|
||||
// perform initial mount of color changing pattern
|
||||
useEffect(disableShift ? handleHover : (() => { return }), []);
|
||||
useEffect(disableShift ? shift : (() => { return }), []);
|
||||
|
||||
// set this function to repeat
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
handleHover();
|
||||
}, shiftInterval);
|
||||
shift();
|
||||
}, shiftInterval ?? 3000);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, [])
|
||||
|
||||
return { ...circleColors, handleHover };
|
||||
return { ...circleColors, shift, shiftInterval };
|
||||
}
|
||||
|
||||
export default useColorShift
|
||||
|
||||
Reference in New Issue
Block a user