SOURCE

console 命令行工具 X clear

                    
>
console
console.clear();

// Colored title
Splitting("h1[data-splitting]").forEach((split) => {
  console.log(split);
  split.chars.forEach((char) =>
    char.style.setProperty("--random", Math.random())
  );
});

const elApp = document.querySelector("#app");

const elButtons = Array.from(
  document.querySelectorAll(".section .nav"),
  (elButton) => {
    elButton.addEventListener("click", (e) => {
      elApp.dataset.state = "single";
      const elSection = elButton.closest(".section");

      flip(() => {
        delete document.querySelector("[data-active]")?.dataset.active;
        elSection.dataset.active = true;
      }, elSection);
    });
    return elButton;
  }
);

const elContents = Array.from(
  document.querySelectorAll(".section .content"),
  (elContent) => {
    elContent.addEventListener("click", (e) => {
      elApp.dataset.state = "grid";
      const elSection = elContent.closest(".section");

      flip(() => {
        delete document.querySelector("[data-active]")?.dataset.active;
      }, elSection);
    });
    return elContent;
  }
);

// some FLIPping stuff from last episode

const getRect = (el) => {
  return el.getBoundingClientRect();
};

/**
 * FLIP
 * F = First
 * L = Last
 * I = Invert
 * P = Play
 */
function flip(doSomething, firstEl, getLastEl = () => firstEl) {
  // First
  const firstRect = getRect(firstEl);

  requestAnimationFrame(() => {
    // (something that changes layout)
    doSomething();

    // Last
    let lastEl = getLastEl();
    const lastRect = getRect(lastEl);

    // Invert
    const dx = lastRect.x - firstRect.x;
    const dy = lastRect.y - firstRect.y;
    const dw = lastRect.width / firstRect.width;
    const dh = lastRect.height / firstRect.height;

    console.log({ dx, dy, dw, dh });

    // (so CSS knows it's being flipped)
    // data-flipping="true"
    lastEl.dataset.flipping = true;

    lastEl.style.setProperty("--dx", dx);
    lastEl.style.setProperty("--dy", dy);
    lastEl.style.setProperty("--dw", dw);
    lastEl.style.setProperty("--dh", dh);

    // Play
    // setTimeout(() => {
    //   delete lastEl.dataset.flipping;
    // }, 1000);
    requestAnimationFrame(() => {
      delete lastEl.dataset.flipping;
    });
  });
}

elButtons[0].click();
setTimeout(() => {
  elContents[0].click();
}, 1500);
<!--

  1. z-index
  2. FLIPing 
  3. 3D FLIP
  
-->

<div id="app" data-state="none">

  <h1 class="title color" data-splitting>Select File</h1>

  <div class="wrapper">
    <div class="section">
      <div class="inner">
        <button class="nav">1</button>
        <div class="content">1</div>
      </div>
    </div>
    <span class="color" data-splitting>New</span>
  </div>

  <div class="wrapper">
    <div class="section">
      <div class="inner">
        <button class="nav">2</button>
        <div class="content">2</div>
      </div>
    </div>
    <span class="color" data-splitting>New</span>
  </div>

  <div class="wrapper">
    <div class="section">
      <div class="inner">
        <button class="nav">3</button>
        <div class="content">3</div>
      </div>
    </div>
    <span class="color" data-splitting>New</span>
  </div>

  <div class="wrapper">
    <div class="section">
      <div class="inner">
        <button class="nav">4</button>
        <div class="content">4</div>
      </div>
    </div>
    <span class="color" data-splitting>New</span>
  </div>

</div>

<a href="https://youtu.be/VdZKGG19vmw" target="_blank" data-keyframers-credit style="color: #000"></a>
<script src="https://codepen.io/shshaw/pen/QmZYMG.js"></script>
@import url("https://fonts.googleapis.com/css2?family=Bangers&display=swap");

$gold: #d6ac0e;
$green: #259200;
$gray: #abb1ac;
$pink: #eb87a1;
$yellow: #efbb19;
$blue: #347bdf;

*,
*::before,
*::after {
  box-sizing: border-box;
  position: relative;
}

html {
  background: $gold;
  height: 100%;
  border: inset 5vmin rgba(darken($gold, 10%), 0.5);
  padding: 4vmin 8vmin;
  overflow: hidden;
}

body {
  height: 100%;
}

#app {
  height: 100%;
  display: grid;
  grid-template-columns: minmax(auto, 1fr) minmax(auto, 1fr);
  grid-template-rows: auto 1fr 1fr 10%;
  grid-gap: 2rem;
  justify-items: space-around;
  align-content: space-around;

  > h1 {
    grid-row: 1;
    grid-column: 1 / -1;
    text-align: center;
    margin: 0;
  }
}

/* ---------------------------------- */

.section {
  display: grid;
  z-index: 1;
  align-self: stretch;

  .nav {
    border: outset 1em lighten($gray, 10%);
    background: $gray;
    transition: border-width 1s;
    cursor: pointer;
  }

  .content {
    position: absolute;
    top: 0;
    left: 0;
    height: 100%;
    width: 100%;
    background: #fff;
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 5vmin;
    z-index: -1;
    cursor: zoom-out;
  }

  //   &:after {
  //     position: absolute;
  //     bottom: 100%;
  //     left: 0;
  //     font-family: monospace;
  //     content: attr(style);
  //     white-space: pre-wrap;
  //   }

  // FLIPPING!!
  transition: transform 1s cubic-bezier(0.2, 0, 0.5, 1), z-index 1s linear;
  transform-origin: top left;

  &[data-active] {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: 10;

    .content {
      z-index: 2;
    }
  }

  &[data-flipping] {
    transition: none;
    z-index: 10;
    transform: translateX(calc(-1px * var(--dx)))
      translateY(calc(-1px * var(--dy)))
      scale(calc(1 / var(--dw)), calc(1 / var(--dh)));

    .nav {
      border-width: calc(1em * var(--dw));
      transition: none;
    }
  }

  > .inner {
    height: 100%;
    width: 100%;
    transform-style: preserve-3d;
    perspective: 800px;
    transition: inherit;

    > .nav,
    > .content {
      transform-style: preserve-3d;
      height: 100%;
      width: 100%;
      backface-visibility: visible;
    }

    > .nav {
      transform: translateZ(1px);
    }

    > .content {
      transform: translateZ(-1px) rotateY(0.5turn);
    }
  }

  &[data-active][data-flipping] {
    > .inner {
      transition: none;
      transform: rotateY(0turn) rotateZ(0turn);
    }
  }

  // small state
  &:not([data-active])[data-flipping] {
    > .inner {
      transform: rotateY(0.5turn) rotateZ(1turn);
    }
  }

  // big state
  &[data-active]:not([data-flipping]) {
    > .inner {
      transform: rotateY(0.5turn) rotateZ(1turn);
    }
  }
}

/* ---------------------------------- */

.wrapper {
  display: grid;
  grid-template-columns: 50% 1fr;
  align-items: center;
  grid-gap: 1rem;
}

.wrapper .color {
  grid-column: 2;
  display: block;
}

.section:hover + .color .char {
  animation: wiggle 0.3s linear alternate infinite;
  animation-delay: calc(-0.6s * var(--random));
  @keyframes wiggle {
    to {
      transform: rotate(calc((var(--random) - 0.5) * -9deg)) scale(1.2);
    }
  }
}

.color {
  color: $yellow;
  text-shadow: 1px 1px rgba(#000, 0.5), -1px -1px rgba(#fff, 0.5);
  transform: scaleX(1.3) skewX(10deg);
  transform-origin: 0 0;
  font-size: 6vw;
  letter-spacing: 0.05em;
  font-family: "Bangers", cursive;

  .char {
    transform: rotate(calc((var(--random) - 0.5) * 9deg));

    &::before {
      z-index: 1;
      color: transparent;
      visibility: visible;
      background: linear-gradient(to bottom, transparent 40%, #fff);
      background-size: cover;
      background-clip: text;
      text-shadow: none;
    }
  }

  .char:nth-child(3n + 2) {
    color: $pink;
  }

  .char:nth-child(3n + 1) {
    color: $green;
  }

  .char:nth-child(4n + 1) {
    color: $blue;
  }
}

.title {
  transform-origin: center center;
  font-size: 10vw;
}

本项目引用的自定义外部资源