Created
July 31, 2024 13:20
-
-
Save evertjr/d7c2a0a670cf92231bf519719a19225f to your computer and use it in GitHub Desktop.
Feedback face animation with Framer Motion and Tailwind
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use client"; | |
import { AnimationProps, motion } from "framer-motion"; | |
import { useState } from "react"; | |
type Feedbacks = "bad" | "not bad" | "good"; | |
const feedbackMapping: { [key: number]: Feedbacks } = { | |
0: "bad", | |
1: "not bad", | |
2: "good", | |
}; | |
const transition: AnimationProps["transition"] = { | |
type: "spring", | |
stiffness: 200, | |
damping: 17, | |
}; | |
export function ExperienceAnimation() { | |
const [feedback, setFeedback] = useState<Feedbacks>("good"); | |
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => { | |
const value = parseInt(e.currentTarget.value, 10); | |
setFeedback(feedbackMapping[value]); | |
}; | |
const backgroundColor = | |
feedback === "good" | |
? "#AFC358" | |
: feedback === "not bad" | |
? "#E1AB47" | |
: "#FD8464"; | |
return ( | |
<motion.div | |
animate={{ | |
backgroundColor, | |
}} | |
className="h-screen w-full flex items-center flex-col gap-32 justify-center" | |
> | |
<div className="flex h-64 flex-col gap-2 justify-center items-center"> | |
<Eyes state={feedback} /> | |
<Mouth state={feedback} /> | |
</div> | |
<input | |
value={Object.keys(feedbackMapping).find( | |
(key) => feedbackMapping[parseInt(key)] === feedback | |
)} | |
onChange={handleInputChange} | |
type="range" | |
step={1} | |
min={0} | |
max={2} | |
/> | |
</motion.div> | |
); | |
} | |
function Mouth({ state }: { state: Feedbacks }) { | |
const rotate = state === "good" ? 0 : 180; | |
return ( | |
<motion.svg | |
animate={{ rotate }} | |
transition={transition} | |
xmlns="http://www.w3.org/2000/svg" | |
width={64} | |
height={27} | |
fill="none" | |
className="stroke-black/80" | |
> | |
<path | |
strokeLinecap="round" | |
strokeWidth={14} | |
d="M7.017 7.547C15.865 19.935 41.23 25.834 56.273 9.02" | |
/> | |
</motion.svg> | |
); | |
} | |
function Eyes({ state }: { state?: Feedbacks }) { | |
const height = | |
state === "good" ? "8rem" : state === "not bad" ? "3rem" : "4rem"; | |
const width = state === "bad" ? "4rem" : "8rem"; | |
const rotate = state === "bad"; | |
return ( | |
<div className="flex gap-5 items-center"> | |
<motion.div | |
animate={{ | |
height, | |
rotate: rotate ? -90 : 0, | |
width, | |
}} | |
transition={transition} | |
className="size-32 bg-black/80 rounded-full" | |
/> | |
<motion.div | |
animate={{ | |
height, | |
rotate: rotate ? 90 : 0, | |
width, | |
}} | |
transition={transition} | |
className="size-32 bg-black/80 rounded-full" | |
/> | |
</div> | |
); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment