Skip to content

Instantly share code, notes, and snippets.

@CodeMyUI

CodeMyUI/index.html

Created Jun 11, 2021
Embed
What would you like to do?
Unsubscribe game
<div id="unsubscribe">
<div class="letter">
<div class="shadow"></div>
<div class="background"></div>
<div class="body">
<div class="game idle">
<div class="headline">
<span>Win Ping Pong to unsubscribe</span>
<div class="close">
<svg viewBox="0 0 24 24">
<path d="M19.7,4.3c-0.4-0.4-1-0.4-1.4,0L12,10.6L5.7,4.3c-0.4-0.4-1-0.4-1.4,0s-0.4,1,0,1.4l6.3,6.3l-6.3,6.3 c-0.4,0.4-0.4,1,0,1.4C4.5,19.9,4.7,20,5,20s0.5-0.1,0.7-0.3l6.3-6.3l6.3,6.3c0.2,0.2,0.5,0.3,0.7,0.3s0.5-0.1,0.7-0.3 c0.4-0.4,0.4-1,0-1.4L13.4,12l6.3-6.3C20.1,5.3,20.1,4.7,19.7,4.3z"></path>
</svg>
</div>
</div>
<div class="paddle one"></div>
<div class="ball"></div>
<div class="paddle two"></div>
<div class="controls">
<span>Use <strong>up</strong> &amp; <strong>down</strong> arrow keys or</span>
<div>
<button class="up">
<svg viewBox="0 0 24 24">
<path d="M5.23 10.64a1 1 0 0 0 1.41.13L11 7.14V19a1 1 0 0 0 2 0V7.14l4.36 3.63a1 1 0 1 0 1.28-1.54l-6-5-.15-.09-.13-.07a1 1 0 0 0-.72 0l-.13.07-.15.09-6 5a1 1 0 0 0-.13 1.41z"></path>
</svg>
</button>
<button class="down">
<svg viewBox="0 0 24 24">
<path d="M18.77 13.36a1 1 0 0 0-1.41-.13L13 16.86V5a1 1 0 0 0-2 0v11.86l-4.36-3.63a1 1 0 1 0-1.28 1.54l6 5 .15.09.13.07a1 1 0 0 0 .72 0l.13-.07.15-.09 6-5a1 1 0 0 0 .13-1.41z">
</svg>
</button>
</div>
</div>
<div class="start">
<button>Start</button>
<small>or press up/down key</small>
</div>
</div>
</div>
</div>
<h1>Unsubscribe</h1>
<p>We are sorry to see you go!</p>
<div class="cta">
<button class="confirm">
Confirm
</button>
</div>
</div>
<!-- dribbble - twitter -->
<a class="dribbble" href="https://dribbble.com/ai" target="_blank"><img src="https://cdn.dribbble.com/assets/dribbble-ball-mark-2bd45f09c2fb58dbbfb44766d5d1d07c5a12972d602ef8b32204d28fa3dda554.svg" alt=""></a>
<a class="twitter" target="_blank" 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 $ = (s, o = document) => o.querySelector(s);
const $$ = (s, o = document) => o.querySelectorAll(s);
var unsubscribe = $('#unsubscribe'),
game = $('.game', unsubscribe),
confirmButton = $('.confirm', unsubscribe),
upButton = $('.controls .up', game),
downButton = $('.controls .down', game),
startButton = $('.start', game),
closeButton = $('.close', game);
var ball = {
elem: $('.ball', game),
x: 0,
y: 0,
top: 0,
left: 0
},
one = {
elem: $('.paddle.one', game),
y: 0,
top: 0,
score: 0
},
two = {
elem: $('.paddle.two', game),
y: 0,
score: 0
},
interval;
function init() {
if(game.classList.contains('idle')) {
one.y = game.offsetHeight / 2 - one.elem.offsetHeight / 2;
two.y = game.offsetHeight / 2 - one.elem.offsetHeight / 2;
start();
game.classList.remove('idle');
game.classList.add('init');
}
}
startButton.addEventListener('click', init);
confirmButton.addEventListener('click', e => {
unsubscribe.classList.add('show-game');
});
closeButton.addEventListener('click', e => {
unsubscribe.classList.add('hide-game');
unsubscribe.classList.remove('show-game');
setTimeout(() => unsubscribe.classList.remove('hide-game'), 800);
});
function start() {
ball.x = game.offsetWidth / 2 - ball.elem.offsetWidth / 2;
ball.y = game.offsetHeight / 2 - ball.elem.offsetHeight / 2;
ball.top = Math.random() * 2 + 2;
//ball.left = ((Math.random() < .5) ? 1 : -1) * (Math.random() * 2 + 2);
ball.left = (1 * Math.random() * 2 + 2);
interval = window.setInterval(render, 1000 / 60);
}
function render() {
one.y += one.top;
two.y = ball.y - two.elem.offsetHeight / 2;
ball.x += ball.left;
ball.y += ball.top;
if(one.y <= 0) {
one.y = 0;
}
if(two.y <= 0) {
two.y = 0;
}
if(one.y >= game.offsetHeight - one.elem.offsetHeight) {
one.y = game.offsetHeight - one.elem.offsetHeight;
}
if(two.y > game.offsetHeight - two.elem.offsetHeight) {
two.y = game.offsetHeight - two.elem.offsetHeight;
}
if(ball.y <= 0 || ball.y >= game.offsetHeight - ball.elem.offsetHeight) {
ball.top = -ball.top;
}
if(ball.x <= (one.elem.offsetWidth - 2)) {
if((ball.y + ball.elem.offsetHeight / 2 ) > one.y && (ball.y + ball.elem.offsetHeight / 2 ) < one.y + one.elem.offsetHeight) {
ball.left = -ball.left;
} else {
two.score++;
setTimeout(() => game.classList.add('idle'), 500);
clearInterval(interval);
//start();
}
}
if(ball.x >= game.offsetWidth - ball.elem.offsetWidth - (two.elem.offsetWidth - 2)) {
if((ball.y + ball.elem.offsetHeight / 2 ) > two.y && (ball.y + ball.elem.offsetHeight / 2 ) < two.y + two.elem.offsetHeight) {
ball.left = -ball.left;
} else {
one.score++
setTimeout(() => game.classList.add('idle'), 500);
clearInterval(interval);
//start();
}
}
one.elem.style.setProperty('--y', one.y + 'px');
two.elem.style.setProperty('--y', two.y + 'px');
ball.elem.style.setProperty('--x', ball.x + 'px');
ball.elem.style.setProperty('--y', ball.y + 'px');
}
document.addEventListener('keydown', e => {
e.preventDefault();
init();
if(e.keyCode == 38 || e.which == 38) {
one.top = -8;
}
if(e.keyCode == 40 || e.which == 40) {
one.top = 8;
}
}, false);
document.addEventListener('keyup', e => {
e.preventDefault();
init();
if(e.keyCode == 38 || e.which == 38) {
one.top = 0;
}
if(e.keyCode == 40 || e.which == 40) {
one.top = 0;
}
}, false);
upButton.onmousedown = e => {
init();
one.top = -8;
};
downButton.onmousedown = e => {
init();
one.top = 8;
};
upButton.onmouseup = e => {
one.top = 0;
};
downButton.onmouseup = e => {
one.top = 0;
};
upButton.ontouchstart = e => {
init();
one.top = -8;
};
downButton.ontouchstart = e => {
init();
one.top = 8;
};
upButton.ontouchend = e => {
one.top = 0;
};
downButton.ontouchend = e => {
one.top = 0;
};
:root {
--text-color: #646B8C;
--headline-color: #2B3044;
--mail: #555B77;
--mail-triangle: #494F69;
--mail-background: #404660;
--mail-shadow: #D1D6EE;
--paper: #fff;
--paper-border: #D1D6EE;
--confirm-color: #fff;
--confirm-background: #275EFE;
--game-paddle: #404660;
--game-ball: #275EFE;
--controls-text: #646B8C;
--controls-icon: #646B8C;
--controls-background: #E1E6F9;
}
#unsubscribe,
#game {
button {
outline: none;
border: none;
display: table;
margin: 0 auto;
font-size: 14px;
font-weight: 500;
font-family: inherit;
padding: 8px 20px;
line-height: 21px;
border-radius: 7px;
cursor: pointer;
-webkit-appearance: none;
-webkit-tap-highlight-color: transparent;
}
}
#unsubscribe {
.letter {
width: 84px;
height: 72px;
margin: 0 auto 32px auto;
position: relative;
animation: letter 2s ease infinite;
&:before,
&:after {
content: '';
position: absolute;
left: 0;
bottom: 0;
width: 100%;
height: 48px;
z-index: 1;
}
&:before {
background: var(--mail);
clip-path: polygon(0 0, 50% 55%, 100% 0, 100% 100%, 0 100%);
}
&:after {
background: var(--mail-triangle);
clip-path: polygon(0 100%, 50% 55%, 100% 100%);
}
.background {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: var(--mail-background);
clip-path: polygon(0 24px, 50% 0, 100% 24px, 100% 100%, 0 100%);
}
.shadow {
background: black;
width: 92px;
height: 4px;
border-radius: 50%;
position: absolute;
top: 108%;
left: -4px;
background: var(--mail-shadow);
animation: shadow 2s ease infinite;
}
.body {
width: 360px;
height: 260px;
bottom: 0;
left: -138px;
border-radius: 1px;
background: var(--paper);
box-shadow: inset 0 0 0 1px var(--paper-border);
position: absolute;
transform: translateY(36%) translateZ(0) scale(.2, .16) rotate(90deg);
.game {
width: 360px;
height: 260px;
position: relative;
transition: opacity .3s ease .8s;
.headline {
position: absolute;
left: 0;
right: 0;
top: -32px;
display: flex;
justify-content: space-between;
align-items: center;
transform: translateZ(0);
span {
color: var(--headline-color);
font-size: 16px;
font-weight: 600;
line-height: 24px;
}
.close {
cursor: pointer;
svg {
width: 20px;
height: 20px;
display: block;
fill: var(--text-color);
padding: 2px;
}
}
}
.paddle,
.ball {
top: 0;
position: absolute;
transition: opacity .3s;
transform: translate(var(--x, 0), var(--y, 0));
}
.paddle {
width: 6px;
height: 48px;
border-radius: 3px;
--y: 106px;
background: var(--game-paddle);
&.one {
left: 0;
}
&.two {
right: 0;
}
}
.ball {
background: var(--game-ball);
border-radius: 50%;
width: 16px;
height: 16px;
left: 0;
}
.controls {
bottom: -80px;
left: 0;
right: 0;
position: absolute;
span {
display: block;
text-align: center;
margin-bottom: 12px;
font-size: 14px;
font-weight: 500;
color: var(--controls-text);
}
div {
display: flex;
justify-content: center;
button {
width: 64px;
padding: 8px 0;
margin: 0;
background: var(--controls-background);
&:not(:last-child) {
margin-right: 16px;
}
svg {
width: 20px;
height: 20px;
display: block;
margin: 0 auto;
fill: var(--controls-icon);
}
}
}
}
.start {
position: absolute;
text-align: center;
white-space: nowrap;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
transition: opacity .3s;
button {
color: var(--confirm-color);
background: var(--confirm-background);
}
small {
margin: 4px 0 0 0;
display: block;
font-style: italic;
font-size: 12px;
color: var(--text-color);
}
}
&:not(.idle) {
.start {
opacity: 0;
pointer-events: none;
}
}
&:not(.init) {
.ball {
opacity: 0;
}
}
}
}
}
h1 {
text-align: center;
margin: 0 0 8px 0;
font-family: inherit;
font-weight: 600;
font-size: 24px;
color: var(--headline-color);
}
p {
text-align: center;
margin: 0;
font-size: 16px;
color: var(--text-color);
}
.cta {
margin-top: 32px;
button {
color: var(--confirm-color);
background: var(--confirm-background);
}
}
&:not(.show-game) {
.letter {
.body {
.game {
opacity: 0;
pointer-events: none;
transition-delay: 0s;
}
}
}
}
&.show-game {
.letter {
animation-play-state: paused;
.body {
animation: paper .8s linear forwards;
}
.shadow {
animation-play-state: paused;
}
}
}
&.hide-game {
.letter {
.body {
animation: paper-back .8s linear forwards;
}
}
}
}
@keyframes paper {
30% {
z-index: 0;
transform: translateY(18%) translateZ(0) scale(.2, .16) rotate(90deg);
}
60%,
100% {
z-index: 2;
}
60% {
transform: translateY(0) translateZ(0) scale(.2, .16) rotate(0deg);
}
100% {
transform: translateY(63%) translateZ(0);
}
}
@keyframes paper-back {
0% {
transform: translateY(63%) translateZ(0);
}
30% {
transform: translateY(0) translateZ(0) scale(.2, .16) rotate(0deg);
}
60% {
z-index: 0;
transform: translateY(18%) translateZ(0) scale(.2, .16) rotate(90deg);
}
0%,
30% {
z-index: 2;
}
100% {
transform: translateY(36%) translateZ(0) scale(.2, .16) rotate(90deg);
}
}
@keyframes letter {
50% {
transform: translateY(-4px);
}
}
@keyframes shadow {
50% {
opacity: .7;
transform: translateY(4px) scale(.8);
}
}
html {
box-sizing: border-box;
-webkit-font-smoothing: antialiased;
}
* {
box-sizing: inherit;
&:before,
&:after {
box-sizing: inherit;
}
}
// dribbble & twitter
body {
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
font-family: 'Inter', 'Inter UI', Arial;
background: #F2F5FF;
.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;
}
}
}
<link href="https://cdn.jsdelivr.net/npm/inter-ui@3.11.0/inter.min.css" rel="stylesheet" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment