Skip to content

Instantly share code, notes, and snippets.

@thibauld
Created March 25, 2023 15:56
Show Gist options
  • Save thibauld/38f22a0c2d6bb2a3b8a2ace37cc2ba8c to your computer and use it in GitHub Desktop.
Save thibauld/38f22a0c2d6bb2a3b8a2ace37cc2ba8c to your computer and use it in GitHub Desktop.
✨ Button Hover Animation
<button class="generate-button">
<svg class="icon" viewBox="0 0 24 26">
<path
d="M5.16515 2.62145L5.8075 0.999247C5.83876 0.919722 5.9154 0.866699 6.00112 0.866699C6.08683 0.866699 6.16347 0.919722 6.19473 0.999247L6.83708 2.62145L8.44145 3.27094C8.5201 3.30254 8.57254 3.38003 8.57254 3.4667C8.57254 3.55337 8.5201 3.63085 8.44145 3.66246L6.83708 4.31195L6.19473 5.93415C6.16347 6.0147 6.08683 6.0667 6.00112 6.0667C5.9154 6.0667 5.83876 6.0147 5.8075 5.93415L5.16515 4.31195L3.56078 3.66246C3.48112 3.63085 3.42969 3.55337 3.42969 3.4667C3.42969 3.38003 3.48112 3.30254 3.56078 3.27094L5.16515 2.62145Z"
/>
<path
d="M11.2362 9.43967C11.5502 9.30067 11.8015 9.05025 11.9405 8.73617L13.5494 5.11725C13.7169 4.74204 14.0887 4.5 14.5 4.5C14.9112 4.5 15.2839 4.74204 15.4506 5.11725L17.0603 8.73617C17.1985 9.05025 17.4497 9.3015 17.7638 9.43967L21.3827 11.0494C21.7579 11.2161 22 11.5887 22 12C22 12.4112 21.7579 12.7831 21.3827 12.9506L17.7638 14.5595C17.4497 14.6985 17.1993 14.9497 17.0603 15.2638L15.4506 18.8827C15.2839 19.2579 14.9112 19.5 14.5 19.5C14.0887 19.5 13.7169 19.2579 13.5494 18.8827L11.9405 15.2638C11.8015 14.9497 11.5502 14.6985 11.2362 14.5595L7.61725 12.9506C7.24204 12.7831 7 12.4112 7 12C7 11.5887 7.24204 11.2161 7.61725 11.0494L11.2362 9.43967Z"
/>
<path
d="M4.60728 19.392L5.67703 16.6875C5.72997 16.5541 5.85854 16.4666 6.00056 16.4666C6.14258 16.4666 6.27031 16.5541 6.32325 16.6875L7.39299 19.392L10.0678 20.4736C10.1997 20.5271 10.2863 20.6563 10.2863 20.7999C10.2863 20.9435 10.1997 21.0735 10.0678 21.1271L7.39299 22.2087L6.32325 24.9123C6.27031 25.0457 6.14258 25.1332 6.00056 25.1332C5.85854 25.1332 5.72997 25.0457 5.67703 24.9123L4.60728 22.2087L1.93333 21.1271C1.8014 21.0735 1.71484 20.9435 1.71484 20.7999C1.71484 20.6563 1.8014 20.5271 1.93333 20.4736L4.60728 19.392Z"
/>
</svg>
<span>Generate Site</span>
</button>
<!-- twitter -->
<a class="twitter" target="_top" href="https://twitter.com/aaroniker_me"><svg xmlns="http://www.w3.org/2000/svg" width="72" height="72" viewBox="0 0 72 72"><path d="M67.812 16.141a26.246 26.246 0 0 1-7.519 2.06 13.134 13.134 0 0 0 5.756-7.244 26.127 26.127 0 0 1-8.313 3.176A13.075 13.075 0 0 0 48.182 10c-7.229 0-13.092 5.861-13.092 13.093 0 1.026.118 2.021.338 2.981-10.885-.548-20.528-5.757-26.987-13.679a13.048 13.048 0 0 0-1.771 6.581c0 4.542 2.312 8.551 5.824 10.898a13.048 13.048 0 0 1-5.93-1.638c-.002.055-.002.11-.002.162 0 6.345 4.513 11.638 10.504 12.84a13.177 13.177 0 0 1-3.449.457c-.846 0-1.667-.078-2.465-.231 1.667 5.2 6.499 8.986 12.23 9.09a26.276 26.276 0 0 1-16.26 5.606A26.21 26.21 0 0 1 4 55.976a37.036 37.036 0 0 0 20.067 5.882c24.083 0 37.251-19.949 37.251-37.249 0-.566-.014-1.134-.039-1.694a26.597 26.597 0 0 0 6.533-6.774z"></path></svg></a>
const DOT_AMOUNT = 40;
const createSVG = (width, height, className, childType, childAttributes) => {
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.classList.add(className);
const child = document.createElementNS(
"http://www.w3.org/2000/svg",
childType
);
svg.setAttributeNS(
"http://www.w3.org/2000/svg",
"viewBox",
`0 0 ${width} ${height}`
);
for (const attr in childAttributes) {
child.setAttribute(attr, childAttributes[attr]);
}
svg.appendChild(child);
return { svg, child };
};
document.querySelectorAll(".generate-button").forEach((button) => {
const width = button.offsetWidth;
const height = button.offsetHeight;
const style = getComputedStyle(button);
const { svg, child: circle } = createSVG(width, height, "dots", "circle", {
cx: "0",
cy: "0",
r: "0",
});
const strokeGroup = document.createElement("div");
strokeGroup.classList.add("stroke");
const { svg: stroke } = createSVG(width, height, "stroke-line", "rect", {
x: "0",
y: "0",
width: "100%",
height: "100%",
rx: parseInt(style.borderRadius, 10),
ry: parseInt(style.borderRadius, 10),
pathLength: "10",
});
button.appendChild(svg);
strokeGroup.appendChild(stroke);
strokeGroup.appendChild(stroke.cloneNode(true));
button.appendChild(strokeGroup);
const timeline = gsap.timeline({ paused: true });
for (var i = 0; i < DOT_AMOUNT; i++) {
var p = circle.cloneNode(true);
svg.appendChild(p);
gsap.set(p, {
attr: {
cx: gsap.utils.random(width * 0.25, width * 0.75),
cy: gsap.utils.random(height * 0.5, height * 0.5),
r: 0,
},
});
var durationRandom = gsap.utils.random(10, 12);
var tl = gsap.timeline();
tl.to(
p,
{
duration: durationRandom,
rotation: i % 2 === 0 ? 200 : -200,
attr: {
r: gsap.utils.random(0.75, 1.5),
cy: -width * gsap.utils.random(1.25, 1.75),
},
physics2D: {
angle: -90,
gravity: gsap.utils.random(-4, -8),
velocity: gsap.utils.random(10, 25),
},
},
"-=" + durationRandom / 2
).to(
p,
{
duration: durationRandom / 3,
attr: {
r: 0,
},
},
"-=" + durationRandom / 4
);
timeline.add(tl, i / 3);
}
svg.removeChild(circle);
const finalTimeline = gsap.to(timeline, {
duration: 10,
repeat: -1,
time: timeline.duration(),
paused: true,
});
const stars = gsap.to(button, {
repeat: -1,
repeatDelay: 0.75,
paused: true,
keyframes: [
{
"--generate-button-star-2-scale": ".5",
"--generate-button-star-2-opacity": ".25",
"--generate-button-star-3-scale": "1.25",
"--generate-button-star-3-opacity": "1",
duration: 0.3,
},
{
"--generate-button-star-1-scale": "1.5",
"--generate-button-star-1-opacity": ".5",
"--generate-button-star-2-scale": ".5",
"--generate-button-star-3-scale": "1",
"--generate-button-star-3-opacity": ".5",
duration: 0.3,
},
{
"--generate-button-star-1-scale": "1",
"--generate-button-star-1-opacity": ".25",
"--generate-button-star-2-scale": "1.15",
"--generate-button-star-2-opacity": "1",
duration: 0.3,
},
{
"--generate-button-star-2-scale": "1",
duration: 0.35,
},
],
});
button.addEventListener("pointerenter", () => {
gsap.to(button, {
"--generate-button-dots-opacity": ".5",
duration: 0.25,
onStart: () => {
finalTimeline.restart().play();
setTimeout(() => stars.restart().play(), 500);
},
});
});
button.addEventListener("pointerleave", () => {
gsap.to(button, {
"--generate-button-dots-opacity": "0",
"--generate-button-star-1-opacity": ".25",
"--generate-button-star-1-scale": "1",
"--generate-button-star-2-opacity": "1",
"--generate-button-star-2-scale": "1",
"--generate-button-star-3-opacity": ".5",
"--generate-button-star-3-scale": "1",
duration: 0.15,
onComplete: () => {
finalTimeline.pause();
stars.pause();
},
});
});
});
<script src="https://unpkg.co/gsap@3/dist/gsap.min.js"></script>
<script src="https://assets.codepen.io/16327/Physics2DPlugin3.min.js"></script>
.generate-button {
--generate-button-star-1-opacity: .25;
--generate-button-star-1-scale: 1;
--generate-button-star-2-opacity: 1;
--generate-button-star-2-scale: 1;
--generate-button-star-3-opacity: .5;
--generate-button-star-3-scale: 1;
--generate-button-dots-opacity: 0;
appearance: none;
outline: none;
border: none;
padding: 16px 24px 16px 20px;
border-radius: 29px;
margin: 0;
background-color: #1D1D1D;
color: #616161;
min-width: 192px;
display: flex;
position: relative;
cursor: pointer;
-webkit-tap-highlight-color: transparent;
z-index: 1;
transform: scale(var(--generate-button-scale, 1)) translateZ(0);
box-shadow: 0px 0px 120px var(--generate-button-shadow-wide, transparent), 0px 4px 12px rgba(0, 0, 0, 0.05), 0px 1px 2px rgba(0, 0, 0, 0.1), inset 0px 1px 1px var(--generate-button-shadow-inset, rgba(255, 255, 255, 0.04)), 0 0 0 var(--generate-button-shadow-outline, 0px) rgba(109, 68, 244, 0.4);
transition: transform .3s, background-color .3s, box-shadow .3s, color .3s;
&:before {
content: '';
display: block;
position: absolute;
right: 20%;
height: 20px;
left: 20%;
bottom: -10px;
background: #D3B3FF;
filter: blur(12.5px);
z-index: 2;
clip-path: inset(-200% -30% 10px -30% round 29px);
opacity: 0;
transition: opacity .4s;
transform: translateZ(0);
}
span {
position: relative;
z-index: 1;
font-family: 'Poppins', Arial;
font-weight: 600;
font-size: 16px;
line-height: 26px;
letter-spacing: 0.005em;
display: block;
&:before {
content: '';
background-image: linear-gradient(to right, transparent, #1D1D1D);
position: absolute;
inset: 0;
z-index: 1;
pointer-events: none;
opacity: .4;
transition: opacity .4s;
}
}
.stroke {
mix-blend-mode: hard-light;
svg {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
fill: none;
stroke-width: .75px;
stroke: #E2D9FF;
stroke-dasharray: 1.5 14;
stroke-dashoffset: 22;
opacity: 0;
&:nth-child(2) {
stroke-width: 1px;
stroke-opacity: .5;
filter: blur(3px);
}
}
}
svg {
display: block;
overflow: visible;
pointer-events: none;
&.dots {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
z-index: 10;
fill: #D3B2FF;
opacity: var(--generate-button-dots-opacity);
}
&.icon {
width: 24px;
height: 26px;
margin-right: 12px;
fill: currentColor;
path {
&:nth-child(1) {
opacity: var(--generate-button-star-1-opacity);
transform: scale(var(--generate-button-star-1-scale)) translateZ(0);
transform-origin: 25% 14.58%;
}
&:nth-child(2) {
opacity: var(--generate-button-star-2-opacity);
transform: scale(var(--generate-button-star-2-scale)) translateZ(0);
transform-origin: 60.42% 50%;
}
&:nth-child(3) {
opacity: var(--generate-button-star-3-opacity);
transform: scale(var(--generate-button-star-3-scale)) translateZ(0);
transform-origin: 25% 85.42%;
}
}
}
}
&:hover {
--generate-button-scale: 1.1;
--generate-button-shadow-wide: rgba(208, 173, 255, 0.4);
--generate-button-shadow-inset: rgba(255, 255, 255, 0.35);
--generate-button-shadow-outline: 3px;
color: #fff;
background-color: #6D44F4;
.stroke {
svg {
animation: stroke 2s linear infinite;
}
}
&:before {
opacity: 1;
}
span {
&:before {
opacity: 0;
}
}
&:active {
--generate-button-scale: 1.05;
}
}
}
@keyframes stroke {
0% {
opacity: 0;
}
25%,
75% {
opacity: 1;
}
95%,
100% {
stroke-dashoffset: 6;
opacity: 0;
}
}
body {
transition: background-color .3s;
background-color: #111111;
&:after {
content: '';
position: absolute;
inset: 0;
background-image: url("https://assets.codepen.io/165585/noise_1.png");
background-repeat: repeat;
opacity: 0;
mix-blend-mode: overlay;
pointer-events: none;
}
&:has(.generate-button:hover) {
background-color: #10042E;
}
&:after {
transition: opacity .3s;
}
&:hover :after {
opacity: .25;
}
}
html {
box-sizing: border-box;
-webkit-font-smoothing: antialiased;
}
* {
box-sizing: inherit;
&:before,
&:after {
box-sizing: inherit;
}
}
// Center
body {
min-height: 100vh;
display: flex;
font-family: 'Inter', Arial;
justify-content: center;
align-items: center;
.twitter {
position: fixed;
display: block;
right: 12px;
bottom: 12px;
svg {
width: 32px;
height: 32px;
fill: #fff;
}
}
}
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600&amp;display=swap" rel="stylesheet" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment