CSS3實(shí)現(xiàn)類似翻書(shū)效果的過(guò)渡動(dòng)畫(huà)的示例代碼
在VUE實(shí)戰(zhàn)項(xiàng)目的中有一個(gè)加載推薦書(shū)籍的過(guò)渡動(dòng)畫(huà),在項(xiàng)目中是使用JS實(shí)現(xiàn)。

當(dāng)時(shí)看視頻大概一個(gè)小時(shí)作用,拆分動(dòng)畫(huà)邏輯,寫代碼更是耗時(shí)。后來(lái)自己想著能不能用CSS動(dòng)畫(huà)直接寫出來(lái),折騰了半天,終于算是實(shí)現(xiàn)了。
可以查看加載動(dòng)畫(huà)地址
/*首先是DOM結(jié)構(gòu),不能像js一樣分左右兩邊,正面翻為反面還要改變z-index,按照同樣的布局也嘗試過(guò),沒(méi)有用CSS動(dòng)畫(huà)寫出來(lái),邏輯太復(fù)雜。
這是一個(gè)類似翻書(shū)一樣的動(dòng)畫(huà)效果,想著結(jié)構(gòu)分為一頁(yè)一頁(yè),一頁(yè)旋轉(zhuǎn)180°,完成時(shí)再改變“這一頁(yè)”的z-index。每個(gè)page類的before偽類為正面,after偽類為反面;分解5頁(yè)的在每一秒的動(dòng)畫(huà),寫出各自的animation。
*/
<div class="card">
<div class="page"></div>
<div class="page"></div>
<div class="page"></div>
<div class="page"></div>
<div class="page"></div>
</div>
.card{
width: 50px;
height: 50px;
opacity: 0;
position: relative;
animation: .5s all;
}
.card .page{
width: 50%;
height: 100%;
position: absolute;
left: 50%;
top: 0;
transform-style: preserve-3d;
transform-origin: left;
}
.card .page::before, .card .page::after{
content: '';
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
border-radius: 0px 50px 50px 0px;
}
.card .page::after{
border-radius: 50px 0px 0px 50px;
}
.card .page:nth-child(1){
animation: animation1 2s linear infinite;
}
.card .page:nth-child(2){
animation: animation2 2s linear infinite;
}
.card .page:nth-child(3){
animation: animation3 2s linear infinite;
}
.card .page:nth-child(4){
animation: animation4 2s linear infinite;
}
.card .page:nth-child(5){
animation: animation5 2s linear infinite;
}
.card .page:nth-child(1)::before{
background: aqua url("./images/star-right.png") center left/50% 50% no-repeat;
}
.card .page:nth-child(1)::after{
background: hotpink url("./images/compass-left.png") center right/50% 50% no-repeat;
transform: rotateY(180deg);
}
.card .page:nth-child(2)::before{
background: hotpink url("./images/compass-right.png") center left/50% 50% no-repeat;
}
.card .page:nth-child(2)::after{
background: coral url("./images/crown-left.png") center right/50% 50% no-repeat;
transform: rotateY(180deg);
}
.card .page:nth-child(3)::before{
background: coral url("./images/crown-right.png") center left/50% 50% no-repeat;
}
.card .page:nth-child(3)::after{
background: cyan url("./images/gift-left.png") center right/50% 50% no-repeat;
transform: rotateY(180deg);
}
.card .page:nth-child(4)::before{
background: cyan url("./images/gift-right.png") center left/50% 50% no-repeat;
}
.card .page:nth-child(4)::after{
background: yellowgreen url("./images/heart-left.png") center right/50% 50% no-repeat;
transform: rotateY(180deg);
}
.card .page:nth-child(5)::before{
background: yellowgreen url("./images/heart-right.png") center left/50% 50% no-repeat;
}
.card .page:nth-child(5)::after{
background: aqua url("./images/star-left.png") center right/50% 50% no-repeat;
transform: rotateY(180deg);
}
@keyframes animation1 {
0%{
z-index: 1;
transform: rotateY(180deg);
}
20%{
z-index: 1;
transform: rotateY(180deg);
}
40%{
z-index: 3;
transform: rotateY(360deg);
}
60%{
z-index: 4;
transform: rotateY(360deg);
}
60.0001%{
z-index: 4;
transform: rotateY(0deg);
}
80%{
z-index: 5;
transform: rotateY(0deg);
}
100%{
z-index: 4;
transform: rotateY(180deg);
}
}
@keyframes animation2 {
0%{
z-index: 5;
transform: rotateY(0deg);
}
20%{
z-index: 4;
transform: rotateY(180deg);
}
40%{
z-index: 1;
transform: rotateY(180deg);
}
60%{
z-index: 3;
transform: rotateY(360deg);
}
80%{
z-index: 4;
transform: rotateY(360deg);
}
100%{
z-index: 5;
transform: rotateY(360deg);
}
}
@keyframes animation3 {
0%{
z-index: 4;
transform: rotateY(0deg);
}
20%{
z-index: 5;
transform: rotateY(0deg);
}
40%{
z-index: 4;
transform: rotateY(180deg);
}
60%{
z-index: 1;
transform: rotateY(180deg);
}
80%{
z-index: 3;
transform: rotateY(360deg);
}
100%{
z-index: 4;
transform: rotateY(360deg);
}
}
@keyframes animation4 {
0%{
z-index: 3;
transform: rotateY(0deg);
}
20%{
z-index: 4;
transform: rotateY(0deg);
}
40%{
z-index: 5;
transform: rotateY(0deg);
}
60%{
z-index: 4;
transform: rotateY(180deg);
}
80%{
z-index: 1;
transform: rotateY(180deg);
}
100%{
z-index: 3;
transform: rotateY(360deg);
}
}
@keyframes animation5 {
0%{
z-index: 2;
transform: rotateY(0deg);
}
20%{
z-index: 3;
transform: rotateY(0deg);
}
40%{
z-index: 4;
transform: rotateY(0deg);
}
60%{
z-index: 5;
transform: rotateY(0deg);
}
80%{
z-index: 4;
transform: rotateY(180deg);
}
100%{
z-index: 1;
transform: rotateY(180deg);
}
}
再貼出JS實(shí)現(xiàn):
<div class="flap-card" v-for="(item, index) in flapCardList" :key="index" :style="{zIndex: item.zIndex}">
<div class="flap-card-circle">
<div class="flap-card-semi-circle flap-card-semi-circle-left" :style="semiCircleStyle(item, 'left')"
ref="left"></div>
<div class="flap-card-semi-circle flap-card-semi-circle-right" :style="semiCircleStyle(item, 'right')"
ref="right"></div>
</div>
</div>
data() {
return {
front: 0,
back: 1,
}
},
methods: {
// flapCardList是存儲(chǔ)著圖片信息的對(duì)象數(shù)組,通過(guò)v-for循環(huán)和semiCircleStyle方法設(shè)置5組圖片的背景。
semiCircleStyle(item, dir) {
return {
backgroundColor: `rgb(${item.r}, ${item.g}, ${item.b})`,
backgroundSize: item.backgroundSize,
backgroundImage: dir === 'left' ? item.imgLeft : item.imgRight
}
},
rotate(index, type) {
//卡牌翻轉(zhuǎn),"front"選擇右邊卡片,否則選擇左邊卡片;然后改變dom元素的樣式
const item = this.flapCardList[index]
let dom
if (type === 'front') {
dom = this.$refs.right[index]
} else {
dom = this.$refs.left[index]
}
dom.style.transform = `rotateY(${item.rotateDegree}deg)`
dom.style.backgroundColor = `rgb(${item.r}, ${item._g}, ${item.b})`
},
flapCardRotate() {
//首先是翻轉(zhuǎn)函數(shù),每次翻轉(zhuǎn)分正反兩面。當(dāng)動(dòng)畫(huà)至90°時(shí),改變背面的z-index值,每一幀動(dòng)畫(huà)完畢執(zhí)行rotate函數(shù)以改變樣式。
const frontFlapCard = this.flapCardList[this.front]
const backFlapCard = this.flapCardList[this.back]
frontFlapCard.rotateDegree += 10
frontFlapCard._g -= 5
backFlapCard.rotateDegree -= 10
if (backFlapCard.rotateDegree < 90) {
backFlapCard._g += 5
}
if (frontFlapCard.rotateDegree === 90 && backFlapCard.rotateDegree === 90) {
backFlapCard.zIndex += 2
}
this.rotate(this.front, 'front')
this.rotate(this.back, 'back')
if (frontFlapCard.rotateDegree === 180 && backFlapCard.rotateDegree === 0) {
this.next()
}
},
prepare() {
const backFlapCard = this.flapCardList[this.back]
backFlapCard.rotateDegree = 180
backFlapCard._g = backFlapCard.g - 5 * 9
this.rotate(this.back, 'back')
},
next() {
const frontFlapCard = this.flapCardList[this.front]
const backFlapCard = this.flapCardList[this.back]
frontFlapCard.rotateDegree = 0
backFlapCard.rotateDegree = 0
frontFlapCard._g = frontFlapCard.g
backFlapCard._g = backFlapCard.g
this.rotate(this.front, 'front')
this.rotate(this.back, 'back')
this.front++
this.back++
const len = this.flapCardList.length
if (this.front >= len) {
this.front = 0
}
if (this.back >= len) {
this.back = 0
}
// 動(dòng)態(tài)設(shè)置zIndex
// 100 -> 96
// 99 -> 100
// 98 -> 99
// 97 -> 98
// 96 -> 97
// (0 - 1 + 5) % 5 = 4
// (1 - 1 + 5) % 5 = 0
this.flapCardList.forEach((item, index) => {
item.zIndex = 100 - ((index - this.front + len) % len)
})
this.prepare()
},
startFlapCardAnimation() {
this.prepare()
this.task = setInterval(() => {
this.flapCardRotate()
}, this.intervalTime)
},
stopAnimation() {
//動(dòng)畫(huà)停止時(shí),清除所有setTimeout;否則再次進(jìn)入動(dòng)畫(huà)出現(xiàn)錯(cuò)誤。
if (this.task) {
clearInterval(this.task)
}
if (this.timeout) {
clearTimeout(this.timeout)
}
if (this.timeout2) {
clearTimeout(this.timeout2)
}
this.reset()
},
runAnimation() {
//點(diǎn)擊推薦,則動(dòng)畫(huà)開(kāi)始(vuex中定義了flapCardVisible,watch監(jiān)聽(tīng)其變化,為TRUE則動(dòng)畫(huà)開(kāi)始)
this.runFlapCardAnimation = true
this.timeout = setTimeout(() => {
this.startFlapCardAnimation()
this.startPointAnimation()
}, 300)
this.timeout2 = setTimeout(() => {
this.stopAnimation()
//2500ms過(guò)渡動(dòng)畫(huà)結(jié)束,推薦圖書(shū)顯示
this.runBookCardAnimation = true
}, 2500)
},
watch: {
flapCardVisible(v) {
if (v) {
this.runAnimation()
}
}
},
}
寫在最后:初次寫CSS動(dòng)畫(huà)的總結(jié),而且感覺(jué)自己的實(shí)現(xiàn)并不是很好,應(yīng)該有更簡(jiǎn)單些的實(shí)現(xiàn)方法。從DOM結(jié)構(gòu)設(shè)計(jì),到動(dòng)畫(huà)過(guò)程分析還欠缺很多。并且一個(gè)動(dòng)畫(huà)如此費(fèi)時(shí),在實(shí)際項(xiàng)目中應(yīng)該得不償失。還是需要大量的練習(xí),經(jīng)常練手才可熟能生巧。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章

CSS3實(shí)現(xiàn)的鼠標(biāo)懸停圖片模糊過(guò)渡動(dòng)畫(huà)特效源碼
這是一款基于CSS3實(shí)現(xiàn)的鼠標(biāo)懸停圖片模糊過(guò)渡動(dòng)畫(huà)特效源碼。界面中展示了四件服裝商品,當(dāng)鼠標(biāo)滑過(guò)商品的時(shí)候,可以看到商品圖片出現(xiàn)模糊化,并快速過(guò)渡切換到下一張商品圖2019-07-10菜單欄 “三” 變形為“X”css3過(guò)渡動(dòng)畫(huà)
本文主要介紹了菜單欄 “三” 變形為“X”css3過(guò)渡動(dòng)畫(huà)的實(shí)現(xiàn)方法。具有很好的參考價(jià)值,下面跟著小編一起來(lái)看下吧2017-02-28詳解Css3新特性應(yīng)用之過(guò)渡與動(dòng)畫(huà)
本篇文章主要介紹了Css3新特性應(yīng)用之過(guò)渡與動(dòng)畫(huà),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-01-10用css3實(shí)現(xiàn)轉(zhuǎn)換過(guò)渡和動(dòng)畫(huà)效果
這篇文章主要介紹了用css3實(shí)現(xiàn)轉(zhuǎn)換過(guò)渡和動(dòng)畫(huà)效果,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-03-13


