feat: add animate quote at about page
This commit is contained in:
parent
e605b2e975
commit
93eb451880
5 changed files with 167 additions and 1 deletions
|
|
@ -100,7 +100,12 @@ const MyPage = () => {
|
||||||
type: "spring",
|
type: "spring",
|
||||||
}}>
|
}}>
|
||||||
<Button variation="primary">
|
<Button variation="primary">
|
||||||
<Link href={"/docs/cv.pdf"}>
|
<Link
|
||||||
|
href={"/docs/cv.pdf"}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
locale={false}
|
||||||
|
download>
|
||||||
Download CV
|
Download CV
|
||||||
</Link>
|
</Link>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
||||||
63
app/about/(quote)/quote.jsx
Normal file
63
app/about/(quote)/quote.jsx
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
// Quote.js
|
||||||
|
import "./style.css";
|
||||||
|
import { motion } from "framer-motion";
|
||||||
|
import { useIntersectionObserver } from "./useIntersectionObserver";
|
||||||
|
|
||||||
|
export default function Quote() {
|
||||||
|
const text1 = '"There are no limits to what you can accomplish'.split(" ");
|
||||||
|
const text2 = 'except the limits you place on your own thinking."'.split(
|
||||||
|
" "
|
||||||
|
);
|
||||||
|
const [ref, isIntersecting] = useIntersectionObserver();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={ref} className="text-center">
|
||||||
|
<h3 className="text-[2rem]">
|
||||||
|
{text1.map((word, index) => (
|
||||||
|
<motion.span
|
||||||
|
key={index}
|
||||||
|
initial={{
|
||||||
|
opacity: 0,
|
||||||
|
filter: "blur(4px)",
|
||||||
|
scale: 0.92,
|
||||||
|
}}
|
||||||
|
animate={{
|
||||||
|
opacity: isIntersecting ? 1 : 0,
|
||||||
|
filter: isIntersecting ? "blur(0px)" : "blur(4px)",
|
||||||
|
scale: isIntersecting ? 1 : 0.92,
|
||||||
|
}}
|
||||||
|
transition={{
|
||||||
|
delay: isIntersecting ? index * 0.1 : 0,
|
||||||
|
duration: 0.5,
|
||||||
|
}}>
|
||||||
|
{word}{" "}
|
||||||
|
</motion.span>
|
||||||
|
))}
|
||||||
|
</h3>
|
||||||
|
<h3 className="text-xl">
|
||||||
|
{text2.map((word, index) => (
|
||||||
|
<motion.span
|
||||||
|
key={index + text1.length}
|
||||||
|
initial={{
|
||||||
|
opacity: 0,
|
||||||
|
filter: "blur(4px)",
|
||||||
|
scale: 0.92,
|
||||||
|
}}
|
||||||
|
animate={{
|
||||||
|
opacity: isIntersecting ? 1 : 0,
|
||||||
|
filter: isIntersecting ? "blur(0px)" : "blur(4px)",
|
||||||
|
scale: isIntersecting ? 1 : 0.92,
|
||||||
|
}}
|
||||||
|
transition={{
|
||||||
|
delay: isIntersecting
|
||||||
|
? (text1.length + index) * 0.1
|
||||||
|
: 0,
|
||||||
|
duration: 0.5,
|
||||||
|
}}>
|
||||||
|
{word}{" "}
|
||||||
|
</motion.span>
|
||||||
|
))}
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
45
app/about/(quote)/style.css
Normal file
45
app/about/(quote)/style.css
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
@keyframes scale {
|
||||||
|
100% {
|
||||||
|
transform: scale(1);
|
||||||
|
-webkit-transform: scale(1);
|
||||||
|
-moz-transform: scale(1);
|
||||||
|
-ms-transform: scale(1);
|
||||||
|
-o-transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fade-in {
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
filter: blur(0);
|
||||||
|
-webkit-filter: blur(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.q-wrap {
|
||||||
|
display: inline-block;
|
||||||
|
opacity: 1;
|
||||||
|
filter: blur(4px);
|
||||||
|
-webkit-filter: blur(4px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.q-wrap:nth-child(1) {
|
||||||
|
animation: fade-in 0.8s 0.1 forwards cubic-bezier(0.11, 0, 0.5, 0);
|
||||||
|
-webkit-animation: fade-in 0.8s 0.1 forwards cubic-bezier(0.11, 0, 0.5, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.q-wrap:nth-child(2) {
|
||||||
|
animation: fade-in 0.8s 0.2 forwards cubic-bezier(0.11, 0, 0.5, 0);
|
||||||
|
-webkit-animation: fade-in 0.8s 0.2 forwards cubic-bezier(0.11, 0, 0.5, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.q {
|
||||||
|
text-align: center;
|
||||||
|
transform: scale(0.92);
|
||||||
|
-webkit-transform: scale(0.92);
|
||||||
|
-moz-transform: scale(0.92);
|
||||||
|
-ms-transform: scale(0.92);
|
||||||
|
-o-transform: scale(0.92);
|
||||||
|
animation: scale 3s forwards cubic-bezier(0.5, 1, 0.89, 1);
|
||||||
|
-webkit-animation: scale 3s forwards cubic-bezier(0.5, 1, 0.89, 1);
|
||||||
|
}
|
||||||
32
app/about/(quote)/useIntersectionObserver.js
Normal file
32
app/about/(quote)/useIntersectionObserver.js
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
// useIntersectionObserver.js
|
||||||
|
import { useEffect, useState, useRef } from "react";
|
||||||
|
|
||||||
|
export function useIntersectionObserver() {
|
||||||
|
const [isIntersecting, setIsIntersecting] = useState(false);
|
||||||
|
const ref = useRef(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const observer = new IntersectionObserver(
|
||||||
|
([entry]) => {
|
||||||
|
setIsIntersecting(entry.isIntersecting);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
root: null,
|
||||||
|
rootMargin: "0px",
|
||||||
|
threshold: 0.1,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (ref.current) {
|
||||||
|
observer.observe(ref.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (ref.current) {
|
||||||
|
observer.unobserve(ref.current);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return [ref, isIntersecting];
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,7 @@ import FixedButon from "@/components/FixedButton";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { faChevronLeft } from "@fortawesome/free-solid-svg-icons";
|
import { faChevronLeft } from "@fortawesome/free-solid-svg-icons";
|
||||||
import Card from "./(spotify)/card.jsx";
|
import Card from "./(spotify)/card.jsx";
|
||||||
|
import Quote from "./(quote)/quote.jsx";
|
||||||
|
|
||||||
// images
|
// images
|
||||||
import Me1 from "@/public/img/me1.jpg";
|
import Me1 from "@/public/img/me1.jpg";
|
||||||
|
|
@ -453,6 +454,26 @@ export default function Page() {
|
||||||
</p>
|
</p>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="min-h-[80vh] w-screen mx-auto container p-10 grid grid-cols-1 mt-10 ">
|
||||||
|
<motion.div
|
||||||
|
className="flex justify-center items-center flex-col mb-5 "
|
||||||
|
initial={{
|
||||||
|
opacity: 0,
|
||||||
|
scale: 0.9,
|
||||||
|
}}
|
||||||
|
whileInView={{
|
||||||
|
opacity: 1,
|
||||||
|
scale: 1,
|
||||||
|
}}
|
||||||
|
transition={{
|
||||||
|
delay: 0.6,
|
||||||
|
duration: 2,
|
||||||
|
ease: [0.22, 1, 0.36, 1],
|
||||||
|
}}>
|
||||||
|
<Quote />
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue