.button {
--background: #275EFE;
--background-hover: #2456E8;
--text: #fff;
--icon: #fff;
display: flex;
outline: none;
cursor: pointer;
border: 0;
min-width: 113px;
padding: 8px 20px 8px 12px;
border-radius: 9px;
line-height: 24px;
font-family: inherit;
font-weight: 600;
font-size: 14px;
color: var(--text);
background: var(--b, var(--background));
transition: transform .3s, background .4s;
transform: scale(var(--scale, 1)) translateZ(0);
&:active {
--scale: .95;
}
&:hover {
--b: var(--background-hover);
}
.icon {
--arrow-y: 0;
--arrow-rotate: 45;
width: 24px;
height: 24px;
position: relative;
display: inline-block;
vertical-align: top;
margin-right: 8px;
pointer-events: none;
.arrow,
.line {
position: absolute;
}
.arrow {
left: 11px;
top: 4px;
width: 2px;
height: 12px;
border-radius: 1px;
background: var(--icon);
transform: translateY(calc(var(--arrow-y) * 1px)) translateZ(0);
&:before,
&:after {
content: '';
width: 2px;
height: 7px;
position: absolute;
left: 0;
top: 0;
border-radius: 1px;
background: inherit;
transform-origin: 1px 1px;
transform: rotate(var(--r, calc(var(--arrow-rotate) * -1deg)));
}
&:after {
--r: calc(var(--arrow-rotate) * 1deg);
}
}
.line {
width: 24px;
height: 24px;
display: block;
left: 0;
top: 7px;
fill: none;
stroke: var(--icon);
stroke-width: 2;
stroke-linejoin: round;
stroke-linecap: round;
}
}
}
html {
box-sizing: border-box;
-webkit-font-smoothing: antialiased;
}
* {
box-sizing: inherit;
&:before,
&:after {
box-sizing: inherit;
}
}
// Center & dribbble
body {
min-height: 100vh;
display: flex;
font-family: 'Inter', Arial;
justify-content: center;
align-items: center;
background: #ECEFFC;
.dribbble {
position: fixed;
display: block;
right: 20px;
bottom: 20px;
img {
display: block;
height: 28px;
}
}
.twitter {
position: fixed;
display: block;
right: 64px;
bottom: 14px;
svg {
width: 32px;
height: 32px;
fill: #1da1f2;
}
}
}
View Compiled
const $ = (s, o = document) => o.querySelector(s);
const $$ = (s, o = document) => o.querySelectorAll(s);
$$('.button').forEach(button => {
let icon = $('.icon', button),
line = $('.line', icon),
svgPath = new Proxy({
y: null
}, {
set(target, key, value) {
target[key] = value;
if(target.y !== null) {
line.innerHTML = getPath(target.y, .25, null);
}
return true;
},
get(target, key) {
return target[key];
}
}),
timeline = gsap.timeline({
paused: true
}),
interval;
svgPath.y = 12;
timeline.to(icon, {
'--arrow-y': 6,
'--arrow-rotate': 70,
ease: "elastic.in(1.1, .8)",
duration: .7
}).to(icon, {
'--arrow-y': 0,
'--arrow-rotate': 45,
ease: "elastic.out(1.1, .8)",
duration: .7
});
timeline.to(svgPath, {
y: 15,
duration: .15
}, .65).to(svgPath, {
y: 12,
ease: "elastic.out(1.2, .7)",
duration: .6
}, .8);
button.addEventListener('mouseover', e => {
timeline.restart();
interval = setInterval(() => timeline.restart(), 1500);
});
button.addEventListener('mouseout', e => clearInterval(interval));
});
function getPoint(point, i, a, smoothing) {
let cp = (current, previous, next, reverse) => {
let p = previous || current,
n = next || current,
o = {
length: Math.sqrt(Math.pow(n[0] - p[0], 2) + Math.pow(n[1] - p[1], 2)),
angle: Math.atan2(n[1] - p[1], n[0] - p[0])
},
angle = o.angle + (reverse ? Math.PI : 0),
length = o.length * smoothing;
return [current[0] + Math.cos(angle) * length, current[1] + Math.sin(angle) * length];
},
cps = cp(a[i - 1], a[i - 2], point, false),
cpe = cp(point, a[i - 1], a[i + 1], true);
return `C ${cps[0]},${cps[1]} ${cpe[0]},${cpe[1]} ${point[0]},${point[1]}`;
}
function getPath(update, smoothing, pointsNew) {
let points = pointsNew ? pointsNew : [
[5, 12],
[12, update],
[19, 12]
],
d = points.reduce((acc, point, i, a) => i === 0 ? `M ${point[0]},${point[1]}` : `${acc} ${getPoint(point, i, a, smoothing)}`, '');
return `<path d="${d}" />`;
}