console
const DOM = {
class: {
add: (element, name, delay) => element.classList.add(name),
get: (element, name) => element.getElementsByClassName(name),
has: (element, name) => element.classList.contains(name),
remove: (element, name) => element.classList.remove(name),
transition: (element, name, duration) => {
if(DOM.class.has(element, name)) {
DOM.class.remove(element, name);
}
DOM.class.add(element, name);
setTimeout(() => DOM.class.remove(element, name), duration);
}
},
get: id => document.getElementById(id)
}
const N = {
clamp: (min, value, max) => Math.min(Math.max(min, value), max)
}
const T = {
range: (value, percent) => {
if(percent >= 0.5) {
const p = (percent - 0.5) / 0.5;
return -1 * value * p;
}
const p = (0.5 - percent) / 0.5;
return value * p;
}
}
const display = DOM.get("card-display");
const card1 = DOM.get("card-1"),
card2 = DOM.get("card-2"),
card3 = DOM.get("card-3");
display.onmousemove = e => {
const rect = display.getBoundingClientRect();
const x = e.clientX - rect.left,
y = e.clientY - rect.top;
const xPercent = x / display.clientWidth,
yPercent = y / display.clientHeight;
const xDeg = T.range(-10, yPercent),
yDeg = T.range(10, xPercent);
display.style.transform = `perspective(1200px) rotateX(${xDeg}deg) rotateY(${yDeg}deg)`;
};
display.onmouseleave = e => {
DOM.class.transition(display, "transition", 500);
display.style.transform = `perspective(1200px) rotateX(0deg) rotateY(0deg)`;
};
const moveChef = (e, element) => {
const rect = element.getBoundingClientRect(),
chef = DOM.class.get(element, "card-chef")[0];
const x = e.clientX - rect.left,
y = e.clientY - rect.top;
const percent = x / element.clientWidth;
const half = chef.clientWidth / 2,
value = (element.clientWidth * percent) - half,
max = element.clientWidth - (chef.clientWidth * 1.5);
const tX = N.clamp(half, value, max);
chef.style.transform = `translateX(${tX}px)`;
}
const releaseCard = element => {
if(!DOM.class.has(element, "selected") && !DOM.class.has(element, "chef-reappear")) {
DOM.class.transition(element, "selected", 1000);
setTimeout(() => DOM.class.transition(element, "chef-reappear", 250), 1000);
}
}
card1.onmousemove = e => moveChef(e, card1);
card1.onmouseup = e => releaseCard(card1);
card2.onmousemove = e => moveChef(e, card2);
card2.onmouseup = e => releaseCard(card2);
card3.onmousemove = e => moveChef(e, card3);
card3.onmouseup = e => releaseCard(card3);
<script src="https://kit.fontawesome.com/86933bf68b.js"></script>
<div id="card-display-background" class="background-image"></div>
<div id="card-display-wrapper">
<div id="card-display">
<button id="card-1" class="card-wrapper" type="button">
<div class="card">
<div class="card-svgs">
<svg class="svg svg-left" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
<path fill="#F2F4F8" d="M28.2,-44.5C36.5,-38.6,43,-30.6,41.6,-22.4C40.1,-14.1,30.6,-5.5,27.8,2.9C24.9,11.4,28.6,19.7,28.5,30.9C28.5,42.1,24.5,56.1,18.3,54.3C12.1,52.4,3.6,34.7,-7.9,29.9C-19.3,25.2,-33.7,33.4,-41.9,31.9C-50,30.4,-51.8,19,-54,7.5C-56.1,-4.1,-58.4,-15.9,-56.9,-28.7C-55.3,-41.5,-49.9,-55.3,-39.7,-60.3C-29.6,-65.4,-14.8,-61.6,-2.4,-57.9C10,-54.1,20,-50.3,28.2,-44.5Z" transform="translate(100 100)" />
</svg>
<svg class="svg svg-right" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
<path fill="#F2F4F8" d="M26.4,-42.2C35.7,-35.1,45.9,-30.3,57.4,-21.4C69,-12.5,82,0.6,83.7,14.4C85.4,28.2,75.8,42.8,63.3,52C50.9,61.3,35.6,65.2,21.7,65.3C7.8,65.4,-4.6,61.7,-12.2,53.9C-19.8,46,-22.7,34.1,-29.7,25.8C-36.7,17.5,-47.9,13,-50,6.4C-52.1,-0.1,-45.2,-8.8,-40.7,-18.6C-36.3,-28.5,-34.4,-39.6,-28.1,-48C-21.7,-56.4,-10.8,-62.2,-1.2,-60.4C8.5,-58.6,17.1,-49.2,26.4,-42.2Z" transform="translate(100 100)" />
</svg>
</div>
<span class="card-amount roboto-mono">$25</span>
<i class="card-brand fa-solid fa-fork-knife"></i>
<i class="card-icon fa-duotone fa-ice-cream"></i>
</div>
<div class="card-chef">
<i class="fa-solid fa-user-chef"></i>
<i class="fa-regular fa-fire"></i>
</div>
</button>
<button id="card-2" class="card-wrapper" type="button">
<div class="card">
<div class="card-svgs">
<svg class="svg svg-left" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
<path fill="#F2F4F8" d="M38.4,-55.8C50.9,-51.7,63,-43,61.2,-32.2C59.4,-21.5,43.8,-8.7,41.7,6C39.7,20.8,51.1,37.5,48.4,43.5C45.7,49.6,28.8,45,15.7,44.9C2.6,44.9,-6.6,49.4,-20.9,53.4C-35.2,57.3,-54.5,60.7,-61.1,53C-67.6,45.3,-61.4,26.6,-60.3,10.8C-59.3,-5,-63.4,-17.9,-57.3,-23.9C-51.1,-30,-34.6,-29.1,-23.3,-33.7C-12.1,-38.4,-6,-48.7,3.5,-54.1C12.9,-59.4,25.9,-59.9,38.4,-55.8Z" transform="translate(100 100)" />
</svg>
<svg class="svg svg-right" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
<path fill="#F2F4F8" d="M26.7,-42.6C39.8,-33.1,59.2,-34.5,70.1,-27.2C81,-19.8,83.4,-3.7,73.2,4.8C63.1,13.3,40.4,14.3,28.2,19.4C16,24.4,14.2,33.5,9.2,37.9C4.2,42.4,-4,42.2,-10.3,38.7C-16.6,35.2,-20.8,28.5,-32.1,23.4C-43.3,18.3,-61.5,14.9,-61.1,9.5C-60.8,4.2,-41.8,-3.1,-31.7,-10C-21.6,-16.9,-20.3,-23.3,-16.5,-37C-12.7,-50.8,-6.4,-72,0.2,-72.3C6.8,-72.6,13.6,-52.2,26.7,-42.6Z" transform="translate(100 100)" />
</svg>
</div>
<span class="card-amount roboto-mono">$100</span>
<i class="card-brand fa-solid fa-fork-knife"></i>
<i class="card-icon fa-duotone fa-burger-soda"></i>
</div>
<div class="card-chef">
<i class="fa-solid fa-user-chef"></i>
<i class="fa-regular fa-fire"></i>
</div>
</button>
<button id="card-3" class="card-wrapper" type="button">
<div class="card">
<div class="card-svgs">
<svg class="svg svg-left" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
<path fill="#F2F4F8" d="M14.8,-26.2C19.7,-19.9,24.6,-16.7,34.9,-11C45.3,-5.3,61.1,2.9,61.1,9.4C61,15.8,45.1,20.5,35.9,31.1C26.7,41.7,24.3,58.2,15.9,66C7.5,73.9,-6.8,73,-18.8,67.8C-30.7,62.5,-40.2,52.8,-45.9,42.1C-51.7,31.4,-53.7,19.7,-48.8,10.9C-44,2.2,-32.3,-3.6,-29.9,-15.5C-27.4,-27.4,-34.1,-45.4,-30.6,-52.4C-27.2,-59.3,-13.6,-55.1,-4.3,-48.4C5,-41.7,9.9,-32.4,14.8,-26.2Z" transform="translate(100 100)" />
</svg>
<svg class="svg svg-right" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
<path fill="#F2F4F8" d="M16.5,-31.8C25.4,-20,39.3,-22.2,53.7,-16.8C68.1,-11.5,83,1.3,79.3,9.8C75.6,18.3,53.2,22.5,39.4,28.9C25.6,35.3,20.5,43.9,12.9,48.1C5.2,52.4,-5,52.2,-19,53.3C-33,54.4,-50.8,56.9,-58.1,49.7C-65.3,42.4,-62.2,25.4,-57.1,12.7C-52,-0.1,-44.9,-8.7,-43,-22.5C-41.1,-36.2,-44.3,-55.1,-38,-68C-31.7,-80.9,-15.9,-87.8,-6,-78.5C3.8,-69.1,7.7,-43.5,16.5,-31.8Z" transform="translate(100 100)" />
</svg>
</div>
<span class="card-amount roboto-mono">$50</span>
<i class="card-brand fa-solid fa-fork-knife"></i>
<i class="card-icon fa-duotone fa-donut"></i>
</div>
<div class="card-chef">
<i class="fa-solid fa-user-chef"></i>
<i class="fa-regular fa-fire"></i>
</div>
</button>
</div>
</div>
@keyframes reappear {
from {
top: 0px;
}
to {
top: -50px;
}
}
html,
body {
background-color: #1e1e1e;
height: 100vh;
margin: 0px;
padding: 0px;
width: 100vw;
}
.roboto-mono {
font-family: "Roboto Mono", monospace;
}
body {
align-items: center;
display: flex;
justify-content: center;
overflow: hidden;
}
body .background-image {
background-position: center;
background-repeat: no-repeat;
background-size: cover;
}
body #card-display-background {
background-image: url("https://images.unsplash.com/photo-1555992336-fb0d29498b13?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1064&q=80");
height: 100%;
left: 50%;
max-width: 1800px;
opacity: 0.25;
position: absolute;
transform: translateX(-50%);
width: 100%;
}
body #card-display-background:after, body #card-display-background:before {
content: "";
height: inherit;
left: 0px;
position: absolute;
top: 0px;
width: inherit;
z-index: 1;
}
body #card-display-background:after {
background: linear-gradient(to top, #1e1e1e, transparent, #1e1e1e);
}
body #card-display-background:before {
background: linear-gradient(to right, #1e1e1e 5%, transparent 20%, transparent 80%, #1e1e1e 95%);
}
body #card-display-wrapper {
position: relative;
transition: transform 250ms;
z-index: 2;
}
body #card-display-wrapper #card-display {
display: flex;
gap: 20px;
position: relative;
z-index: 2;
}
body #card-display-wrapper #card-display.transition {
transition: transform 500ms;
}
body #card-display-wrapper #card-display .card-wrapper {
background-color: transparent;
border: none;
border-radius: 15px;
outline: none;
padding: 0px;
position: relative;
transition: transform 250ms;
}
body #card-display-wrapper #card-display .card-wrapper:first-of-type {
transform: scale(0.85);
}
body #card-display-wrapper #card-display .card-wrapper:first-of-type .card {
background-color: #ef5350;
}
body #card-display-wrapper #card-display .card-wrapper:nth-of-type(2) {
transform: scale(0.95);
}
body #card-display-wrapper #card-display .card-wrapper:last-of-type {
transform: scale(0.85);
}
body #card-display-wrapper #card-display .card-wrapper:last-of-type .card {
background-color: #29b6f6;
}
body #card-display-wrapper #card-display .card-wrapper:hover {
transform: scale(1);
}
body #card-display-wrapper #card-display .card-wrapper:hover .card .card-svgs .svg-left {
transform: translate(-30%, -30%);
}
body #card-display-wrapper #card-display .card-wrapper:hover .card .card-svgs .svg-right {
transform: translate(30%, -30%);
}
body #card-display-wrapper #card-display .card-wrapper:hover .card .card-icon {
transform: translate(-20px, 20%) scale(1.2) rotate(-10deg);
}
body #card-display-wrapper #card-display .card-wrapper:hover .card-chef {
opacity: 1;
top: -50px;
}
body #card-display-wrapper #card-display .card-wrapper:active {
transform: scale(0.95);
}
body #card-display-wrapper #card-display .card-wrapper:active .card .card-svgs .svg-left {
transform: translate(-30%, -30%) scale(1.2);
}
body #card-display-wrapper #card-display .card-wrapper:active .card .card-svgs .svg-right {
transform: translate(30%, -30%) scale(1.2);
}
body #card-display-wrapper #card-display .card-wrapper:active .card .card-icon {
transform: translate(-20px, 20%) scale(1.4) rotate(-10deg);
}
body #card-display-wrapper #card-display .card-wrapper.selected .card-chef {
opacity: 0;
top: -500px;
transition: opacity 500ms, top 500ms;
}
body #card-display-wrapper #card-display .card-wrapper.chef-reappear .card-chef {
animation: reappear 250ms ease-in;
}
body #card-display-wrapper #card-display .card-wrapper:not(.chef-reappear, .selected):active .card-chef {
top: -20px;
}
body #card-display-wrapper #card-display .card-wrapper .card {
background-color: #141414;
border-radius: inherit;
box-shadow: 0px 0px 40px 10px rgba(0, 0, 0, 0.3);
cursor: pointer;
height: 300px;
position: relative;
width: 485px;
z-index: 2;
}
body #card-display-wrapper #card-display .card-wrapper .card .card-svgs {
border-radius: inherit;
height: 100%;
left: 0px;
overflow: hidden;
position: absolute;
top: 0px;
width: 100%;
z-index: 2;
}
body #card-display-wrapper #card-display .card-wrapper .card .card-svgs .svg {
opacity: 0.1;
position: absolute;
transition: transform 250ms;
}
body #card-display-wrapper #card-display .card-wrapper .card .card-svgs .svg-left {
left: 0px;
top: 50%;
transform: translate(-25%, -25%);
}
body #card-display-wrapper #card-display .card-wrapper .card .card-svgs .svg-right {
right: 0px;
top: -50%;
transform: translate(25%, -25%);
}
body #card-display-wrapper #card-display .card-wrapper .card .card-amount {
bottom: 0px;
color: white;
font-size: 5em;
left: 0px;
position: absolute;
transform: translate(20px, -20px);
}
body #card-display-wrapper #card-display .card-wrapper .card .card-brand {
color: white;
font-size: 2.5em;
position: absolute;
right: 0px;
top: 0px;
transform: translate(-30px, 30px);
transition: transform 250ms;
}
body #card-display-wrapper #card-display .card-wrapper .card .card-icon {
bottom: 0px;
color: white;
font-size: 10em;
position: absolute;
right: 0px;
transform: translate(-20px, 20%);
transition: transform 250ms;
}
body #card-display-wrapper #card-display .card-wrapper .card-chef {
left: 0px;
opacity: 0;
position: absolute;
top: 0px;
transition: opacity 250ms, top 250ms;
z-index: 1;
}
body #card-display-wrapper #card-display .card-wrapper .card-chef i {
color: white;
text-align: center;
}
body #card-display-wrapper #card-display .card-wrapper .card-chef .fa-user-chef {
color: white;
font-size: 3.5em;
height: 60px;
line-height: 60px;
width: 50px;
}
body #card-display-wrapper #card-display .card-wrapper .card-chef .fa-fire {
bottom: 0px;
font-size: 2em;
left: 50%;
position: absolute;
transform: rotate(180deg) translate(50%, -90%);
}