這篇“Vue動畫實例代碼分析”文章的知識點大部分人都不太理解,所以小編給大家總結了以下內容,內容詳細,步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“Vue動畫實例代碼分析”文章吧。
成都創(chuàng)新互聯(lián)公司服務項目包括利通網站建設、利通網站制作、利通網頁制作以及利通網絡營銷策劃等。多年來,我們專注于互聯(lián)網行業(yè),利用自身積累的技術優(yōu)勢、行業(yè)經驗、深度合作伙伴關系等,向廣大中小型企業(yè)、政府機構等提供互聯(lián)網行業(yè)的解決方案,利通網站推廣取得了明顯的社會效益與經濟效益。目前,我們服務的客戶以成都為中心已經輻射到利通省份的部分城市,未來相信會繼續(xù)擴大服務區(qū)域并繼續(xù)獲得客戶的支持與信任!
以顯示和隱藏動畫為例:
hello
當動畫開始的時候,P 標簽還是隱藏的,此時 Vue 會給 P 加上兩個class:
.fade-enter { opacity: 0; } .fade-enter-active { transition: opacity 0.5s; }
當動畫開始后,會移除.fade-enter
(在元素被插入之前生效,在元素被插入之后的下一幀移除),這個時候 P 標簽的 opacity
就恢復到 1,即顯示,這個時候就會觸發(fā)transition
, 檢測到opacity
的變化,就會產生動畫。當動畫結束后,會移除 Vue 加上的 class(v-enter-to, v-enter-active
)。
上面這個過程是怎么實現(xiàn)的呢?它主要用到了requestAnimationFrame
這個api,我們自己可以實現(xiàn)一個簡易版的動畫,當生成下一幀的時候添加或刪除某個類,從而形成動畫效果。
Document
當生成下一幀的時候,會移除enter
這個class,那么 div 就會顯示出來,就會觸發(fā)transition
產生動畫效果。
當隱藏的時候也產生動畫,如下圖:
.fade-leave-to { opacity: 0; } .fade-leave-active { transition: opacity 0.5s; }
剛開始 P 標簽是顯示的,因為此時fade-leave
(在離開過渡被觸發(fā)時立刻生效,下一幀被移除) 的樣式是 opacity
是 1,執(zhí)行到第二幀的時候加上fade-leave-to
(在離開過渡被觸發(fā)之后下一幀生效 ,與此同時 fade-leave
被刪除),此時opacity
是 0,既然發(fā)生了屬性的變化,transition
就會監(jiān)聽到,從而形成動畫。
這樣顯示和隱藏就形成了一個完整的動畫。
原理是當你通過點擊事件改變css屬性,比如opacity
時,transition
會檢測到這個變化,從而形成動畫。
CSS 動畫原理同 CSS 過渡類似,區(qū)別是在動畫中 v-enter 類名在節(jié)點插入 DOM 后不會立即刪除,而是在 animationend
事件觸發(fā)時刪除。
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris facilisis enim libero, at lacinia diam fermentum id. Pellentesque habitant morbi tristique senectus et netus.
當我們點擊改變show
為false
時,會在 P 標簽上添加.bounce-leave-active
這個class,這個類就會執(zhí)行animation 動畫,當動畫執(zhí)行完成后刪除.bounce-leave-active
。
...Demo
before-enter: 是指在動畫之前執(zhí)行的函數(shù);
enter: 就是整個動畫的過程, 執(zhí)行完后要加一個done(),來告訴vue已經執(zhí)行完畢;當只用 JavaScript 過渡的時候,在 enter 和 leave 中必須使用 done 進行回調。否則,它們將被同步調用,過渡會立即完成。
after-enter: 動畫結束之后執(zhí)行;
Javascript 鉤子動畫一般用在比較復雜的動畫上,而不是簡單的過渡動畫。后面我們會用很大的篇幅通過幾個例子來說明用 Javascript 鉤子動畫是如何完成復雜動畫的。
因為CSS過渡動畫需要有個觸發(fā)條件,比如opacity
必須有一個變化,如果沒有變化就不會觸發(fā)。那么,可以通過 appear
attribute 設置節(jié)點在初始渲染的過渡:
CSS 動畫(animation)則不需要觸發(fā)條件。
一旦涉及到多個元素的過渡,那么就會出現(xiàn)舊元素和新元素進出的先后問題。
的默認行為是進入和離開同時發(fā)生,但是這樣就會產生一些不協(xié)調的效果,所以 Vue 提供了過渡模式:
in-out
:新元素先進行過渡,完成之后當前元素過渡離開(in-out
模式不是經常用到,但對于一些稍微不同的過渡效果還是有用的)。
out-in
:當前元素先進行過渡,完成之后新元素過渡進入(這個用的比較多)。
但是這兩個模式并不能完全滿足實際需要,實際上我們可以定制我們要想的先后效果,比如后臺管理系統(tǒng)中有一個面包屑導航欄,當改變路由的時候需要更改面包屑里面的內容,那么這個更改的動畫可以讓舊的元素向左滑出,新的元素從右邊滑入。
// 一定要設置key if Demoelse demo
當有相同標簽名的元素切換時,需要通過 key attribute 設置唯一的值來標記以讓 Vue 區(qū)分它們,否則 Vue 為了效率只會替換相同標簽內部的內容。即使在技術上沒有必要,給在 transition 組件中的多個元素設置 key 是一個更好的實踐。
多個組件的過渡簡單很多 - 我們不需要使用 key
attribute。相反,我們只需要使用動態(tài)組件:
new Vue({ el: '#transition-components-demo', data: { view: 'v-a' }, components: { 'v-a': { template: ' Component A' }, 'v-b': { template: 'Component B' } } }) .component-fade-enter-active, .component-fade-leave-active { transition: opacity .3s ease; } .component-fade-enter, .component-fade-leave-to { opacity: 0; }
上面講的動畫都是針對單個節(jié)點,或者同一時間渲染多個節(jié)點中的一個,那么怎么同時渲染整個列表,比如使用 v-for
?
在這種場景中,使用
組件,這個組件的幾個特點:
不同于
,它會以一個真實元素呈現(xiàn):默認為一個 。你也可以通過
tag
attribute 更換為其他元素。
過渡模式不可用,因為我們不再相互切換特有的元素。
內部元素總是需要提供唯一的 key
attribute 值。
CSS 過渡的類將會應用在內部的元素中,而不是這個組/容器本身。
后面我們會通過一個例子演示如何使用
。
在后臺管理系統(tǒng)中,當路由變化時,對應的組件內容也會發(fā)生變化,當在變化時加上一個動畫,讓整個頁面效果更加自然。
// 這里加了key computed: { key() { return this.$route.path } } .fade-transform-leave-active, .fade-transform-enter-active { transition: all 0.5s; } .fade-transform-enter { opacity: 0; transform: translateX(-30px) } .fade-transform-leave-to { opacity: 0; transform: translateX(30px) }
在 H5 頁面開發(fā)中,一個常用的功能是點擊一個按鈕,隱藏的內容從屏幕底部彈出,同時彈出的時候有個動畫效果,一般是緩慢上升。
// 外面一層遮罩 // 這里面才是內容Add
這個動畫有兩層,一層是最外層的內容,另一層是最里面部分的內容
在 H5 頁面開發(fā)中,如果在一個列表頁中刪除其中一個子項,要求有一個刪除動畫效果。
.item { height: 40px; } .list-enter-active, .list-leave-active { transition: all 0.1s } .list-enter, .list-leave-to { height: 0 }delete
一般復雜的動畫,并不能用簡單的 css 過渡或者 css 動畫能實現(xiàn)的,需要使用 javascript鉤子動畫實現(xiàn)。
針對上面圖片的動畫進行拆解:
當從底部談起的時候,頁面頂部(向下的箭頭和歌曲名稱部分)從上往下滑入,同時有個回彈的效果,同時,底部(播放進度條的部分)從下往上滑入,也有一個回彈的效果。
左下角旋轉的圓有兩個動畫效果,一個是從小變大,一個是從左下角滑動到中心部分
圓的旋轉動畫
代碼結構:
// 頂部區(qū)域...// 中間區(qū)域...// 底部區(qū)域... // 內容區(qū)域...
實現(xiàn)第一個動畫效果
// 這是stylus的寫法 .normal-enter-active, .normal-leave-active transition: all 0.4s .top, .bottom // 通過這個白塞爾曲線,使得動畫有個回彈的效果 transition: all 0.4s cubic-bezier(0.86, 0.18, 0.82, 1.32) .normal-enter, .normal-leave-to opacity: 0 .top // 從上往下滑入 transform: translate3d(0, -100px, 0) .bottom // 從下往上滑入 transform: translate3d(0, 100px, 0)
通過第一章節(jié)部分的學習,看懂這段動畫代碼應該不難。
實現(xiàn)第二個動畫效果
要實現(xiàn)這個動畫效果,必須要計算左下角的圓到中心部分的圓的 x 軸和 y 軸方向上的距離,因為H5頁面在不同的手機屏幕下,這個距離是不同的,所以一開始就不能寫死,只能通過 javascript 去動態(tài)的獲取。
// 計算從小圓中心到大圓中心的距離以及縮放比例 _getPosAndScale() { const targetWidth = 40 const paddingLeft = 40 const paddingBottom = 30 const paddingTop = 80 const width = window.innerWidth * 0.8 const scale = targetWidth / width const x = -(window.innerWidth / 2 - paddingLeft) const y = window.innerHeight - paddingTop - width / 2 - paddingBottom return { x, y, scale } }
這段代碼細節(jié)可以不用看,只需要知道它是計算左下角小圓的原心到中心部分圓的原心的距離(x, y),以及根據圓的直徑獲取放大縮小倍數(shù)(scale)。
// 這個庫可以讓我們使用js來創(chuàng)建一個keyframe的動畫,為什么要用js來生成呢?這是因為有些變化的屬性需要動態(tài)的計算,而不是一開始就定好了 import animations from 'create-keyframe-animation' // 動畫鉤子 // done:當動畫執(zhí)行完后執(zhí)行done函數(shù),然后跳到afterEnter鉤子函數(shù) enter(el, done) { const { x, y, scale } = this._getPosAndScale() // 對于大圓來說,進入的時機就是從小圓到小圓 let animation = { 0: { // 一開始大圓相對于小圓的位置,所以x為負數(shù),y為整數(shù) transform: `translate3d(${x}px, ${y}px, 0) scale(${scale})` }, // scale: 1.1 這樣圓就有個放大后變回原樣的效果 60: { transform: 'translate3d(0, 0, 0) scale(1.1)' }, 100: { transform: 'translate3d(0, 0, 0) scale(1)' } } // 設置animation animations.registerAnimation({ name: 'move', animation, presets: { duration: 400, easing: 'linear' } }) // 往dom上加上這個animation,并執(zhí)行動畫 animations.runAnimation(this.$refs.cdWrapper, 'move', done) }, // 動畫結束之后把樣式置為空 afterEnter() { animations.unregisterAnimation('move') this.$refs.cdWrapper.style.animation = '' }, leave(el, done) { this.$refs.cdWrapper.style.transition = 'all 0.4s' const { x, y, scale } = this._getPosAndScale() this.$refs.cdWrapper.style[ transform ] = `translate3d(${x}px,${y}px,0) scale(${scale})` // 這樣寫的目的是如果沒有監(jiān)聽到動畫結束的事件,那么我們自己就寫一個定時器,400ms后執(zhí)行done函數(shù) const timer = setTimeout(done, 400) // 監(jiān)聽動畫結束 this.$refs.cdWrapper.addEventListener('transitionend', () => { clearTimeout(timer) done() }) }
這段代碼的效果就是大圓的動畫效果。當點擊小圓的時候,大圓開始進入,進入的過程就是動畫的過程。當點擊向下的箭頭,大圓將消失,消失的過程就是大圓退出的動畫過程。
雖然有點復雜,但是也不難看懂,以后我們對于復雜動畫可以模仿上面的代碼。
那小圓的動畫呢?它非常簡單,就是一個顯示隱藏的動畫:
.mini-enter-active, .mini-leave-active transition: all 0.4s .mini-enter, .mini-leave-to opacity: 0
至此,就完成小圓和大圓的聯(lián)動動畫,整體效果還是很驚艷的。
實現(xiàn)第三個動畫
// 模板部分// 邏輯部分 // 通過事件來控制playing的值,然后改變img標簽的class,從而是動畫停止和展示 cdCls() { return this.playing ? 'play' : 'play pause' } // css部分 .play animation: rotate 20s linear infinite .pause animation-play-state: paused @keyframes rotate 0% transform: rotate(0) 100% transform: rotate(360deg)
首先對動畫進行拆解:
當點擊 + 的時候,有一個小圓從右側向xiang左側滾動出來到指定的位置,既有滾動的效果,也有從右往左移動的效果。同時,當點擊 - 的時候,小圓從左側滾動到右側并消失。
當點擊 + 的時候,會出現(xiàn)一個小球,這個小球會從點擊的位置做一個拋物線運動軌跡到左下角的購物車中。同時,當我連續(xù)點擊的時候,會出現(xiàn)多個小球同時做拋物線運行出現(xiàn)在屏幕中。
實現(xiàn)第一個動畫
通過上面的學習,對這一個動畫的實現(xiàn)應該不難。代碼如下:
// 小圓 -.move-enter-active, &.move-leave-active transition: all 0.4s linear .move-enter, &.move-leave-active // 外層動畫是從右往左運動 opacity: 0 transform: translate3d(24px, 0, 0) .inner // 內層動畫是旋轉180° transform: rotate(180deg) 0" @click.stop="decrease"> -0">{{food.count}}// 小圓 ++
實現(xiàn)第二個動畫
創(chuàng)建小球,因為這個動畫就是對小球的動畫
這里創(chuàng)建是10個小球,小球開始的狀態(tài)都是隱藏的。
點擊 + 按鈕的時候,觸發(fā)一個小球彈出
// 點擊加號調用這個函數(shù),同時把加號的dom傳遞,這樣就能知道小球運動的起點位置 onAdd(target) { // shopCart就是圖中底部組件,執(zhí)行drop函數(shù) this.$refs.shopCart.drop(target) }, // 把加號對應的dom傳入,并綁定到小球el屬性上 drop(el) { for (let i = 0; i < this.balls.length; i++) { const ball = this.balls[i] if (!ball.show) { ball.show = true ball.el = el // dropBalls表示正在下落的小球,因為當快速點擊時,會觸發(fā)多個小球下落 this.dropBalls.push(ball) return } } },
因為小球的ball.show
為true,那么就會觸發(fā)對應的動畫鉤子函數(shù),首先觸發(fā)beforeDrop
:
beforeDrop(el) { // 取出最后一個小球 const ball = this.dropBalls[this.dropBalls.length - 1] // 獲取小球的起點位置,就是在哪個地方點擊的加號按鈕 const rect = ball.el.getBoundingClientRect() const x = rect.left - 32 const y = -(window.innerHeight - rect.top - 22) // 設置小球的位置,把小球設置到點擊加號按鈕的那個地方 el.style.display = '' // 外層動畫,向下 el.style.transform = el.style.webkitTransform = `translate3d(0,${y}px,0)` const inner = el.getElementsByClassName(innerClsHook)[0] // 內層動畫向左 inner.style.transform = inner.style.webkitTransform = `translate3d(${x}px,0,0)` }
接著執(zhí)行enter
事件函數(shù)dropping
:
dropping(el, done) { // 觸發(fā)瀏覽器重繪,把beforeDrop事件中設置的小球位置從底部位置移動到點擊加號的位置,這樣小球就會從上面往下面落下 this._reflow = document.body.offsetHeight // 設置小球落下的終點位置 el.style.transform = el.style.webkitTransform = `translate3d(0,0,0)` const inner = el.getElementsByClassName(innerClsHook)[0] inner.style.transform = inner.style.webkitTransform = `translate3d(0,0,0)` // 監(jiān)聽動畫結束 el.addEventListener('transitionend', done) }
最后執(zhí)行after-enter
事件函數(shù)afterDrop
:
afterDrop(el) { // 取出第一個小球,設置屬性show為false,同時要設置el.style.display = 'none' const ball = this.dropBalls.shift() if (ball) { ball.show = false el.style.display = 'none' } }
我們來梳理下流程:
點擊加號位置,觸發(fā)drop
函數(shù),然后把一個隱藏的小球設置為顯示狀態(tài),存儲在dropBalls
中,因為用戶可以快速點擊,所以dropBalls
里面可能有多個小球。
當把小球狀態(tài)設置為顯示狀態(tài),就會觸發(fā)動畫鉤子before-enter
,enter
,after-enter
這三個鉤子。
before-enter
的作用是把小球的位置設置到點擊的位置,同時利用offsetHeight
觸發(fā)瀏覽器重繪,這樣就會把小球的位置放在點擊的位置。
enter
的作用就是讓小球從點擊的位置落下,動畫分為兩層,一層是向下,另一層是向左,當兩者結合就構成了一個從右上角到左下角斜線的動畫軌跡,但是一個斜的直線動畫軌跡比較丑,這里就使用了時間函數(shù)cubic-bezier
來改變動畫軌跡,使其有一個先向上運動,最后向下運動的拋物線軌跡動畫。
.ball position: fixed left: 32px bottom: 22px z-index: 200 transition: all 0.4s cubic-bezier(0.49, -0.29, 0.75, 0.41) .inner width: 16px height: 16px border-radius: 50% background: $color-blue transition: all 0.4s linear
after-enter
當小球到達目的后,需要把小球隱藏起來,所以取出第一個小球,然后設置show
的屬性為false
,這樣小球就隱藏起來,等待下一次動畫執(zhí)行。
所以函數(shù)的執(zhí)行順序是:drop
-> before-enter
-> enter
-> after-enter
-> drop
-> before-enter
-> enter
-> after-enter
...,這樣就形成了在頁面中同時出現(xiàn)多個小球的動畫。
注意:當我們設置
show
的屬性為false
就可以了,但是代碼中同時也設置了el.style.display = 'none',如果不設置這個小球消失有一個延遲。
以上就是關于“Vue動畫實例代碼分析”這篇文章的內容,相信大家都有了一定的了解,希望小編分享的內容對大家有幫助,若想了解更多相關的知識內容,請關注創(chuàng)新互聯(lián)行業(yè)資訊頻道。