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>
相关推荐
小小小小宇1 分钟前
Vue `import` 为什么可以异步加载
前端
WMYeah6 分钟前
【无标题】
前端·rust·抽奖程序·跨平台抽奖程序
Unbelievabletobe7 分钟前
免费外汇api的响应时间在不同时段下的波动分析
大数据·开发语言·前端·python
大哥,带带弟弟16 分钟前
Grafana 前端嵌入与 JWT 鉴权实战
前端·grafana
小小小小宇17 分钟前
前端 V8 引擎垃圾回收机制与内存问题排查
前端
前端老石人28 分钟前
CSS 值定义语法
前端·css
sheeta199838 分钟前
Vue 前端基础笔记
前端·vue.js·笔记
小小小小宇39 分钟前
GitLab + GitLab Runner + Qiankun 微前端 + Nginx + Node 中间件 前端开发机从零搭建 CI/CD 全流程
前端
前端那点事43 分钟前
别再写垃圾组件!Vue3 如何设计「真正可复用」的高质量通用组件
前端·vue.js
卷帘依旧1 小时前
JavaScript 中的 Symbol
前端·javascript