效果预览
1 原理介绍
1.1 前置知识
backface-visibility
backface-visibility
是一个用于控制元素的背面是否可见的 CSS 属性。该属性通常用于在进行 3D 转换时,控制元素背面的可见性。
当元素进行 3D 转换时,其背面可能会变得可见,backface-visibility
属性就是用来控制这种可见性的。属性有两个可能的值:
visible
:默认值。背面可见。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;
}
加上上面样式后得到如下效果:
上面的效果显然不满足翻转卡片的前置条件,思考一下我们想要达到的效果:
-
首先,正面和背面应该叠在一起。
实现:绝对定位。
-
叠在一起的初始状态应该只显示正面,隐藏背面。
实现:
默认将正面css设置为:transform: rotateY(0deg); backface-visibility: hidden;
默认将背面css设置为:transform: rotateY(180deg); backface-visibility: hidden;
这样初始状态下只显示正面内容,背面会自动隐藏。
-
翻转后应该显示背面,隐藏正面。
实现:将父盒子沿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>