Added Skills

This commit is contained in:
IM23a-spirgif
2025-03-27 11:08:16 +01:00
parent 650d8e5594
commit 1bd462ce30
4 changed files with 115 additions and 63 deletions
+43
View File
@@ -8,6 +8,7 @@
"name": "portfolio",
"version": "0.1.0",
"dependencies": {
"framer-motion": "^12.6.2",
"next": "15.2.0",
"react": "^19.0.0",
"react-dom": "^19.0.0"
@@ -2745,6 +2746,33 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/framer-motion": {
"version": "12.6.2",
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.6.2.tgz",
"integrity": "sha512-7LgPRlPs5aG8UxeZiMCMZz8firC53+2+9TnWV22tuSi38D3IFRxHRUqOREKckAkt6ztX+Dn6weLcatQilJTMcg==",
"license": "MIT",
"dependencies": {
"motion-dom": "^12.6.1",
"motion-utils": "^12.5.0",
"tslib": "^2.4.0"
},
"peerDependencies": {
"@emotion/is-prop-valid": "*",
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@emotion/is-prop-valid": {
"optional": true
},
"react": {
"optional": true
},
"react-dom": {
"optional": true
}
}
},
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
@@ -3955,6 +3983,21 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/motion-dom": {
"version": "12.6.1",
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.6.1.tgz",
"integrity": "sha512-8XVsriTUEVOepoIDgE/LDGdg7qaKXWdt+wQA/8z0p8YzJDLYL8gbimZ3YkCLlj7bB2i/4UBD/g+VO7y9ZY0zHQ==",
"license": "MIT",
"dependencies": {
"motion-utils": "^12.5.0"
}
},
"node_modules/motion-utils": {
"version": "12.5.0",
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.5.0.tgz",
"integrity": "sha512-+hFFzvimn0sBMP9iPxBa9OtRX35ZQ3py0UHnb8U29VD+d8lQ8zH3dTygJWqK7av2v6yhg7scj9iZuvTS0f4+SA==",
"license": "MIT"
},
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+5 -4
View File
@@ -9,15 +9,16 @@
"lint": "next lint"
},
"dependencies": {
"framer-motion": "^12.6.2",
"next": "15.2.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"next": "15.2.0"
"react-dom": "^19.0.0"
},
"devDependencies": {
"@eslint/eslintrc": "^3",
"@tailwindcss/postcss": "^4",
"tailwindcss": "^4",
"eslint": "^9",
"eslint-config-next": "15.2.0",
"@eslint/eslintrc": "^3"
"tailwindcss": "^4"
}
}
+55 -59
View File
@@ -9,85 +9,76 @@ export default function About() {
content: "I discovered my passion for technology by experimenting with Scratch and creating some simple Games."
},
{
date: "2022",
title: "Exploring Web Development",
content: "Started creating my first website and got into HTML and CSS."
date: "2020",
title: "Exploring Web & Scripting",
content: "Started creating small websites and got into JavaScript, CSS, and working with various scripting languages."
},
{
date: "2023",
title: "Diving into Informatics",
content: "Started with Python and basic exploration of 'real' programming."
date: "2022",
title: "Diving into Game Development",
content: "Tried Unity and C#, building small 2D and 3D prototypes to expand my skill set."
},
{
date: "2024",
title: "Full Stack & Beyond",
content: "Ventured into backend (SQL, Python) and frontend using Javascript"
content: "Ventured into backend (SQL, Python) and continued refining my frontend abilities, always exploring new challenges."
}
];
const skills = [
{ name: "JavaScript", level: 80, waveSpeed: "2s" },
{ name: "Python", level: 75, waveSpeed: "2.5s" },
{ name: "C#", level: 50, waveSpeed: "3s" },
{ name: "SQL", level: 85, waveSpeed: "2.2s" },
{ name: "HTML & CSS", level: 70, waveSpeed: "2.7s" },
];
const sectionRefs = timelineData.map(() => useRef(null));
const aboutMeRef = useRef(null);
const [currentSection, setCurrentSection] = useState(0);
const [timelineFinished, setTimelineFinished] = useState(false);
const [isManualScroll, setIsManualScroll] = useState(false); // Track user scrolling
const scrollContainerRef = useRef(null);
const [currentSection, setCurrentSection] = useState(0);
const [timelineFinished, setTimelineFinished] = useState(false);
// Scroll to 'About Me' title and reset timeline on page load
useEffect(() => {
setCurrentSection(0); // Reset timeline
setTimelineFinished(false);
// Ensure 'About Me' is visible on reload
setTimeout(() => {
aboutMeRef.current?.scrollIntoView({ behavior: "smooth", block: "start" });
aboutMeRef.current?.scrollIntoView({behavior: "smooth", block: "start"});
}, 100);
}, []);
// Handle button navigation
const handleButtonClick = (direction) => {
setIsManualScroll(false); // Ensure programmatic scrolling happens
setCurrentSection((prev) => {
const newSection = Math.max(0, Math.min(prev + direction, timelineData.length - 1));
if (newSection === timelineData.length - 1) {
setTimelineFinished(true);
} else {
setTimelineFinished(false);
}
sectionRefs[newSection]?.current?.scrollIntoView({behavior: "smooth", inline: "start"});
return newSection;
});
};
// Scroll to current section (only if not manually scrolling)
useEffect(() => {
if (!isManualScroll) {
sectionRefs[currentSection]?.current?.scrollIntoView({behavior: "smooth", inline: "start"});
}
}, [currentSection, isManualScroll]);
useEffect(() => {
const handleScroll = () => {
setIsManualScroll(true);
};
const scrollContainer = scrollContainerRef.current;
if (scrollContainer) {
scrollContainer.addEventListener("scroll", handleScroll);
}
return () => {
if (scrollContainer) {
scrollContainer.removeEventListener("scroll", handleScroll);
}
};
}, []);
// Intersection Observer to detect when last section is fully visible
// Track scrolling manually and update currentSection
useEffect(() => {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting && entry.target === sectionRefs[timelineData.length - 1].current) {
setTimelineFinished(true);
if (entry.isIntersecting) {
const index = sectionRefs.findIndex(ref => ref.current === entry.target);
if (index !== -1) {
setCurrentSection(index);
setTimelineFinished(index === timelineData.length - 1);
}
}
});
},
{threshold: 0.8} // Increased threshold to ensure full visibility before triggering
{threshold: 0.6}
);
sectionRefs.forEach((ref) => {
@@ -115,7 +106,7 @@ export default function About() {
<h1 style={{fontSize: "2.5rem", marginBottom: "1rem"}}>About Me</h1>
<p style={{maxWidth: "600px", textAlign: "center"}}>
I’m a passionate developer always excited about new challenges.
Scroll down to check out my coding journey through the years!
Scroll down to learn more about me!
</p>
</section>
@@ -195,26 +186,31 @@ export default function About() {
</div>
</section>
{/* Skills Section with Liquid Progress Bars */}
{timelineFinished && (
<section
style={{
minHeight: "100vh",
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
padding: "2rem",
boxSizing: "border-box",
}}
>
<h2 style={{fontSize: "2rem", marginBottom: "1rem"}}>My Skills</h2>
<p style={{maxWidth: "600px", textAlign: "center"}}>
By completing projects in web development, scripting, and game
design, I’ve cultivated a diverse set of skills — spanning
JavaScript, Python, C#, and more!
</p>
<section style={{ minHeight: "100vh", display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", padding: "2rem", color: "#fff" }}>
<h2 style={{ fontSize: "2rem", marginBottom: "2rem" }}>My Skills</h2>
<div style={{ display: "flex", gap: "2rem" }}>
{skills.map((skill, index) => (
<div key={index} style={{ textAlign: "center" }}>
<div style={{ width: "60px", height: "200px", backgroundColor: "#333", borderRadius: "10px", overflow: "hidden", position: "relative", border: "2px solid #555" }}>
<div className="liquid" style={{
width: "100%",
height: `${skill.level}%`,
position: "absolute",
bottom: 0,
background: "linear-gradient(45deg, #7400a9, #411357)",
borderTop: "4px solid rgba(255, 255, 255, 0.6)",
animation: `wave ${skill.waveSpeed} infinite ease-in-out`,
}}><p style={{ marginTop: "10px", fontSize: "1rem" }}>{skill.level}%</p></div>
</div>
<p style={{ marginTop: "10px", fontSize: "1rem" }}>{skill.name}</p>
</div>
))}
</div>
</section>
)}
</div>
);
)
}
+12
View File
@@ -181,3 +181,15 @@ body {
object-fit: contain;
display: block;
}
@keyframes wave {
0% {
transform: translateY(8px);
}
50% {
transform: translateY(0px);
}
100% {
transform: translateY(8px);
}
}