vue實(shí)現(xiàn)書本翻頁動(dòng)畫效果實(shí)例詳解

偶然興起,想要用vue來做一個(gè)書本的組件,有了這個(gè)想法后邊開始動(dòng)手,先簡(jiǎn)單地實(shí)現(xiàn)基本的效果,為后續(xù)封裝為組件進(jìn)行準(zhǔn)備工作,實(shí)現(xiàn)該效果的要使用vue + css + JavaScript。
關(guān)鍵字
transform
Transform屬性應(yīng)用于元素的2D或3D轉(zhuǎn)換。這個(gè)屬性允許你將元素旋轉(zhuǎn),縮放,移動(dòng),傾斜等。
語法為transform: none|*transform-functions*; 我們主要使用到其旋轉(zhuǎn)效果,我們可以這樣寫。
transform: rotateY(90deg) //表示沿Y軸旋轉(zhuǎn)90度
animation
既然是要實(shí)現(xiàn)動(dòng)畫效果,那么肯定少不了animation的出場(chǎng)了,
animation屬性的語法為: animation: name duration timing-function delay iteration-count direction fill-mode play-state;我們需要用到的只是前兩個(gè)屬性,name和duration,分別為指定要綁定到選擇器的關(guān)鍵幀的名稱和動(dòng)畫指定需要多少秒或毫秒完成 我們可以這樣寫
animation: fanPre 2s;
@keyframes
使用@keyframes規(guī)則,你可以創(chuàng)建動(dòng)畫,創(chuàng)建動(dòng)畫是通過逐步改變從一個(gè)CSS樣式設(shè)定到另一個(gè),在動(dòng)畫過程中,可以更改CSS樣式的設(shè)定多次,指定的變化時(shí)發(fā)生時(shí)使用%,或關(guān)鍵字"from"和"to",這是和0%到100%相同。
語法為:@keyframes *animationname* {*keyframes-selector* {*css-styles;}* } 我們可以這樣寫
@keyframes fanPre {
0% {
transform: rotateY(0deg);
background-color: rgba(122, 112, 112);
}
50% {
background-color: rgba(122, 112, 112);
}
75% {
background-color: rgba(122, 112, 112);
}
100% {
transform: rotateY(-140deg);
background-color: none;
}
}
var
此var并不是JavaScript中的var而是css中的var,我們可以使用其來實(shí)現(xiàn)css與vue數(shù)據(jù)繼續(xù)數(shù)據(jù)交換,及css中可以使用vue定義的data來進(jìn)行屬性設(shè)置,具體如下:
//html
<div :style="{ '--speed': speed }"></div>
//javascript
props: {
speed: {
type: String,
default: "2s",
}
}
//css
<style vars="{ speed, degs }" lang="scss" scoped>
animation: fanPre var(--speed);
</style>
實(shí)現(xiàn)
知道了上面這幾個(gè)關(guān)鍵詞之后,我們便可以開始著手實(shí)現(xiàn)該效果了,首先我們需要一個(gè)書本頁面列表數(shù)據(jù)
//書本頁面列表
pagesList: {
type: Array,
default: () => {
return [
{
title: "關(guān)雎",
text: [
"關(guān)關(guān)雎鳩,在河之洲。窈窕淑女,君子好逑。",
"參差荇菜,左右流之。窈窕淑女,寤寐求之。",
"求之不得,寤寐思服。悠哉悠哉,輾轉(zhuǎn)反側(cè)。",
"參差荇菜,左右采之。窈窕淑女,琴瑟友之。",
"參差荇菜,左右芼之。窈窕淑女,鐘鼓樂之。",
],
},
{
title: "聲聲慢·尋尋覓覓",
text: [
"尋尋覓覓,冷冷清清,凄凄慘慘戚戚。乍暖還寒時(shí)候,最難將息。三杯兩盞淡酒,怎敵他、晚來風(fēng)急!雁過也,正傷心,卻是舊時(shí)相識(shí)。",
"滿地黃花堆積,憔悴損,如今有誰堪摘?守著窗兒,獨(dú)自怎生得黑!梧桐更兼細(xì)雨,到黃昏、點(diǎn)點(diǎn)滴滴。這次第,怎一個(gè)愁字了得!",
],
},
{
title: "青玉案·元夕",
text: [
"東風(fēng)夜放花千樹。更吹落、星如雨。寶馬雕車香滿路。鳳簫聲動(dòng),玉壺光轉(zhuǎn),一夜魚龍舞。",
"蛾兒雪柳黃金縷。笑語盈盈暗香去。眾里尋他千百度。驀然回首,那人卻在,燈火闌珊處。",
],
},
{
title: "蝶戀花·佇倚危樓風(fēng)細(xì)細(xì)",
text: [
"佇倚危樓風(fēng)細(xì)細(xì),望極春愁,黯黯生天際。草色煙光殘照里,無言誰會(huì)憑闌意。",
"擬把疏狂圖一醉,對(duì)酒當(dāng)歌,強(qiáng)樂還無味。衣帶漸寬終不悔,為伊消得人憔悴。",
],
},
{
title: "雨霖鈴·秋別",
text: [
"寒蟬凄切,對(duì)長亭晚,驟雨初歇。都門帳飲無緒,留戀處,蘭舟催發(fā)。執(zhí)手相看淚眼,竟無語凝噎。念去去,千里煙波,暮靄沉沉楚天闊。",
"多情自古傷離別,更那堪,冷落清秋節(jié)!今宵酒醒何處?楊柳岸,曉風(fēng)殘?jiān)?。此去?jīng)年,應(yīng)是良辰好景虛設(shè)。便縱有千種風(fēng)情,更與何人說",
],
},
];
},
},
將數(shù)據(jù)渲染到頁面上,如下例子
<div
@click="turnPage(1)"
class="j-book-page"
:key="'page-now-' + index"
:style="getPageStyle(index)"
>
<h3>{{ nowPage.title }}</h3>
<p
v-for="(t, nowPageInd) in nowPage.text"
:key="'nowPage-' + nowPageInd"
>
{{ t }}
</p>
</div>
頁面翻頁功能實(shí)現(xiàn)如下,使用currentPage來記錄當(dāng)前展示頁面頁數(shù),使用flag來區(qū)分是上一頁還是下一頁翻頁,并進(jìn)行相應(yīng)的翻頁操作。其中要注意對(duì)點(diǎn)擊事件進(jìn)行防抖操作,防止翻頁過快,具體代碼如下:
turnPage(flag) {
if (!this.canTurn) return;
if (this.currentPage <= this.pagesList.length)
this.setPage(this.currentPage, flag);
if (this.currentPage < this.pagesList.length && this.currentPage > 0) {
this.canTurn = false;
setTimeout(() => {
this.canTurn = true;
}, parseInt(this.speed) * 1000 - 100);
}
if (flag === 1) {
if (this.currentPage < this.pagesList.length) {
this.currentPage++;
this.nextClick = true;
this.lastClick = false;
}
} else {
if (this.currentPage > 0) {
this.currentPage--;
this.nextClick = false;
this.lastClick = true;
}
}
},
完整代碼
<template>
<div class="j-book" :style="getBookStyle()">
<div :style="{ '--speed': speed, '--degs': degs }">
<div
class="j-book-page-pre"
@click="turnPage(-1)"
v-if="currentPage > 0 && showCover"
>
<div class="j-book-page">
<h3>{{ prePage.title }}</h3>
<p v-for="(t, textInd) in prePage.text" :key="'prePage-' + textInd">
{{ t }}
</p>
</div>
</div>
<div class="j-book-pages">
<template v-for="(item, index) in pagesList">
<div
@click="turnPage(-1)"
class="j-book-page turn-page-ani"
v-if="currentPage === index + 2 && nextClick"
:key="'page-last--' + index"
:style="getPageStyle(index)"
>
<h3>{{ item.title }}</h3>
<p v-for="(t, itemInd) in item.text" :key="'item-' + itemInd">
{{ t }}
</p>
</div>
<div
@click="turnPage(-1)"
class="j-book-page turn-page-ani"
v-if="currentPage === 1 && nextClick"
:key="'page-last-' + index"
:style="getPageStyle(index)"
>
<h3>{{ cover.title }}</h3>
<p v-for="(t, coverInd) in cover.text" :key="'cover-' + coverInd">
{{ t }}
</p>
</div>
<div
@click="turnPage(1)"
class="j-book-page turn-page-pre-ani"
v-if="lastClick && currentPage === 0"
:key="'page-pre-currentPage' + index"
:style="getPageStyle(5)"
>
<h3>{{ cover.title }}</h3>
<p v-for="(t, coverInd) in cover.text" :key="'cover-0-' + coverInd">
{{ t }}
</p>
</div>
<div
@click="turnPage(1)"
class="j-book-page turn-page-pre-ani"
v-if="lastClick && currentPage === index + 1"
:key="'page-pre-' + index"
:style="getPageStyle(5)"
>
<h3>{{ item.title }}</h3>
<p v-for="(t, itemInd) in item.text" :key="'item-0-' + itemInd">
{{ t }}
</p>
</div>
<div
@click="turnPage(1)"
class="j-book-page"
:key="'page-now-' + index"
:style="getPageStyle(index)"
>
<h3>{{ nowPage.title }}</h3>
<p
v-for="(t, nowPageInd) in nowPage.text"
:key="'nowPage-' + nowPageInd"
>
{{ t }}
</p>
</div>
</template>
</div>
</div>
</div>
</template>
<script>
export default {
name: "book",
props: {
width: {
type: Number,
default: 300,
},
height: {
type: Number,
default: 400,
},
speed: {
type: String,
default: "2s",
},
//書本頁面列表
pagesList: {
type: Array,
default: () => {
return [
{
title: "關(guān)雎",
text: [
"關(guān)關(guān)雎鳩,在河之洲。窈窕淑女,君子好逑。",
"參差荇菜,左右流之。窈窕淑女,寤寐求之。",
"求之不得,寤寐思服。悠哉悠哉,輾轉(zhuǎn)反側(cè)。",
"參差荇菜,左右采之。窈窕淑女,琴瑟友之。",
"參差荇菜,左右芼之。窈窕淑女,鐘鼓樂之。",
],
},
{
title: "聲聲慢·尋尋覓覓",
text: [
"尋尋覓覓,冷冷清清,凄凄慘慘戚戚。乍暖還寒時(shí)候,最難將息。三杯兩盞淡酒,怎敵他、晚來風(fēng)急!雁過也,正傷心,卻是舊時(shí)相識(shí)。",
"滿地黃花堆積,憔悴損,如今有誰堪摘?守著窗兒,獨(dú)自怎生得黑!梧桐更兼細(xì)雨,到黃昏、點(diǎn)點(diǎn)滴滴。這次第,怎一個(gè)愁字了得!",
],
},
{
title: "青玉案·元夕",
text: [
"東風(fēng)夜放花千樹。更吹落、星如雨。寶馬雕車香滿路。鳳簫聲動(dòng),玉壺光轉(zhuǎn),一夜魚龍舞。",
"蛾兒雪柳黃金縷。笑語盈盈暗香去。眾里尋他千百度。驀然回首,那人卻在,燈火闌珊處。",
],
},
{
title: "蝶戀花·佇倚危樓風(fēng)細(xì)細(xì)",
text: [
"佇倚危樓風(fēng)細(xì)細(xì),望極春愁,黯黯生天際。草色煙光殘照里,無言誰會(huì)憑闌意。",
"擬把疏狂圖一醉,對(duì)酒當(dāng)歌,強(qiáng)樂還無味。衣帶漸寬終不悔,為伊消得人憔悴。",
],
},
{
title: "雨霖鈴·秋別",
text: [
"寒蟬凄切,對(duì)長亭晚,驟雨初歇。都門帳飲無緒,留戀處,蘭舟催發(fā)。執(zhí)手相看淚眼,竟無語凝噎。念去去,千里煙波,暮靄沉沉楚天闊。",
"多情自古傷離別,更那堪,冷落清秋節(jié)!今宵酒醒何處?楊柳岸,曉風(fēng)殘?jiān)?。此去?jīng)年,應(yīng)是良辰好景虛設(shè)。便縱有千種風(fēng)情,更與何人說",
],
},
];
},
},
//書本封面
cover: {
type: Object,
default: () => {
return {
title: "封面",
text: ["封面"],
};
},
},
},
data() {
return {
currentPage: 0,
nextClick: false,
lastClick: false,
prePage: {},
nowPage: {},
canTurn: true,
degs: "0deg",
showCover: false,
};
},
mounted() {
this.init();
},
methods: {
init() {
this.nowPage = this.cover;
},
getBookStyle() {
let res = "";
res += "width:" + this.width + "px;";
res += "height:" + this.height + "px;";
res += "'--speed':" + this.speed + ";";
res += "transform: rotate(" + this.degs + ");";
return res;
},
getPageStyle(index) {
let res = "";
res += "z-index:" + (this.pagesList.length - index) + ";";
return res;
},
setPage(page, flag) {
if (flag === -1) {
this.prePage = this.pagesList[page - 3] || this.cover;
this.nowPage = this.pagesList[page - 1];
} else {
this.prePage = this.pagesList[page - 2] || this.cover;
this.nowPage =
this.pagesList[ page ] || this.pagesList[this.pagesList.length - 1];
}
let speed = this.speed;
speed = parseInt(speed) * 1000 - 500;
setTimeout(() => {
if (this.currentPage === 1) {
this.showCover = true;
}
if (this.currentPage === 0) {
this.showCover = false;
}
if (flag === -1) {
this.nowPage = this.pagesList[this.currentPage - 1] || this.cover;
if (this.currentPage === 0) {
this.degs = "0deg";
}
} else {
this.degs = "-5deg";
this.prePage = this.pagesList[this.currentPage - 2] || this.cover;
}
}, speed);
},
turnPage(flag) {
if (!this.canTurn) return;
if (this.currentPage <= this.pagesList.length)
this.setPage(this.currentPage, flag);
if (this.currentPage < this.pagesList.length && this.currentPage > 0) {
this.canTurn = false;
setTimeout(() => {
this.canTurn = true;
}, parseInt(this.speed) * 1000 - 100);
}
if (flag === 1) {
if (this.currentPage < this.pagesList.length) {
this.currentPage++;
this.nextClick = true;
this.lastClick = false;
}
} else {
if (this.currentPage > 0) {
this.currentPage--;
this.nextClick = false;
this.lastClick = true;
}
}
},
},
};
</script>
<style vars="{ speed, degs }" lang="scss" scoped>
.j-book {
background-color: gray;
position: relative;
box-shadow: 30px 0px 30px rgb(0, 0, 0, 0.6) inset;
transform: rotate(var(--degs));
color: #dec38f;
.j-book-page-pre {
position: absolute;
width: 100%;
height: 100%;
z-index: 2;
background-size: 100%;
transform-origin: left;
border-top-left-radius: 2px;
border-bottom-left-radius: 2px;
background-color: rgba(122, 112, 112);
transform: rotateY(-140deg);
.j-book-page {
position: absolute;
width: 100%;
height: 100%;
}
}
.j-book-pages {
position: absolute;
width: 100%;
height: 100%;
.turn-page-pre-ani {
position: absolute;
width: 100%;
height: 100%;
z-index: 2;
background-size: 100%;
transform-origin: left;
border-top-left-radius: 2px;
border-bottom-left-radius: 2px;
transform: rotateY(0deg);
animation: fanPre var(--speed);
}
@keyframes fanPre {
0% {
transform: rotateY(-140deg);
background-color: rgba(122, 112, 112);
}
50% {
background-color: rgba(122, 112, 112);
}
100% {
transform: rotateY(0deg);
background-color: none;
}
}
.turn-page-ani {
position: absolute;
width: 100%;
height: 100%;
z-index: 2;
background-size: 100%;
transform-origin: left;
border-top-left-radius: 2px;
border-bottom-left-radius: 2px;
transform: rotateY(-140deg);
animation: fan var(--speed);
}
@keyframes fan {
0% {
transform: rotateY(0deg);
background-color: none;
}
100% {
transform: rotateY(-140deg);
background-color: rgba(122, 112, 112);
}
}
.j-book-page {
position: absolute;
width: 100%;
height: 100%;
top: 0;
h3 {
text-align: center;
}
p {
}
}
}
.j-book-btns {
position: absolute;
bottom: 0;
display: flex;
justify-content: space-around;
width: 100%;
}
}
</style>
更多關(guān)于VUE特效請(qǐng)查看下面的相關(guān)鏈接
- 基于Vuejs框架實(shí)現(xiàn)翻頁組件
- 基于vue實(shí)現(xiàn)分頁/翻頁組件paginator示例
- vue router自動(dòng)判斷左右翻頁轉(zhuǎn)場(chǎng)動(dòng)畫效果
- 基于Vue2.0+ElementUI實(shí)現(xiàn)表格翻頁功能
- Vue2.0+ElementUI實(shí)現(xiàn)表格翻頁的實(shí)例
- Vue實(shí)現(xiàn)簡(jiǎn)易翻頁效果源碼分享
- vue-awesome-swiper 基于vue實(shí)現(xiàn)h5滑動(dòng)翻頁效果【推薦】
- vue 實(shí)現(xiàn)滾動(dòng)到底部翻頁效果(pc端)
- vue 翻頁組件vue-flip-page效果
- vue+animation實(shí)現(xiàn)翻頁動(dòng)畫
相關(guān)文章
vue中router.beforeEach()的簡(jiǎn)單用法舉例
router.beforeEach()一般用來做一些進(jìn)入頁面的限制,比如沒有登錄,就不能進(jìn)入某些頁面,只有登錄了之后才有權(quán)限查看某些頁面,下面這篇文章主要給大家介紹了關(guān)于vue中router.beforeEach()的簡(jiǎn)單用法舉例,需要的朋友可以參考下2023-01-01
element實(shí)現(xiàn)二級(jí)菜單和頂部導(dǎo)航聯(lián)動(dòng)的示例
本文主要介紹了element實(shí)現(xiàn)二級(jí)菜單和頂部導(dǎo)航聯(lián)動(dòng)的示例,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02
Vue使用xlsx模塊實(shí)現(xiàn)讀寫Excel文檔內(nèi)容
在前端項(xiàng)目開發(fā)中,讀寫Excel文件的需求變得越來越常見,本文將詳細(xì)介紹如何在Vue項(xiàng)目中利用FileReader對(duì)象和xlsx模塊來讀取Excel文件的內(nèi)容,需要的可以參考下2024-03-03
webstorm和.vue中es6語法報(bào)錯(cuò)的解決方法
本篇文章主要介紹了webstorm和.vue中es6語法報(bào)錯(cuò)的解決方法,小編總結(jié)了webstorm和.vue中出現(xiàn)的es6語法報(bào)錯(cuò),避免大家采坑,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-05-05
vue實(shí)現(xiàn)某元素吸頂或固定位置顯示(監(jiān)聽滾動(dòng)事件)
這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)某元素吸頂或固定位置顯示,監(jiān)聽滾動(dòng)事件,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12
vue中this.$refs有值,但無法獲取ref的值問題及解決
這篇文章主要介紹了vue中this.$refs有值,但無法獲取ref的值問題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-01-01
vue通過滾動(dòng)行為實(shí)現(xiàn)從列表到詳情,返回列表原位置的方法
今天小編就為大家分享一篇vue通過滾動(dòng)行為實(shí)現(xiàn)從列表到詳情,返回列表原位置的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-08-08

