当前位置: 首页 > article >正文

vue3 拆信封动画

snows_l's BLOGicon-default.png?t=O83Ahttp://snows-l.site/

一、效果如下

截图工具截图效果不是很好, 可以查看线上效果

 信封 | snows_l's BLOGicon-default.png?t=O83Ahttp://snows-l.site/about/like/envelope

二、源码如下

<!--
 * @Description: ------------ fileDescription -----------
 * @Author: snows_l snows_l@163.com
 * @Date: 2025-01-02 19:05:25
 * @LastEditors: snows_l snows_l@163.com
 * @LastEditTime: 2025-01-03 15:19:59
 * @FilePath: \BLOG\src\views\love\envelope\index.vue
-->
<template>
  <div class="envelope-warp">
    <div class="envelope-inner" :style="{ scale: isMobi ? 0.6 : 1 }" :class="{ 'is-flip': state.isFlip, 'no-flip': !state.isFlip }">
      <div class="back">
        <div class="flip-warp">
          <div class="latter-warp" :class="{ 'is-chou': state.isOpen }">
            <div class="latter-top" :style="{ transform: state.isOpen ? 'rotateX(180deg)' : 'rotateX(0deg)' }">
              <div class="latter-top-front"></div>
              <div class="latter-top-back">
                <div
                  :style="{ opacity: state.isOpen ? 1 : 0, '--c': state.isOpen ? '0.5s' : '0.3s' }"
                  class="close pointer iconfont icon-cc-close-crude"
                  @click="() => (state.isOpen = false)"></div>
              </div>
            </div>
            <div class="latter-inner" :class="{ 'is-transform': state.isOpen }" :style="{ '--b': state.isOpen ? '0.7s' : '0.7s' }">
              <div class="latter-bottom">
                <div class="header">Dear Lover:</div>
                <div class="latter-content">
                  <div class="latter-content-inner">
                    <div class="latter-content-item" v-for="(item, index) in state.contents" :key="index">{{ item }}</div>
                  </div>
                </div>
                <div class="footer">—— Your Lover</div>
              </div>
            </div>
          </div>
          <div class="bottom">
            <div class="flip-btn pointer" @click="handleFlip(true)">Flip</div>
          </div>

          <div class="top" :class="[state.isOpen ? 'is-open' : 'no-open']" :style="{ '--a': state.isOpen ? '0s' : '1.2s', 'z-index': state.isOpen ? '98' : '100' }">
            <div class="top-back"></div>
            <div class="top-front">
              <div class="yinzhang pointer" :style="{ 'background-position': state.isFirstOpen ? '0 100%' : '0 200%' }" @click="handleOpen"></div>
            </div>
          </div>
        </div>
      </div>

      <div class="front">
        <h1>Dear Lover</h1>
        <div class="flip-btn pointer" @click="handleFlip(false)">Flip</div>
      </div>
    </div>
  </div>
</template>

<script lang="ts" setup>
import { reactive } from 'vue';
import useResize from '@/hooks/useResize';
const { isMobi } = useResize();
const state = reactive({
  isFlip: false,
  isOpen: false,
  isFirstOpen: false,

  contents: []
});

const content = `💖 见字如晤,展信舒颜。这封信承载着我对你深深的思念。

🌹 喜欢你,或许是这个世界上最无法解释的事情。不是权衡利弊,不是见色起意,而是突然间有了你,让我牵肠挂肚,无法割舍。

🌈 你是我安稳岁月里的意外,是我平淡生活里的星辰大海。我不曾搜索爱的定义,因为当我开车等红灯时,转头看到你坐在副驾驶,那就是爱;当我清晨醒来,希望第一眼看到的是你的睡颜,那也是爱。

🌅 无论是清晨的日出,还是正午的悠闲,或是漫天星辰的夜晚,我都想与你共度。我的爱,无需理由,只因是你。

💍 遇见你,是故事的开始;而你,是我余生的欢喜。做过最勇敢的事,就是在最真实的时候选择送你离开,因为你值得更好的。

🌸 你是一树一树的花开,是温暖的希望,是人间四月天。我试图诅咒你,只因你只能待在我身边,只爱我一个人。但我知道,人生短暂,我想娶自己爱的人。

🌻 你是我生命中不可或缺的一部分,是我一生的希望。我不想放弃你,不想放弃爱的感觉。我不想再让你孤单,不想再让你孤独。

🌼 你是我生命中最美的风景,是我生命中最值得珍惜的记忆。

🌲 你是我生命中最美的记忆,是我生命中最珍贵的回忆。

🌴 你是我生命中最美的风景,是我生命中最值得珍惜的记忆

🌱 你是我生命中最美的记忆,是我生命中最珍贵的回忆

👨‍❤️‍👩‍👧‍👦 我曾以为我会孤独终老,直到遇见你。我不敢承诺一生不惹你生气,但在我能想象的未来里,只想对你一个人好。纵使生活不易,我还是想把你放在未来里,一生欢喜,纯净如初。
`;

state.contents = content.split('\n');

// 信封翻转
const handleFlip = (isBackFlip: boolean = false) => {
  if (isBackFlip && state.isOpen) {
    state.isOpen = false;
    setTimeout(() => {
      state.isFlip = !state.isFlip;
    }, 1.4 * 1000);
  } else {
    state.isFlip = !state.isFlip;
  }
};

// 打开/关闭信封
const handleOpen = () => {
  state.isOpen = !state.isOpen;
  if (!state.isFirstOpen) state.isFirstOpen = true;
};
</script>

<style lang="scss" scoped>
.envelope-warp {
  width: 100vw;
  height: 100vh;
  position: relative;
  display: flex;
  justify-content: center;
  align-items: center;
  .envelope-inner {
    margin-top: 100px;
    position: relative;
    width: 600px;
    height: 300px;
    .front {
      backface-visibility: hidden;
      position: absolute;
      top: 0;
      left: 0;
      width: 600px;
      height: 300px;
      font-size: 1.25em;
      background: url('@/assets/images/common/letterStamp.png') no-repeat 20px 20px, beige url('@/assets/images/common/letterBg.png');
      border: 1px solid #eae1d5;
      box-shadow: inset 0 0 10px 1px hsla(0, 0%, 100%, 0.6), 0 2px 3px -2px rgba(0, 0, 0, 0.6);
      padding: 20px;
      color: #837362;
      text-shadow: 0 1px 0 #fff, 0 1px 0 #fff;
      display: flex;
      justify-content: center;
      align-items: center;
      position: relative;
      .flip-btn {
        position: absolute;
        bottom: 20px;
        right: 20px;
        font-size: 18px;
        font-weight: 600;
      }
    }

    .back {
      transform: rotateY(-180deg);
      position: absolute;
      top: 0;
      left: 0;
      width: 600px;
      height: 300px;
      color: #837362;
      background-color: #837362;
      text-shadow: 0 1px 0 #fff, 0 1px 0 #fff;
      .flip-warp {
        width: 600px;
        height: 300px;
        position: relative;
        perspective: 100;
        // 信封 上半部分
        .top {
          transform-style: preserve-3d;
          position: absolute;
          left: 0;
          top: 0;
          z-index: 101;
          width: 100%;
          height: 150px;
          border-bottom-right-radius: 30px;
          border-bottom-left-radius: 30px;
          box-shadow: inset 0 0 10px 1px hsla(0, 0%, 100%, 0.6), 0 2px 3px -2px rgba(0, 0, 0, 0.6);
          transition: transform 0.7s ease var(--a), z-index 0.7s ease var(--a);
          transform-origin: top center;

          .top-front {
            backface-visibility: hidden;
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 150px;
            background: beige url('@/assets/images/common/letterBg.png');
            border-bottom-right-radius: 30px;
            border-bottom-left-radius: 30px;
            border-bottom: 1px solid #eae1d5;
            .yinzhang {
              width: 150px;
              height: 150px;
              background-image: url('@/assets/images/common/letterStitch.png');
              background-size: 100% 200%;
              position: absolute;
              bottom: -71px;
              left: 50%;
              margin-left: -75px;
            }
          }
          .top-back {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 150px;
            background-color: #837362;
            border: 35px solid hsla(0, 0%, 100%, 0.1);
            border-top: 2px solid #8f8579;
            box-shadow: inset 0 10px 30px 10px rgba(0, 0, 0, 0.1);
            border-bottom-right-radius: 30px;
            border-bottom-left-radius: 30px;
          }
        }
        // 信封 下半部分
        .bottom {
          position: absolute;
          left: 0;
          bottom: 0;
          z-index: 100;
          width: 100%;
          height: 200px;
          background: beige url('@/assets/images/common/letterBg.png');
          border: 1px solid #eae1d5;
          box-shadow: inset 0 0 10px 1px hsla(0, 0%, 100%, 0.6), 0 2px 3px -2px rgba(0, 0, 0, 0.6);
          .flip-btn {
            position: absolute;
            bottom: 20px;
            right: 20px;
            font-size: 18px;
            font-weight: 600;
          }
        }

        // 信纸
        .latter-warp {
          background-color: #fff;
          margin: 5%;
          width: 90%;
          position: absolute;
          left: 0;
          top: -8%;
          z-index: 99;
          transition: all 0.7s ease 0.7s;
          .latter-top {
            height: 40px;
            background-color: #fff;
            transform-style: preserve-3d;
            transition: transform 0.7s ease 0.3s;
            transform-origin: top center;
            // backface-visibility: hidden;
            .latter-top-back {
              position: absolute;
              top: 0;
              left: 0;
              height: 100%;
              width: 100%;
              display: flex;
              justify-content: flex-end;
              align-items: center;
              padding: 10px 20px;
              font-size: 16px;
              font-weight: 600;
              // background-color: #f2f2f2;
              border-bottom: 1px solid #f2f2f2;
              border-top: 1px solid #f2f2f2;
              .close {
                transition: all 0.3s ease var(--c);
              }
            }
            .latter-top-front {
              position: absolute;
              top: 0px;
              left: 0;
              height: 100%;
              width: 100%;
            }
          }
          .latter-inner {
            margin-top: -40px;
            backface-visibility: hidden;
            width: 100%;
            height: 100%;
            transform: rotateX(-180deg);
            transform-style: preserve-3d;
            transition: all 0.7s ease var(--b);

            .latter-bottom {
              background-color: #fff;
              .latter-content {
                padding: 0 20px;
                .latter-content-inner {
                  height: 197px;
                  overflow-y: auto;
                  .latter-content-item {
                    line-height: 24px;
                    font-size: 16px;
                    text-indent: 32px;
                    min-height: 24px;
                  }
                }
              }
              .header {
                height: 27px;
                line-height: 27px;
                padding: 0 20px;
                text-align: left;
                margin: 8px 0;
                font-size: 20px;
                font-weight: 600;
              }
              .footer {
                padding: 18px 0;
                line-height: 27px;
                padding: 0 20px;
                text-align: right;
                margin: 12px 0;
                font-size: 20px;
                font-weight: 600;
              }
            }
          }
          .is-transform {
            transform: rotateX(0deg);
          }
        }
        .is-chou {
          transform: translateY(-68%);
        }
      }
    }
  }
  .is-flip {
    transform: rotateY(180deg);
    transform-style: preserve-3d;
    transition: transform 0.7s 0s;
    transform-origin: center center;
  }
  .no-flip {
    transform: rotateY(360deg);
    transform-style: preserve-3d;
    transition: transform 0.7s 0s;
    transform-origin: center center;
  }

  .is-open {
    transform: rotateX(-180deg);
  }

  .no-open {
    transform: rotateX(0deg);
  }
}

@keyframes flip {
}
</style>

如果想要资源的直接去我的 snows_l's BLOG 打开 开发者工具直接拿就行了


http://www.kler.cn/a/467642.html

相关文章:

  • Spring MVC实战指南:构建高效Web应用的架构与技巧(三)
  • GPT系统重大升级,开创国内先河:o1支持图片识别功能正式上线
  • 2025元旦源码免费送
  • 如何在 Spring Cloud Gateway 中创建全局过滤器、局部过滤器和自定义条件过滤器
  • 【信息系统项目管理师】高分论文:论信息系统项目的风险管理(资金管控系统)
  • LangChain 介绍
  • 【OceanBase】通过 OceanBase 的向量检索技术构建图搜图应用
  • day33 多点通信
  • 卸载干净 IDEA(图文讲解)
  • 人工智能安全——大语言模型遗忘学习(LLM unlearning)与多目标优化算法
  • 32单片机从入门到精通之软件编程——中断处理(九)
  • Spring Boot 3 实现 MySQL 主从数据库之间的数据同步
  • 手搓人工神经网络
  • Introducing Optimization
  • 基于生成式对抗网络(GAN)的前沿研究与应用
  • 单片机-独立按键矩阵按键实验
  • [Qt] 输入控件 | Line | Text | Combo | Spin | Date | Dial | Slider
  • python基于diagrams库绘制系统架构图
  • 基于Redis有序集合实现滑动窗口限流
  • 【C#特性整理】C#特性及语法基础
  • C# 找出给定三角形的所有角度(Find all angles of a given triangle)
  • 银行系统安全用电解决方案
  • Day29:continue 语句
  • 什么是.net framework,什么是.net core,什么是.net5~8,版本对应关系
  • linux 系统配置ip
  • Linux 内核中网络接口的创建与管理