Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 6db694d

Browse files
Merge pull request #174 from Yugveer06/motion
Enhancement: Smoothed modal animation + reduced motion
2 parents 55a9225 + 8a1352c commit 6db694d

File tree

4 files changed

+87
-45
lines changed

4 files changed

+87
-45
lines changed

‎package-lock.json

Lines changed: 32 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"snippets:consolidate": "node ./utils/consolidateSnippets.js"
1717
},
1818
"dependencies": {
19-
"framer-motion": "^11.15.0",
19+
"motion": "^11.15.0",
2020
"prismjs": "^1.29.0",
2121
"react": "^18.3.1",
2222
"react-dom": "^18.3.1",

‎src/components/SnippetList.tsx

Lines changed: 45 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { motion, AnimatePresence} from "framer-motion";
1+
import { motion, AnimatePresence,useReducedMotion} from "motion/react";
22
import { useState } from "react";
33

44
import { useAppContext } from "@contexts/AppContext";
@@ -13,6 +13,8 @@ const SnippetList = () => {
1313
const { fetchedSnippets } = useSnippets();
1414
const [isModalOpen, setIsModalOpen] = useState(false);
1515

16+
const shouldReduceMotion = useReducedMotion();
17+
1618
if (!fetchedSnippets)
1719
return (
1820
<div>
@@ -34,41 +36,49 @@ const SnippetList = () => {
3436
<>
3537
<motion.ul role="list" className="snippets">
3638
<AnimatePresence mode="popLayout">
37-
{fetchedSnippets.map((snippet, idx) => (
38-
<motion.li
39-
key={idx}
40-
initial={{opacity: 0,y: 20}}
41-
animate={{
42-
opacity: 1,
43-
y: 0,
44-
transition: {
45-
delay: idx*0.05,
46-
duration: 0.2,
47-
},
48-
}}
49-
exit={{
50-
opacity: 0,
51-
y: -20,
52-
transition: {
53-
delay: (fetchedSnippets.length-1-idx)*0.01,
54-
duration: 0.09,
55-
},
56-
}}
57-
>
58-
<motion.button
59-
className="snippet | flow"
60-
data-flow-space="sm"
61-
onClick={()=>handleOpenModal(snippet)}
62-
whileHover={{scale: 1.01}}
63-
whileTap={{scale: 0.98}}
39+
{fetchedSnippets.map((snippet, idx) => {
40+
constuniqueId=`${language.lang}-${snippet.title}`;
41+
return(
42+
<motion.li
43+
key={uniqueId}
44+
layoutId={uniqueId}
45+
initial={{opacity: 0,y: 20}}
46+
animate={{
47+
opacity: 1,
48+
y: 0,
49+
transition: {
50+
delay: shouldReduceMotion ? 0 : 0.09+idx*0.05,
51+
duration: shouldReduceMotion ? 0 : 0.2,
52+
},
53+
}}
54+
exit={{
55+
opacity: 0,
56+
y: -20,
57+
transition: {
58+
delay: idx*0.01,
59+
duration: shouldReduceMotion ? 0 : 0.09,
60+
},
61+
}}
62+
transition={{
63+
ease: [0,0.75,0.25,1],
64+
duration: shouldReduceMotion ? 0 : 0.25,
65+
}}
6466
>
65-
<div className="snippet__preview">
66-
<img src={language.icon} alt={language.lang} />
67-
</div>
68-
<h3 className="snippet__title">{snippet.title}</h3>
69-
</motion.button>
70-
</motion.li>
71-
))}
67+
<motion.button
68+
className="snippet | flow"
69+
data-flow-space="sm"
70+
onClick={() => handleOpenModal(snippet)}
71+
whileHover={{ scale: 1.01 }}
72+
whileTap={{ scale: 0.98 }}
73+
>
74+
<div className="snippet__preview">
75+
<img src={language.icon} alt={language.lang} />
76+
</div>
77+
<h3 className="snippet__title">{snippet.title}</h3>
78+
</motion.button>
79+
</motion.li>
80+
);
81+
})}
7282
</AnimatePresence>
7383
</motion.ul>
7484

‎src/components/SnippetModal.tsx

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { motion} from "framer-motion";
1+
import { motion,useReducedMotion} from "motion/react";
22
import React from "react";
33
import ReactDOM from "react-dom";
44

@@ -23,6 +23,8 @@ const SnippetModal: React.FC<Props> = ({
2323
}) => {
2424
const modalRoot = document.getElementById("modal-root");
2525

26+
const shouldReduceMotion = useReducedMotion();
27+
2628
useEscapeKey(handleCloseModal);
2729

2830
if (!modalRoot) {
@@ -41,16 +43,17 @@ const SnippetModal: React.FC<Props> = ({
4143
initial={{ opacity: 0 }}
4244
animate={{ opacity: 1 }}
4345
exit={{ opacity: 0 }}
44-
transition={{ duration: 0.2 }}
46+
transition={{ duration: shouldReduceMotion ? 0 : 0.2 }}
4547
>
4648
<motion.div
4749
key="modal-content"
4850
className="modal | flow"
4951
data-flow-space="lg"
50-
initial={{ scale: 0.8, opacity: 0, y: 20 }}
51-
animate={{ scale: 1, opacity: 1, y: 0 }}
52-
exit={{ scale: 0.8, opacity: 0, y: 20 }}
53-
transition={{ type: "spring", duration: 0.5 }}
52+
layoutId={`${language}-${snippet.title}`}
53+
transition={{
54+
ease: [0, 0.75, 0.25, 1],
55+
duration: shouldReduceMotion ? 0 : 0.3,
56+
}}
5457
>
5558
<div className="modal__header">
5659
<h2 className="section-title">{snippet.title}</h2>

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /