Skip to content

Commit de78950

Browse files
committed
feat(): Adding BG and cards
1 parent ec2917c commit de78950

12 files changed

Lines changed: 233 additions & 24 deletions

File tree

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
import React, { useState, useEffect, useRef } from 'react';
2+
3+
import { motion } from 'framer-motion';
4+
import { useCursor } from '../../Cursor';
5+
6+
type Direction = 'TOP' | 'LEFT' | 'BOTTOM' | 'RIGHT';
7+
8+
const HoverBorderGradient = ({ children }: { children: React.ReactNode }) => {
9+
const ref = useRef<HTMLDivElement>(null);
10+
const { setCursorInsets } = useCursor();
11+
12+
const onMouseEnter = () => {
13+
const { top, left, width, height } =
14+
ref.current?.getBoundingClientRect() || {
15+
top: 0,
16+
left: 0,
17+
width: 0,
18+
height: 0,
19+
};
20+
setCursorInsets(undefined);
21+
setTimeout(() => {
22+
setCursorInsets({
23+
height: 0,
24+
width: 0,
25+
top: top + height / 2,
26+
left: left + width / 2,
27+
});
28+
}, 0);
29+
};
30+
31+
const onMouseLeave = () => {
32+
setCursorInsets(undefined);
33+
};
34+
return (
35+
<div
36+
className="m-40 flex justify-center text-center"
37+
ref={ref}
38+
onMouseEnter={onMouseEnter}
39+
onMouseLeave={onMouseLeave}
40+
>
41+
<HoverBorderGradientBase
42+
containerClassName="rounded-full"
43+
as="button"
44+
className="dark:bg-black bg-white text-black dark:text-white flex items-center space-x-2"
45+
>
46+
{children}
47+
</HoverBorderGradientBase>
48+
</div>
49+
);
50+
};
51+
52+
const HoverBorderGradientBase = ({
53+
children,
54+
containerClassName,
55+
className,
56+
as: Tag = 'button',
57+
duration = 1,
58+
clockwise = true,
59+
...props
60+
}: React.PropsWithChildren<
61+
{
62+
as?: React.ElementType;
63+
containerClassName?: string;
64+
className?: string;
65+
duration?: number;
66+
clockwise?: boolean;
67+
} & React.HTMLAttributes<HTMLElement>
68+
>) => {
69+
const [hovered, setHovered] = useState<boolean>(false);
70+
const [direction, setDirection] = useState<Direction>('TOP');
71+
72+
const rotateDirection = (currentDirection: Direction): Direction => {
73+
const directions: Direction[] = ['TOP', 'LEFT', 'BOTTOM', 'RIGHT'];
74+
const currentIndex = directions.indexOf(currentDirection);
75+
const nextIndex = clockwise
76+
? (currentIndex - 1 + directions.length) % directions.length
77+
: (currentIndex + 1) % directions.length;
78+
return directions[nextIndex];
79+
};
80+
81+
const movingMap: Record<Direction, string> = {
82+
TOP: 'radial-gradient(20.7% 50% at 50% 0%, hsl(0, 0%, 100%) 0%, rgba(255, 255, 255, 0) 100%)',
83+
LEFT: 'radial-gradient(16.6% 43.1% at 0% 50%, hsl(0, 0%, 100%) 0%, rgba(255, 255, 255, 0) 100%)',
84+
BOTTOM:
85+
'radial-gradient(20.7% 50% at 50% 100%, hsl(0, 0%, 100%) 0%, rgba(255, 255, 255, 0) 100%)',
86+
RIGHT:
87+
'radial-gradient(16.2% 41.199999999999996% at 100% 50%, hsl(0, 0%, 100%) 0%, rgba(255, 255, 255, 0) 100%)',
88+
};
89+
90+
const highlight =
91+
'radial-gradient(75% 181.15942028985506% at 50% 50%, #3275F8 0%, rgba(255, 255, 255, 0) 100%)';
92+
93+
useEffect(() => {
94+
if (!hovered) {
95+
const interval = setInterval(() => {
96+
setDirection((prevState) => rotateDirection(prevState));
97+
}, duration * 1000);
98+
return () => clearInterval(interval);
99+
}
100+
// eslint-disable-next-line react-hooks/exhaustive-deps
101+
}, [hovered]);
102+
return (
103+
<Tag
104+
onMouseEnter={(event: React.MouseEvent<HTMLDivElement>) => {
105+
setHovered(true);
106+
}}
107+
onMouseLeave={() => setHovered(false)}
108+
className={
109+
'relative flex rounded-full border border-gray-900 content-center bg-black/20 hover:bg-black/10 transition duration-500 dark:bg-white/20 items-center flex-col flex-nowrap gap-10 h-min justify-center overflow-visible p-px decoration-clone w-fit'
110+
}
111+
{...props}
112+
>
113+
<div
114+
className={
115+
'w-auto text-white z-10 bg-black px-4 py-2 rounded-[inherit]'
116+
}
117+
>
118+
{children}
119+
</div>
120+
<motion.div
121+
className={
122+
'flex-none inset-0 overflow-hidden absolute z-0 rounded-[inherit]'
123+
}
124+
style={{
125+
filter: 'blur(2px)',
126+
position: 'absolute',
127+
width: '100%',
128+
height: '100%',
129+
}}
130+
initial={{ background: movingMap[direction] }}
131+
animate={{
132+
background: hovered
133+
? [movingMap[direction], highlight]
134+
: movingMap[direction],
135+
}}
136+
transition={{ ease: 'linear', duration: duration ?? 1 }}
137+
/>
138+
<div className="bg-black absolute z-1 flex-none inset-[2px] rounded-[100px]" />
139+
</Tag>
140+
);
141+
};
142+
143+
export default HoverBorderGradient;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default as HoverBorderGradient } from './HoverBorderGradient';

src/components/Button/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export * from './LinkButton';
2+
export * from './HoverBorderGradient';
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { Box } from '@chakra-ui/react';
2+
3+
const COLOR_MAP = ['#64748b', '#ffffff', '#00ffff', '#ff00ff'];
4+
5+
const MeteorsEffect = ({ number = 20 }: { number?: number }) => {
6+
const meteors = new Array(number).fill(true);
7+
return (
8+
<Box className="absolute top-0 left-0 w-full h-full" overflowX={'hidden'}>
9+
<style>{`
10+
@keyframes meteor {
11+
0% {
12+
transform: rotate(215deg) translateX(0px);
13+
opacity: 1;
14+
}
15+
70% {
16+
opacity: 1;
17+
}
18+
100% {
19+
transform: rotate(215deg) translateX(-1000px);
20+
opacity: 0;
21+
}
22+
}`}</style>
23+
{meteors.map((_el, idx) => (
24+
<span
25+
key={'meteor' + idx}
26+
className={
27+
'animate-meteor-effect absolute top-1/2 left-1/2 h-0.5 w-0.5 rounded-[9999px] bg-blue-500 shadow-[0_0_0_1px_#ffffff10] rotate-[215deg]' +
28+
" before:content-[''] before:absolute before:top-1/2 before:transform before:-translate-y-[50%] before:w-[50px] before:h-[1px] before:bg-gradient-to-r before:from-[#716d7f] before:to-transparent"
29+
}
30+
style={{
31+
backgroundColor: COLOR_MAP[idx % 4],
32+
animation: 'meteor 5s linear infinite',
33+
top: 0,
34+
left: Math.floor(Math.random() * 100) + '%',
35+
animationDelay: Math.random() * (0.8 - 0.2) + 0.2 + 's',
36+
animationDuration: Math.floor(Math.random() * (10 - 2) + 2) + 's',
37+
}}
38+
></span>
39+
))}
40+
</Box>
41+
);
42+
};
43+
44+
export default MeteorsEffect;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default as MeteorsEffect } from './MeteorEffect';

src/components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ export * from './Button';
22
export * from './CardComponent';
33
export * from './Cursor';
44
export * from './Lights';
5+
export * from './MeteorEffect';
56
export * from './Theme';
67
export * from './TitleBox';
78
export * from './3dPin';

src/index.css

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,6 @@
1313
box-shadow: inset 0 0 1px rgb(255, 255, 255);
1414
border-radius: 5px;
1515
}
16-
17-
::-webkit-scrollbar-track:horizontal {
18-
box-shadow: inset 0 0 1px rgb(255, 255, 255);
19-
border-radius: 5px;
20-
}
2116
/* Handle */
2217
::-webkit-scrollbar-thumb {
2318
background: violet;

src/screens/mainFlow/MainScreen.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Text } from '@chakra-ui/react';
2-
import { TitleBoxContainer, Lights } from '@components';
2+
import { TitleBoxContainer, Lights, MeteorsEffect } from '@components';
33
import { Footer, NavigationBar, SocialNavigation } from './components';
44
import { RobotScene } from './scene';
55
import { useState } from 'react';
@@ -13,12 +13,14 @@ const MainScreen = () => {
1313
<TitleBoxContainer
1414
title={'Amit Raikwar | Portfolio'}
1515
bg={'black'}
16-
minHeight={'100vh'}
1716
minW={'99vw'}
1817
display={'flex'}
1918
flexDir={'column'}
19+
alignItems={'center'}
20+
overflowX={'hidden'}
2021
>
21-
{/* <RobotScene type={characterType} /> */}
22+
<MeteorsEffect number={30} />
23+
<RobotScene type={characterType} />
2224
<Lights />
2325
<NavigationBar
2426
handleCharacterClick={(type: 'adam' | 'lieutenant') => {

src/screens/mainFlow/components/NavigationBar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const NavigationBar = ({
1717
return (
1818
<HStack
1919
position={'fixed'}
20-
top={10}
20+
marginY={8}
2121
w={'100%'}
2222
px={10}
2323
justifyContent={'space-between'}

src/screens/mainFlow/contents/Contents.tsx

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
import { Box, Text } from '@chakra-ui/react';
2-
import { Card, ThreeDPin } from '@components';
1+
import { Box, HStack, Text } from '@chakra-ui/react';
2+
import { Card, HoverBorderGradient, ThreeDPin } from '@components';
3+
import HeroText from './HeroText';
34

45
const Contents = () => {
56
return (
67
<Box
8+
marginTop={10}
79
minH={'100vh'}
810
display={'flex'}
911
bottom={0}
@@ -15,14 +17,32 @@ const Contents = () => {
1517
bg={'black'}
1618
color={'white'}
1719
>
18-
<ThreeDPin title="Hourcoding.com" href="https://hourcoding.com">
20+
<HeroText />
21+
<HStack>
1922
<Card
2023
centerText="Hourcoding"
2124
titleText="Hourcoding.com"
2225
subtitleText="A blog about coding and programming"
2326
chipText="React, Next.js, TypeScript, Chakra UI"
2427
/>
25-
</ThreeDPin>
28+
<ThreeDPin title="Hourcoding.com" href="https://hourcoding.com">
29+
<Card
30+
centerText="Hourcoding"
31+
titleText="Hourcoding.com"
32+
subtitleText="A blog about coding and programming"
33+
chipText="React, Next.js, TypeScript, Chakra UI"
34+
/>
35+
</ThreeDPin>
36+
</HStack>
37+
<HoverBorderGradient>
38+
<Text fontSize={'md'}>
39+
© {new Date().getFullYear()}{' '}
40+
<a href={'https://amitraikwar.dev'} color="violet">
41+
Amit Raikwar
42+
</a>{' '}
43+
| All rights reserved
44+
</Text>
45+
</HoverBorderGradient>
2646
</Box>
2747
);
2848
};

0 commit comments

Comments
 (0)