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>
相关推荐
浮华似水14 分钟前
Javascirpt时区——脱坑指南
前端
王二端茶倒水16 分钟前
大龄程序员兼职跑外卖第五周之亲身感悟
前端·后端·程序员
_oP_i21 分钟前
Web 与 Unity 之间的交互
前端·unity·交互
钢铁小狗侠23 分钟前
前端(1)——快速入门HTML
前端·html
凹凸曼打不赢小怪兽1 小时前
react 受控组件和非受控组件
前端·javascript·react.js
狂奔solar1 小时前
分享个好玩的,在k8s上部署web版macos
前端·macos·kubernetes
qiyi.sky1 小时前
JavaWeb——Web入门(8/9)- Tomcat:基本使用(下载与安装、目录结构介绍、启动与关闭、可能出现的问题及解决方案、总结)
java·前端·笔记·学习·tomcat
清云随笔1 小时前
axios 实现 无感刷新方案
前端
鑫宝Code1 小时前
【React】状态管理之Redux
前端·react.js·前端框架
忠实米线2 小时前
使用pdf-lib.js实现pdf添加自定义水印功能
前端·javascript·pdf