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>
相关推荐
小破孩呦2 分钟前
动态列表的数据渲染、新增、编辑等功能开发及数据处理
前端·javascript·elementui
成长ing121382 分钟前
点击音效系统
前端·cocos creator
熟悉不过3 分钟前
cesium项目之cesiumlab地形数据加载
前端·javascript·vue.js·cesium·webgis·cesiumlab
神经毒素13 分钟前
WEB安全--XSS--DOM破坏
前端·web安全·xss
爱写代码的小朋友32 分钟前
PHP+Vue 3实现增删改查(CRUD)
开发语言·vue.js·php
Selicens34 分钟前
VSCode Snippets 魔改专属的 vue 代码片段
vue.js·visual studio code
不简说36 分钟前
sv-print可视化打印组件不完全指南③
前端·javascript·vue.js
前端摸鱼杭小哥38 分钟前
Vue 开发者狂喜!我在 React 中完美复刻了 v-if/v-for 指令
前端·vue.js·react.js
kovli40 分钟前
红宝书第四讲:JavaScript原始值与引用值行为差异详解
前端·javascript
竹苓42 分钟前
CSS Grid布局:从入门到放弃再到真香
前端