Skip to content

Commit b4299b4

Browse files
feat: add page transition and mouse interaction
1 parent e83d0a4 commit b4299b4

File tree

16 files changed

+227
-45
lines changed

16 files changed

+227
-45
lines changed

.eslintignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
build/
2+
dist/

.eslintrc.cjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ module.exports = {
3131
{ vars: "all", args: "after-used", ignoreRestSiblings: false },
3232
],
3333
"react/jsx-pascal-case": ["error", { allowAllCaps: true }],
34+
"jsx-a11y/mouse-events-have-key-events": "off",
3435
"no-console": "error",
3536
"import/no-unresolved": "off",
3637
"import/extensions": "off",

index.html

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
11
<!doctype html>
22
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>CodeX</title>
7+
</head>
38

4-
<head>
5-
<meta charset="UTF-8" />
6-
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
7-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
8-
<title>CodeX</title>
9-
</head>
10-
11-
<body>
12-
<div id="root"></div>
13-
<script type="module" src="/src/main.jsx"></script>
14-
</body>
15-
16-
</html>
9+
<body>
10+
<div id="root"></div>
11+
<script type="module" src="/src/main.jsx"></script>
12+
</body>
13+
</html>

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,14 @@
1818
"@fontsource/league-gothic": "^5.0.18",
1919
"@fontsource/poppins": "^5.0.8",
2020
"@vitejs/plugin-react": "^4.2.1",
21+
"lodash": "^4.17.21",
2122
"prop-types": "^15.8.1",
2223
"react": "^18.2.0",
2324
"react-dom": "^18.2.0",
2425
"react-router-dom": "^6.22.1",
2526
"react-toastify": "^10.0.4",
26-
"vite": "^5.1.0"
27+
"vite": "^5.1.0",
28+
"framer-motion": "^11.0.20"
2729
},
2830
"devDependencies": {
2931
"@commitlint/cli": "^18.6.1",

src/App.jsx

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1+
import { AnimatePresence } from "framer-motion";
12
import { HashRouter as Router, Routes, Route } from "react-router-dom";
23
import { ToastContainer } from "react-toastify";
34
import routes from "@/routes/index";
45
import Navbar from "@/components/Navbar/index";
6+
import Cursor from "./components/Cursor";
7+
import CursorVariantProvider from "@/context/CursorVariantProvider";
58

69
const navLinks = [
710
{ name: "About Us", path: "/about-us" },
@@ -13,16 +16,25 @@ const navLinks = [
1316

1417
function App() {
1518
return (
16-
<Router>
17-
<Navbar links={navLinks} />
19+
<CursorVariantProvider>
20+
<AnimatePresence>
21+
<Router>
22+
<Navbar links={navLinks} />
23+
<Cursor />
1824

19-
<ToastContainer />
20-
<Routes>
21-
{routes.map((route) => (
22-
<Route path={route.path} element={route.render} key={route.lable} />
23-
))}
24-
</Routes>
25-
</Router>
25+
<ToastContainer />
26+
<Routes>
27+
{routes.map((route) => (
28+
<Route
29+
path={route.path}
30+
element={route.render}
31+
key={route.label}
32+
/>
33+
))}
34+
</Routes>
35+
</Router>
36+
</AnimatePresence>
37+
</CursorVariantProvider>
2638
);
2739
}
2840

src/assets/styles/index.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
padding: 0;
1212
box-sizing: border-box;
1313
font-family: "poppins", "sans-serif";
14+
/* cursor: none; */
1415
}
1516

1617
body,

src/components/Cursor/index.jsx

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { useState, useEffect, useRef, useContext } from "react";
2+
import { debounce } from "lodash";
3+
import { motion } from "framer-motion";
4+
import { CursorVariantContext } from "@/context/CursorVariantProvider";
5+
6+
function Cursor() {
7+
const { cursorVariant } = useContext(CursorVariantContext);
8+
9+
const [mousePosition, setMousePosition] = useState({
10+
x: 0,
11+
y: 0,
12+
});
13+
14+
const cursorRef = useRef(null);
15+
16+
const debouncedMouseMove = debounce((e) => {
17+
setMousePosition({
18+
x: e.clientX,
19+
y: e.clientY,
20+
});
21+
}, 4);
22+
23+
function mouseMove(e) {
24+
debouncedMouseMove(e);
25+
}
26+
27+
useEffect(() => {
28+
if (cursorRef.current) {
29+
// cursorRef.current.style.left = `${mousePosition.x}px`;
30+
// cursorRef.current.style.top = `${mousePosition.y}px`;
31+
cursorRef.current.animate(
32+
{
33+
left: `${mousePosition.x}px`,
34+
top: `${mousePosition.y}px`,
35+
},
36+
{ duration: 400, fill: "forwards" },
37+
);
38+
}
39+
window.addEventListener("mousemove", mouseMove);
40+
41+
return () => {
42+
window.removeEventListener("mousemove", mouseMove);
43+
};
44+
}, [mousePosition]);
45+
46+
const variants = {
47+
default: {
48+
mixBlendMode: "normal",
49+
backgroundColor: "#ffffff",
50+
},
51+
text: {
52+
height: 150,
53+
width: 150,
54+
backgroundColor: "#E76941",
55+
mixBlendMode: "difference",
56+
},
57+
none: {
58+
height: 0,
59+
width: 0,
60+
},
61+
};
62+
63+
return (
64+
<motion.div
65+
ref={cursorRef}
66+
className="h-6 w-6 rounded-full border-2 border-background-dark fixed top-0 left-0 -translate-x-1/2 -translate-y-1/2 pointer-events-none z-50"
67+
variants={variants}
68+
animate={cursorVariant}
69+
/>
70+
);
71+
}
72+
73+
export default Cursor;

src/components/Heading/index.jsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,25 @@
1+
import { useContext } from "react";
12
import PropTypes from "prop-types";
3+
import { CursorVariantContext } from "../../context/CursorVariantProvider";
24

35
function Heading({ text, className, bgTextStyle, frontTextStyle }) {
6+
const { setCursorVariantText, setCursorVariantDefault } =
7+
useContext(CursorVariantContext);
8+
49
return (
510
<div
611
className={`relative text-center font-black uppercase tracking-tighter ${className}`}
712
>
813
<div
14+
onMouseOver={setCursorVariantText}
15+
onMouseOut={setCursorVariantDefault}
916
className={`${bgTextStyle} xs:text-4xl sm:text-6xl lg:text-8xl text-outlined text-transparent`}
1017
>
1118
{text}
1219
</div>
1320
<div
21+
onMouseOver={setCursorVariantText}
22+
onMouseOut={setCursorVariantDefault}
1423
className={`${frontTextStyle} xs:text-2xl sm:text-4xl lg:text-6xl absolute w-full leading-none bottom-[-20%]`}
1524
>
1625
{text}

src/components/Navbar/index.jsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,29 @@
1-
import { useState } from "react";
1+
import { useState, useContext } from "react";
22
import { Link, useLocation } from "react-router-dom";
33
import PropTypes from "prop-types";
44
import Logo from "@/assets/images/logo.svg";
55
import closeIcon from "@/assets/images/close.svg";
66
import menuIcon from "@/assets/images/menu.svg";
7+
import { CursorVariantContext } from "@/context/CursorVariantProvider";
78

89
function Navbar({ links }) {
910
const [isOpen, setIsOpen] = useState(false);
1011
const location = useLocation();
1112

13+
const { setCursorVariantNone, setCursorVariantDefault } =
14+
useContext(CursorVariantContext);
15+
1216
return (
1317
<nav
1418
className={`shadow-md w-full flex xs:flex-col md:flex-row ${isOpen ? "xs:h-screen" : ""} md:h-full justify-between items-center px-6 bg-secondary-dark`}
1519
>
1620
<div className="flex flex-row justify-between xs:w-full md:w-auto items-center">
17-
<Link to="/" className="cursor-pointer">
21+
<Link
22+
to="/"
23+
className="cursor-pointer"
24+
onMouseEnter={setCursorVariantNone}
25+
onMouseLeave={setCursorVariantDefault}
26+
>
1827
<img src={Logo} className="w-[10rem]" alt="logo" />
1928
</Link>
2029
<button
@@ -34,6 +43,8 @@ function Navbar({ links }) {
3443
md:flex pl-0 justify-start md:justify-center md:items-center md:pb-0 md:z-auto left-0 xs:justify-center xs:gap-y-12
3544
${isOpen ? "flex flex-col grow" : "hidden"}
3645
`}
46+
onMouseEnter={setCursorVariantNone}
47+
onMouseLeave={setCursorVariantDefault}
3748
>
3849
{links.map((link) => (
3950
<li
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { motion } from "framer-motion";
2+
import PropTypes from "prop-types";
3+
4+
function PageTransition({ children }) {
5+
return (
6+
<>
7+
<motion.div
8+
initial={{ y: "-100%" }}
9+
animate={{ y: ["-100%", "0%", "-100%"] }}
10+
transition={{ duration: 0.8, ease: "easeOut" }}
11+
className="z-50 absolute h-screen w-full bg-primary top-0 left-0"
12+
/>
13+
14+
<motion.div
15+
initial={{ opacity: 0 }}
16+
animate={{ opacity: 1 }}
17+
transition={{ delay: 0.7 }}
18+
>
19+
{children}
20+
</motion.div>
21+
</>
22+
);
23+
}
24+
25+
PageTransition.propTypes = {
26+
children: PropTypes.node.isRequired,
27+
};
28+
29+
export default PageTransition;

0 commit comments

Comments
 (0)