Vue自定义指令实现卡片翻转功能

效果预览

1 原理介绍

1.1 前置知识

backface-visibility

backface-visibility 是一个用于控制元素的背面是否可见的 CSS 属性。该属性通常用于在进行 3D 转换时,控制元素背面的可见性。

当元素进行 3D 转换时,其背面可能会变得可见,backface-visibility 属性就是用来控制这种可见性的。属性有两个可能的值:

  1. visible:默认值。背面可见。
  2. hidden:背面不可见,即不会显示。

如果设置了 backface-visibility: hidden; 盒子背面将不可见,把盒子绕 y 轴进行3d 旋转 180° 后,我们将看到盒子的背面。

backface-visibility 主要用于处理 3D 转换的情况,如果没有使用 3D 转换,该属性的效果可能不会显著。

transform-style: preserve-3d

transform-style: preserve-3d 主要用于配合 CSS 3D 变换,它告诉浏览器在进行 3D 变换时应该如何处理子元素。当给父元素应用 transform-style: preserve-3d 时,其子元素将以三维的方式定位,而不是被扁平化到同一个平面。

必须在父盒子上加上这个属性,因为这里是父盒子翻转带动子盒子,如果不加上这个属性,子盒子中的back背面将显示不出来!

1.2 步骤分析

首先你需要一个大盒子,里面包含两个小盒子,这里第一个小盒子为正面内容,第二个小盒子为反面内容。结构如下:

html 复制代码
    <div class="card">
      <div class="front">front</div>
      <div class="back">back</div>
    </div>
css 复制代码
      .card {
        height: 270px;
        width: 270px;
        margin: 40px auto;
        border: 1px solid #f00;
        position: relative;
      }
      .front,.back {
        width: 100%;
        height: 100%;
      }

      .front {
        background-image: url(./imgs/youli.png);
        background-repeat: no-repeat;
        background-size: cover;
      }

      .back {
        background-image: url(./imgs/erha.jpg);
        background-repeat: no-repeat;
        background-size: cover;
      }

加上上面样式后得到如下效果:

上面的效果显然不满足翻转卡片的前置条件,思考一下我们想要达到的效果:

  1. 首先,正面和背面应该叠在一起。

    实现:绝对定位。

  2. 叠在一起的初始状态应该只显示正面,隐藏背面。

    实现:

    默认将正面css设置为:transform: rotateY(0deg); backface-visibility: hidden;

    默认将背面css设置为:transform: rotateY(180deg); backface-visibility: hidden;

    这样初始状态下只显示正面内容,背面会自动隐藏。

  3. 翻转后应该显示背面,隐藏正面。

    实现:将父盒子沿y轴翻转 180度,这样子盒子 front 会旋转到背面去,从而隐藏。子盒子 back 会旋转到正面,从而显示。通过点击父盒子 添加移除 flipped 类名,从而实现翻转功能。

完整代码如下:

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>卡片旋转效果</title>
    <style>

      .card {
        height: 270px;
        width: 270px;
        margin: 40px auto;
        border: 1px solid #f00;
        position: relative;

        transform-style: preserve-3d;
        transition: transform 0.5s;
        perspective: 0px;
      }

      .card.flipped {
        transform: rotateY(180deg);
      }

      .front,.back {
        width: 100%;
        height: 100%;
        position: absolute;
        left: 0;
        top: 0;
      }

      .front {
        background-image: url(./imgs/youli.png);
        background-repeat: no-repeat;
        background-size: cover;

        backface-visibility: hidden;
        transform: rotateY(0deg);
      }

      .back {
        background-image: url(./imgs/erha.jpg);
        background-repeat: no-repeat;
        background-size: cover;

        transform: rotateY(180deg);
        backface-visibility: hidden;
      }
    </style>
  </head>
  <body>
    <div class="card">
      <div class="front">front</div>
      <div class="back">back</div>
    </div>

    <script>
      const card = document.querySelector('.card')
      card.addEventListener('click', function() {
        this.classList.toggle('flipped')
 
      })
    </script>
  </body>
</html>

2 用 Vue 自定义指令实现卡片翻转

这里自定义指令的用法就不再介绍了,直接贴上实现代码:

js 复制代码
Vue.directive('flip', {
  	bind: function (el, binding) {
        console.log('bind', el, binding.value)
        el.style.position = 'relative'
        el.style.transformStyle = 'preserve-3d'
        el.style.transition = 'transform 0.5s'

        for (let i = 0; i < el.children.length; i++) {
          const element = el.children[i]
          element.style.position = 'absolute'
          element.style.position = 'absolute'
          element.style.width = '100%'
          element.style.height = '100%'
          element.style.left = 0
          element.style.top = 0
          if (i === 0) {
            element.style.backfaceVisibility = 'hidden'
            element.style.transform = 'rotateY(0deg)'
          }
          if (i === 1) {
            element.style.backfaceVisibility = 'hidden'
            element.style.transform = 'rotateY(180deg)'
          }
        }
    },

    // inserted: function (el, binding) {
    //   console.log('inserted', el, binding.value)
    // },
    
    update: function (el, binding) {
        console.log('update', el, binding.value)
        if (binding.value) {
            el.style.transform = 'rotateY(180deg)'
        } else {
            el.style.transform = 'rotateY(0deg)'
        }
    },
    
    //componentUpdated: function (el, binding) {
        //console.log('componentUpdated', el, binding.value)
    //},

    // unbind(el) {
    // },
    
})

组件中使用(注意 html 结构必须如下):

js 复制代码
<template>
    <div class="flipped_card" v-flip="isFlipped" @click="handleFlippedClick">
        <div class="front">front</div>
        <div class="back">back</div>
    </div>
</template>
<script>
  data() {
    return {
      isFlipped: false,
    }
  },
  
  methods: {
    handleFlippedClick() {
      if (!this.isFlipped) {
        this.isFlipped = true
      } else {
        this.isFlipped = false
      }
    },
  }    
</script>
相关推荐
java1234_小锋几秒前
【技术专题】HTML5 网页技术 - Web Storage 本地存储 & 文件 API & 拖放 API
前端·html·html5
恋猫de小郭1 分钟前
Flutter 官方表示:AI 时代代码越来越容易写,为什么还要一套代码库?
android·前端·flutter
软件派8 分钟前
Chrome 插件开发实战指南
前端·chrome
ly76899 分钟前
性能剖析与压测方法论:JMH、async-profiler 与分层瓶颈定位的可复现实验
服务器·前端·数据库·压测·jmh·性能剖析·async-profiler
ZoeLandia21 分钟前
React05:useEffect 与副作用
开发语言·前端·react.js·typescript
狮子座的男孩28 分钟前
html+css基础:24、2D变换(位移/缩放/旋转/扭曲(了解)/多重变换/变换原点)
前端·css·经验分享·html·2d位移·原点变换·缩放/旋转/扭曲
weixin_4173968337 分钟前
酒店弱电怎么验收?四个阶段与逐项检查清单
开发语言·前端·php·综合布线·弱电
风骏时光牛马38 分钟前
探索AI开源工具,解锁原生创造新体验
前端
u01110267540 分钟前
Vue 页面离开后还在轮询?统一清理计时器、监听器与观察器
前端·javascript·vue.js
大猩猩X43 分钟前
vue 表格组件 vxe-table 实现多行编辑,支持增删改查与校验
前端·javascript·vue.js·vxe-table