23
loading...
This website collects cookies to deliver better user experience
All the code is available on Github.
liked
state.import React, { useState } from "react";
import cn from "classnames";
import { ReactComponent as Hand } from "./hand.svg";
import "./styles.scss";
const LikeButton = () => {
const [liked, setLiked] = useState(null);
return (
<button
onClick={() => setLiked(!liked)}
onAnimationEnd={() => setClicked(false)}
className={cn("like-button-wrapper", {
liked,
})}
>
<div className="like-button">
<Hand />
<span>Like</span>
</div>
</button>
);
};
export default LikeButton;
span
tag that will make the change of the whole text possible. The tag will hold the suffix of the “like” text.import React, { useState } from "react";
import cn from "classnames";
import { ReactComponent as Hand } from "./hand.svg";
import "./styles.scss";
const LikeButton = () => {
const [liked, setLiked] = useState(null);
return (
<button
onClick={() => setLiked(!liked)}
onAnimationEnd={() => setClicked(false)}
className={cn("like-button-wrapper", {
liked,
})}
>
<div className="like-button">
<Hand />
<span>Like</span>
<span className={cn("suffix", { liked })}>d</span>
</div>
</button>
);
};
export default LikeButton;
.like-button-wrapper {
&::before {
content: "";
z-index: 1;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
will-change: background-color;
transition: background-color 0.3s, transform 0.3s;
background-color: $dark;
box-shadow: 0 0 10px $darkLower;
border-radius: 8px;
}
&.liked {
&::before {
background-color: $primary;
}
}
.suffix {
opacity: 0;
transition: opacity 300ms, transform 300ms;
transform: translateX(15px);
&.liked {
opacity: 1;
transform: translateX(0);
}
}
}
onAnimationEnd
event handler to reset the state of clicked
back to false
. We need to do this so the animation plays over and over each time the button is clicked.import React, { useState } from "react";
import cn from "classnames";
import { ReactComponent as Hand } from "./hand.svg";
import "./styles.scss";
const LikeButton = () => {
const [liked, setLiked] = useState(null);
const [clicked, setClicked] = useState(false);
return (
<button
onClick={() => {
setLiked(!liked);
setClicked(true);
}}
onAnimationEnd={() => setClicked(false)}
className={cn("like-button-wrapper", {
liked,
clicked,
})}
>
<div className="like-button">
<Hand />
<span>Like</span>
<span className={cn("suffix", { liked })}>d</span>
</div>
</button>
);
};
export default LikeButton;
.like-button-wrapper {
&.clicked {
&::before {
animation: click 300ms;
}
}
}
@keyframes click {
0% {
transform: scale(1);
}
50% {
transform: scale(0.96);
}
100% {
transform: scale(1);
}
}
<svg width="84" height="67" viewBox="0 0 84 67" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Icon">
<g id="hand">
<rect id="Rectangle 1" y="21.9999" width="20" height="45" rx="5" fill="white"/>
<path id="thumb-4" d="M24 23.9999H79C81.7614 23.9999 84 26.2385 84 28.9999V30.9999C84 33.7613 81.7614 35.9999 79 35.9999H24V23.9999Z" fill="white"/>
<path id="thumb-3" d="M24 53.9999H64C66.7614 53.9999 69 56.2385 69 58.9999V60.9999C69 63.7613 66.7614 65.9999 64 65.9999H24V53.9999Z" fill="white"/>
<path id="thumb-2" d="M24 43.9999H69C71.7614 43.9999 74 46.2385 74 48.9999V50.9999C74 53.7613 71.7614 55.9999 69 55.9999H24V43.9999Z" fill="white"/>
<path id="thumb-1" d="M24 33.9999H74C76.7614 33.9999 79 36.2385 79 38.9999V40.9999C79 43.7613 76.7614 45.9999 74 45.9999H24V33.9999Z" fill="white"/>
</g>
<path id="thumb-start" d="M34 29.5506C37.1438 28.5631 44.1537 25.192 47.0428 19.6065" stroke="white" stroke-width="12" stroke-linecap="round" stroke-linejoin="round"/>
<path id="thumb-end" d="M44.6172 22.7471C46.9813 20.4514 51.7265 14.2881 51.7952 8.00001" stroke="white" stroke-width="12" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>
thumb-end
and animate it every time a user hovers over the button. We also want to tilt the whole icon a little. That’s why we’re applying the transform to the whole svg
tag..like-button-wrapper {
&:hover:not(.liked) {
svg {
transform: translateY(-2px) rotate(8deg);
#thumb-end {
transform: rotate(45deg) translate(5px, -45px);
}
}
}
&.liked {
svg {
animation: hop 500ms;
}
}
svg {
width: 22px;
height: 22px;
margin-right: 8px;
transform: translateY(-2px);
transition: transform 0.2s;
#thumb-end {
transition: transform 0.2s;
}
}
}
@keyframes hop {
0% {
transform: rotate(8deg) translateY(-2px);
}
30% {
transform: rotate(-14deg) translateY(-5px);
}
65% {
transform: rotate(7deg) translateY(2px);
}
100% {
transform: rotate(0deg) translateY(-2px);
}
}
hop
animation. It is not really needed, but I think it makes the whole interaction much more fun and playful. Just look at it. 🤓import React, { useState } from "react";
import cn from "classnames";
import { ReactComponent as Hand } from "./hand.svg";
import "./styles.scss";
const particleList = Array.from(Array(10));
const LikeButton = () => {
const [liked, setLiked] = useState(null);
const [clicked, setClicked] = useState(false);
return (
<button
onClick={() => {
setLiked(!liked);
setClicked(true);
}}
onAnimationEnd={() => setClicked(false)}
className={cn("like-button-wrapper", {
liked,
clicked,
})}
>
{liked && (
<div className="particles">
{particleList.map((_, index) => (
<div
className="particle-rotate"
style={{
transform: `rotate(${
(360 / particleList.length) * index + 1
}deg)`,
}}
>
<div className="particle-tick" />
</div>
))}
</div>
)}
<div className="like-button">
<Hand />
<span>Like</span>
<span className={cn("suffix", { liked })}>d</span>
</div>
</button>
);
};
export default LikeButton;
.like-button-wrapper {
.particles {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
.particle-rotate {
position: absolute;
&:nth-child(1) {
right: 0;
top: 50%;
}
&:nth-child(2) {
right: 0;
bottom: 0;
}
&:nth-child(3) {
right: 33%;
bottom: 0;
}
&:nth-child(4) {
right: 66%;
bottom: 0;
}
&:nth-child(5) {
left: 0;
bottom: 0;
}
&:nth-child(6) {
left: 0;
bottom: 50%;
}
&:nth-child(7) {
left: 0;
top: 0;
}
&:nth-child(8) {
left: 33%;
top: 0;
}
&:nth-child(9) {
left: 66%;
top: 0;
}
&:nth-child(10) {
right: 0;
top: 0;
}
}
.particle-tick {
position: absolute;
z-index: -1;
width: 10px;
height: 1px;
background-color: $primary;
animation: boom 500ms;
transform: translateX(-25px);
}
}
}
@keyframes boom {
0% {
transform: translateX(-25px);
opacity: 1;
}
100% {
transform: translateX(50px);
opacity: 0;
}
}
boom
animation to each particle. So now when the button is clicked. We celebrate by releasing the particles. 🎉