Skip to content

Commit

Permalink
Added text effects in hero page
Browse files Browse the repository at this point in the history
  • Loading branch information
adhirajcs committed Sep 12, 2024
1 parent 841207d commit 2d9fc6a
Show file tree
Hide file tree
Showing 10 changed files with 327 additions and 118 deletions.
20 changes: 20 additions & 0 deletions components.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": true,
"tsx": false,
"tailwind": {
"config": "tailwind.config.js",
"css": "src/app/globals.css",
"baseColor": "slate",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"examples": "@/components/examples",
"blocks": "@/components/blocks"
}
}
155 changes: 43 additions & 112 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@
"lint": "next lint"
},
"dependencies": {
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"framer-motion": "^11.5.4",
"lucide-react": "^0.439.0",
"next": "14.2.8",
"react": "^18",
"react-dom": "^18",
"tailwind-merge": "^2.5.2"
"tailwind-merge": "^2.5.2",
"tailwindcss-animate": "^1.0.7"
},
"devDependencies": {
"postcss": "^8",
Expand Down
Binary file added public/assets/profile-picture.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 0 additions & 1 deletion src/app/globals.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

2 changes: 1 addition & 1 deletion src/app/layout.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import "./globals.css";

export const metadata = {
title: "Portfolio",
title: "Adhiraj's Portfolio",
description: "Adhiraj Saha's Portfolio",
};

Expand Down
46 changes: 46 additions & 0 deletions src/components/Hero.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
"use client";

import { TextGenerateEffect } from "./ui/text-generate-effect";
import { TypewriterEffectSmooth } from "./ui/typewriter-effect";

const Hero = () => {
const words1 = [
{
text: `"Hello There"`,
},
{
text: `I am Adhiraj Saha.`,
className: "text-blue-500 dark:text-blue-500",
},
];
// const words2 = [
// {
// text: `I am Adhiraj Saha`,
// className: "text-blue-500 dark:text-blue-500",
// },
// ];

const words3 = `I'm passionate about web development, blockchain technology, and building cool projects that make a difference. I love experimenting with the latest technologies and crafting experiences that are intuitive and engaging. My skills include ReactJS, Python, and Django.`;

return (
<div className="flex flex-col lg:flex-row items-center justify-evenly relative z-10 text-white px-8 lg:px-56 py-10 h-screen">
{/* Left Side Content */}
<div className="text-left space-y-4">
<TypewriterEffectSmooth words={words1} />
{/* <TypewriterEffectSmooth words={words2} /> */}
<TextGenerateEffect words={words3} className="w-2/3" />
</div>

{/* Right Side Profile Picture */}
<div className="mb-6 lg:mb-0 flex-shrink-0">
<img
src="/assets/profile-picture.png"
alt="Profile Picture"
className="w-32 h-32 lg:w-40 lg:h-40 object-cover rounded-full shadow-lg"
/>
</div>
</div>
);
};

export default Hero;
53 changes: 53 additions & 0 deletions src/components/ui/text-generate-effect.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
"use client";
import { useEffect } from "react";
import { motion, stagger, useAnimate } from "framer-motion";
import { cn } from "@/lib/utils";

export const TextGenerateEffect = ({
words,
className,
filter = true,
duration = 0.5
}) => {
const [scope, animate] = useAnimate();
let wordsArray = words.split(" ");
useEffect(() => {
animate("span", {
opacity: 1,
filter: filter ? "blur(0px)" : "none",
}, {
duration: duration ? duration : 1,
delay: stagger(0.2),
});
}, [scope.current]);

const renderWords = () => {
return (
(<motion.div ref={scope}>
{wordsArray.map((word, idx) => {
return (
(<motion.span
key={word + idx}
className="dark:text-white text-black opacity-0"
style={{
filter: filter ? "blur(10px)" : "none",
}}>
{word}{" "}
</motion.span>)
);
})}
</motion.div>)
);
};

return (
(<div className={cn("font-bold", className)}>
<div className="mt-4">
<div
className=" dark:text-white text-black text-lg leading-snug tracking-wide">
{renderWords()}
</div>
</div>
</div>)
);
};
157 changes: 157 additions & 0 deletions src/components/ui/typewriter-effect.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
"use client";

import { cn } from "@/lib/utils";
import { motion, stagger, useAnimate, useInView } from "framer-motion";
import { useEffect } from "react";

export const TypewriterEffect = ({
words,
className,
cursorClassName
}) => {
// split text inside of words into array of characters
const wordsArray = words.map((word) => {
return {
...word,
text: word.text.split(""),
};
});

const [scope, animate] = useAnimate();
const isInView = useInView(scope);
useEffect(() => {
if (isInView) {
animate("span", {
display: "inline-block",
opacity: 1,
width: "fit-content",
}, {
duration: 0.3,
delay: stagger(0.1),
ease: "easeInOut",
});
}
}, [isInView]);

const renderWords = () => {
return (
(<motion.div ref={scope} className="inline">
{wordsArray.map((word, idx) => {
return (
(<div key={`word-${idx}`} className="inline-block">
{word.text.map((char, index) => (
<motion.span
initial={{}}
key={`char-${index}`}
className={cn(`dark:text-white text-black opacity-0 hidden`, word.className)}>
{char}
</motion.span>
))}
</div>)
);
})}
</motion.div>)
);
};
return (
(<div
className={cn(
"text-base sm:text-xl md:text-3xl lg:text-5xl font-bold text-center",
className
)}>
{renderWords()}
<motion.span
initial={{
opacity: 0,
}}
animate={{
opacity: 1,
}}
transition={{
duration: 0.8,
repeat: Infinity,
repeatType: "reverse",
}}
className={cn(
"inline-block rounded-sm w-[4px] h-4 md:h-6 lg:h-10 bg-blue-500",
cursorClassName
)}></motion.span>
</div>)
);
};

export const TypewriterEffectSmooth = ({
words,
className,
cursorClassName
}) => {
// split text inside of words into array of characters
const wordsArray = words.map((word) => {
return {
...word,
text: word.text.split(""),
};
});
const renderWords = () => {
return (
(<div>
{wordsArray.map((word, idx) => {
return (
(<div key={`word-${idx}`} className="inline-block">
{word.text.map((char, index) => (
<span
key={`char-${index}`}
className={cn(`dark:text-white text-black `, word.className)}>
{char}
</span>
))}
</div>)
);
})}
</div>)
);
};

return (
(<div className={cn("flex space-x-1 my-6", className)}>
<motion.div
className="overflow-hidden pb-2"
initial={{
width: "0%",
}}
whileInView={{
width: "fit-content",
}}
transition={{
duration: 2,
ease: "linear",
delay: 1,
}}>
<div
className="text-xs sm:text-base md:text-3xl lg:text:3xl xl:text-4xl font-bold"
style={{
whiteSpace: "nowrap",
}}>
{renderWords()}{" "}
</div>{" "}
</motion.div>
<motion.span
initial={{
opacity: 0,
}}
animate={{
opacity: 1,
}}
transition={{
duration: 0.8,

repeat: Infinity,
repeatType: "reverse",
}}
className={cn(
"block rounded-sm w-[4px] h-4 sm:h-6 md:h-10 xl:h-12 bg-blue-500",
cursorClassName
)}></motion.span>
</div>)
);
};
6 changes: 3 additions & 3 deletions src/lib/utils.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { clsx } from "clsx";
import { twMerge } from "tailwind-merge";
import { clsx } from "clsx"
import { twMerge } from "tailwind-merge"

export function cn(...inputs) {
return twMerge(clsx(inputs));
return twMerge(clsx(inputs))
}

0 comments on commit 2d9fc6a

Please sign in to comment.