炫酷3D卡片翻转动画效果(附源代码)

[复制链接]
七夏(UID:1) 发表于 2024-10-8 11:51:35 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×

图片

这是一个精美的3D卡片翻转动画效果,具有翻转动画和全息效果。这个效果综合运用了现代CSS和JavaScript技术,创造出一个视觉上非常吸引人的3D卡片,适合用于展示特殊活动或VIP通行证等场景。

以下是核心实现原理和主要效果:

核心实现原理:

  1. 3D变换:

    • 使用CSS的 transform-style: preserve-3dperspective 属性创建3D空间。
    • 使用 rotateY 实现卡片的翻转效果。
  2. CSS变量:

  • 大量使用CSS变量(如 --r, --o, --h, --p)来控制动画和样式。
  1. GSAP动画库:
  • 使用GSAP (GreenSock Animation Platform) 创建复杂的动画时间线。
  • 控制卡片的旋转、透明度和全息效果的变化。
  1. 蒙版效果:
  • 使用 mask-image 属性创建票据形状和全息logo效果。
  1. 渐变和混合模式:
  • 使用复杂的渐变和 mix-blend-mode 创建全息效果。

主要效果:

  1. 卡片翻转:卡片在正面和背面之间平滑翻转,展示不同的信息。
  2. 全息效果:卡片上有一个彩虹色的全息效果,随着卡片旋转而变化。
  3. 光泽效果:卡片表面有一个模拟光泽的动画效果,增强3D感。
  4. 响应式设计:使用 vmin 和计算的像素单位 --px 使卡片在不同屏幕尺寸上保持合适的大小。
  5. 鼠标交互:当鼠标悬停在卡片上时,动画暂停;移开时,动画继续。
  6. 背景动画:背景有一个缓慢移动的渐变效果,增加深度感。

创作来源:Gambhir Sharma / Supabase Ticket

使用方式

复制源代码到空白的html格式文件,在浏览中打开运行即可。

源代码

可上下滑动查看完整源代码:

<!DOCTYPE html>
<html lang="en">
<head>
   <meta name="viewport" content="width=device-width, initial-scale=1">
<style>
  html {
  --zoom: 120;
  --green: #37996b;
  --neon: #3ecf8e;
}
:root {
  --unit: 1vmin;
  --available-screen-min: 665;
  --px: calc(var(--zoom) * (var(--unit) / var(--available-screen-min)));
  --bg: #060809;
  --logopng: url(https://res.cloudinary.com/dpphcu4gm/image/upload/v1712993492/supabase-outline-logo_u83xos.png);
  --ticket: url(https://assets.codepen.io/13471/ticket-shape.svg);
  --ar: 10/30;
  --gutter: 8%;
}
* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  font-family: Arial, Helvetica, sans-serif;
  font-size: 16px;
}
*::after,
*::before {
  content: "";
  display: block;
  position: relative;
  box-sizing: border-box;
}
head::before,
head::after,
body::before,
body::after,
html::before,
html::after {
  content: "";
  position: absolute;
  background-repeat: no-repeat;
  box-sizing: border-box;
  filter: blur(0);
}
body {
  all: unset;
  background-color: var(--bg);
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
}
a {
  all: unset;
}

#app {
  perspective: 1200px;
  --o: 0;
  --p: 100%;
  --h: 50%;
  --r: 0;
  transform: translate3d(0, 0, 0.1px);
}

.ticket {
  --scale: 1;
  transform: translate3d(0, 0, 0.1px) scale(var(--scale)) rotateY(var(--r));
  transform-style: preserve-3d;
  pointer-events: auto;
}

.front,
.back {
  grid-area: 1/1;
  background-color: #6e6176;
  background-image: radial-gradient(
    circle at var(--p) 50%,
    #111 10%,
    transparent 100%
  );
  background-size: 100% 220vh;
  background-position: center;
  background-repeat: no-repeat;
  border-radius: 15px;
  display: grid;
  backface-visibility: visible;
  transform: translateZ(1px);
  transform-style: preserve-3d;
  mask-image: var(--ticket);
  mask-size: cover;
  mask-repeat: no-repeat;
  height: calc(420 * var(--px));
}
.cutout {
  position: absolute;
}

.front::after,
.back::after {
  content: "";
  position: absolute;
  inset: 0;
  background-image: linear-gradient(
    -70deg,
    transparent 40%,
    rgba(255, 255, 255, 0.5) 40.5%,
    transparent
  );
  background-size: 200% 200%;
  background-position: var(--p) var(--p);
  z-index: 5;
  opacity: calc(var(--o) + 0.5);
  pointer-events: none;
}

.front {
  transform: rotateY(180deg) translateZ(1px);
}
.back {
  padding: calc(20 * var(--px));
}

.holo {
  display: block;
  position: absolute;
  inset: 0;
  border-radius: 15px;
}

.holo {
  --space: 5%;
  --red: hsl(0, 100%, 50%);
  --orange: hsl(30, 100%, 50%);
  --yellow: hsl(60, 100%, 50%);
  --green: hsl(120, 100%, 50%);
  --cyan: hsl(180, 100%, 50%);
  --blue: hsl(222, 100%, 50%);
  --purple: hsl(258, 100%, 50%);
  --magenta: hsl(300, 100%, 50%);
  background-image: repeating-linear-gradient(
    -45deg,
    var(--red) 0%,
    var(--orange) calc(var(--space) * 1),
    var(--yellow) calc(var(--space) * 2),
    var(--green) calc(var(--space) * 3),
    var(--cyan) calc(var(--space) * 4),
    var(--blue) calc(var(--space) * 5),
    var(--purple) calc(var(--space) * 6),
    var(--magenta) calc(var(--space) * 7),
    var(--red) calc(var(--space) * 8)
  );
  background-size: 150vw 150vh;
  background-position: calc(var(--h)) calc(var(--h));
  background-repeat: no-repeat;
  mask-image: var(--logopng);
  mask-size: 80% 80%;
  mask-repeat: no-repeat;
  mask-position: 150% 180%;
  mix-blend-mode: plus-lighter;
  filter: brightness(0.9) contrast(0.7) saturate(2);
  opacity: var(--o);
}

.logo {
  width: 50%;
  place-self: center;
  transform: translateY(-14%);
}

.divider {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: start;
  bottom: 2%;
  left: 0;
  right: 0;
  height: 18%;
  padding: 0 var(--gutter);
  text-transform: uppercase;

  background-image: repeating-linear-gradient(
      90deg,
      #fff0 0px,
      #fff0 8px,
      var(--green) 8px,
      var(--green) 16px
    ),
    radial-gradient(ellipse at center center, #fff0 10%, transparent 50%);
  background-size: 100% 1.5px, 250% 1.5px;
  background-repeat: no-repeat;
  background-position: -4px top, var(--h) top;
  background-blend-mode: overlay;

  font-size: 16px;
  font-weight: 400;
  z-index: 2;
}

.divider  div {
    display: flex;
    align-items: center;
    justify-content: left;
  }

.divider > div > img {
  margin-right: 10px;
  height: 40px;
}
#app {
  color: #fff;
  /* font-family: "Roboto Mono", monospace; */
  display: grid;
  grid: 1fr/1fr;
  place-content: center;
  overflow: hidden;
  padding: 50px;
  z-index: 999;
  height: 90%;
}

.ticket {
  display: grid;
  grid-area: 1/1;
  width: calc(300 * var(--px));
  height: calc(400 * var(--px));
  aspect-ratio: var(--ar);
}

@media screen and (max-width: 400px) {
  .ticket {
    --scale: 0.75;
  }
}

#id_number {
  position: absolute;
  margin: calc(40 * var(--px)) calc(20 * var(--px));
}

.data {
  position: absolute;
  top: calc(70 * var(--px));
  margin: calc(20 * var(--px));
}
.name {
  font-size: calc(30 * var(--px));
}
.githubid {
  font-size: calc(20 * var(--px));
}
h3 {
  font-size: calc(15 * var(--px));
}

</style>

</head>

<body>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.11.4/gsap.min.js"></script>
<main id="app">
  <section class="ticket">
    <header class="front">
      <div class="holo"></div>
      <img class="logo" src="https://res.cloudinary.com/dpphcu4gm/image/upload/v1712992262/full-logo_bdbltu.png" alt="" />
      <aside class="divider"></aside>
    </header>
    <section class="back">
      <div class="holo"></div>
      <p id="id_number">NO 00004521</p>
      <div class="data">
        <p class="name">gambhir⚡</p>
        <p></p>
      </div>
      <aside class="divider">
        <div>
          <img src="https://res.cloudinary.com/dpphcu4gm/image/upload/v1712992245/logo_ycu0zf.svg" alt="" />
          <h3>April 15-19 / 7AM PT</h2>
        </div>
      </aside>
    </section>
  </section>
</main>
</body>

<script>
  const speed = 7;
const r = gsap.timeline({ repeat: -1 });
const o = gsap.timeline({ repeat: -1 });
const h = gsap.timeline({ repeat: -1 });

const $ticket = document.querySelector(".ticket");
$ticket.addEventListener("mouseenter", () => {
  r.pause();
  o.pause();
  h.pause();
});
$ticket.addEventListener("mouseleave", () => {
  r.play();
  o.play();
  h.play();
});

r.to("#app", {
  "--r": "180deg",
  "--p": "0%",
  duration: speed,
  ease: "sine.in"
});
r.to("#app", {
  "--r": "360deg",
  "--p": "100%",
  duration: speed,
  ease: "sine.out"
});
o.to("#app", {
  "--o": 1,
  duration: speed / 2,
  ease: "power1.in"
});
o.to("#app", {
  "--o": 0,
  duration: speed / 2,
  ease: "power1.out"
});

h.to("#app", {
  "--h": "100%",
  duration: speed / 2,
  ease: "sine.in"
});
h.to("#app", {
  "--h": "50%",
  duration: speed / 2,
  ease: "sine.out"
});
h.to("#app", {
  "--h": "0%",
  duration: speed / 2,
  ease: "sine.in"
});
h.to("#app", {
  "--h": "50%",
  duration: speed / 2,
  ease: "sine.out"
});
</script>
</html>
小时候,看腻了农村的牛和马,长大后,来到了城里,才知道原来到处都是牛马!
全部回复0 显示全部楼层
暂无回复,精彩从你开始!

快速回帖

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关于楼主

管理员
  • 主题

    710
  • 回答

    248
  • 积分

    1903
虚位以待,此位置招租

商务推广

    网盘拉新-短剧推广 此位置招租 此位置招租 此位置招租 此位置招租 此位置招租 此位置招租 此位置招租 此位置招租 此位置招租