SOURCE

console 命令行工具 X clear

                    
>
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%);
}