Skip to content

Instantly share code, notes, and snippets.

@hakimel
Created May 12, 2023 07:44
Show Gist options
  • Save hakimel/8d5925e7447beebf2d6e167a21304e41 to your computer and use it in GitHub Desktop.
Save hakimel/8d5925e7447beebf2d6e167a21304e41 to your computer and use it in GitHub Desktop.
<html>
<head>
<title>Click me if you can</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
html, body {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
margin: 0;
font-family: sans-serif;
font-size: 18px;
}
body {
max-width: 400px;
padding: 1rem;
box-sizing: border-box;
}
button {
display: block;
padding: 0.75rem 1.25rem;
border: 0;
border-radius: 4px;
background-color: hsl(218deg 92% 57%);
color: #fff;
user-select: none;
font-size: 1rem;
transform-style: preserve-3d;
}
button:before,
button:after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
border-radius: 4px;
background-color: hsl(218deg 92% 35%);
transform: translateZ( -5px );
}
button:after {
background-color: hsl(218deg 92% 20%);
transform: translateZ( -10px );
}
.button-wrapper {
position: relative;
perspective: 400px;
align-self: flex-start;
}
hgroup {
width: 100%;
margin: 0 0 2rem 0;
}
h1 {
font-size: 1.5rem;
font-weight: 600;
margin: 0 0 1rem 0;
}
h3 {
font-size: 1rem;
font-weight: 500;
margin: 0 0 0.5rem 0;
display: flex;
justify-content: space-between;
align-items: center;
color: #888;
}
a {
color: inherit;
text-decoration: none;
}
</style>
</head>
<body>
<hgroup>
<h1>Click below to unsubscribe from our newsletter</h1>
<h3><a href="https://twitter.com/hakimel" tabindex="-1">— @hakimel</a></h3>
</hgroup>
<div class="button-wrapper">
<button>Unsubscribe</button>
</div>
<script>
const button = document.querySelector( 'button' );
const distanceBetween = ( p1x, p1y, p2x, p2y ) => {
const dx = p1x-p2x;
const dy = p1y-p2y;
return Math.sqrt( dx*dx + dy*dy );
};
document.addEventListener( 'mousemove', event => {
const radius = Math.max( button.offsetWidth*0.75, button.offsetHeight*0.75, 100 );
const bx = button.parentNode.offsetLeft + button.offsetLeft + button.offsetWidth/2;
const by = button.parentNode.offsetTop + button.offsetTop + button.offsetHeight/2;
const dist = distanceBetween( event.clientX, event.clientY, bx, by );
const angle = Math.atan2( event.clientY - by, event.clientX - bx );
const ox = -1 * Math.cos( angle ) * Math.max( ( radius - dist ), 0 );
const oy = -1 * Math.sin( angle ) * Math.max( ( radius - dist ), 0 );
const rx = oy / 2;
const ry = -ox / 2;
button.style.transition = `all 0.1s ease`;
button.style.transform = `translate(${ox}px, ${oy}px) rotateX(${rx}deg) rotateY(${ry}deg)`;
button.style.boxShadow = `0px ${Math.abs(oy)}px ${Math.abs(oy)/radius*40}px rgba(0,0,0,0.15)`;
} );
const nocheat = () => button.textContent = 'No cheating 🙅‍♂️';
const notouch = () => button.textContent = 'This thing doesn\'t work with touch 😢';
button.addEventListener( 'click', event => button.textContent = 'You win 🎉🙃' );
button.addEventListener( 'keydown', event => { event.preventDefault(); nocheat(); } );
button.click = nocheat;
if( navigator.userAgent.match( /Android|iPhone|iPad|iPod/i ) ) notouch();
window.addEventListener( 'touchstart', notouch );
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment