Vue中使用create-keyframe-animation與動畫鉤子完成復(fù)雜動畫
本篇文章主要介紹了 Vue中使用create-keyframe-animation與動畫鉤子完成復(fù)雜動畫,分享給大家
如何實(shí)現(xiàn)這個動畫?
效果分析
點(diǎn)`start`的時候,我們把整個動畫拆分為兩種效果(過渡和動畫)。
1. 中間cd消失,下方播放條顯示,這是屬于`過渡`
2. `過渡`開始的同時,cd同時移動、放大、縮小到左下方播放條 ,這屬于`動畫`
上面的效果是【過渡】加【動畫】同時使用完成的
- 對于第一種【過渡】,我們用vue中transition標(biāo)簽,加設(shè)置v-enter、v-leave-to、v-enter-active、v-leave-enter即可完成
- 對于第二種【動畫】,我們就要用keyframe來完成了。
這里我們先完成第一種過渡
vue中模板節(jié)點(diǎn)
<template> <div class="index"> <transition> <div class="cd-box" ref="cdWrapper" v-show="fullScreen"> // CD圖片 (動畫的時候圖片初始位置) <img src="../assets/bj.png" alt="" class="bg"> </div> </transition> <button @click="switchMode" style="position:absolute;top:0;left:10px;">start</button> <transition> // 下面播放狀態(tài)框 <div class="mini-player-box" v-show="!fullScreen"> // 狀態(tài)看里面的圖片 (動畫的時候圖片結(jié)束位置) <div class="mini-img"> <img src="../assets/bj.png" alt="" > </div> </div> </transition> </div> </template>
結(jié)構(gòu)很簡單,基本就是 兩個大div
,然后把div的布局按效果圖那些布置。
css部分(省略布局部分)
.cd-box &.v-enter-active, &.v-leave-active transition: all 0.4s &.v-enter, &.v-leave-to opacity: 0 .mini-player-box &.v-enter-active, &.v-leave-active transition: all 0.4s &.v-enter, &.v-leave-to transform: translate3d(0, 40px, 0) opacity: 0
這樣在fullScreen變量改變的時候,就會觸發(fā)【過渡】
這里我們完成第二種動畫
首先安裝插件 , npm i create-keyframe-animation
這個插件是用js寫css的keyframe動畫用的,至于為什么keyframe不在css里面寫呢?那是因?yàn)槠聊淮笮〔灰粯?,會?dǎo)致需要移動的px不一樣,所以要動態(tài)計算。
給 <transition> 添加動畫鉤子
<transition @enter="enter" @after-enter="afterEnter" @leave="leave" @after-leave="afterLeave" > <div class="cd-box" ref="cdWrapper" v-show="fullScreen"> <img src="../assets/bj.png" alt="" class="bg"> </div> </transition>
計算偏移量(中心點(diǎn)到中心的偏移,圖中紅線距離)
// 獲得偏移量,以及scale _getPosAndScale() { // 左下角圖片的寬度 const targetWidth = 40 // cd寬度 const width = 300 const scale = targetWidth / width // 這里的 x,y要算,過程省略,無非就是加加減減,這的x,y都是算出來了的 const x = -167.5 const y = 497 return {x ,y , scale} },
x,y的數(shù)值代表什么?見圖
這里x為什么是負(fù)的,y是正的呢?
因?yàn)?瀏覽器的坐標(biāo)系的中心點(diǎn)是在左上角
的,如圖
那么動畫從 cd中心到左下角,X偏移為負(fù),y偏移為正
然后用animations插件執(zhí)行動畫鉤子
// enter是指當(dāng) cd從隱藏到顯示的動畫, enter(el, done) { const {x, y, scale} = this._getPosAndScale() let animation = { // 第0幀的時候,先讓圖片縮小,顯示在右下角 0: { transform: `translate3d(${x}px, ${y}px, 0) scale(${scale})` }, // 60%的時候,讓圖片回到cd中心,變大 60: { transform: `translate3d(0 ,0 , 0) scale(1.1)` }, // 變回原來的尺寸,會有一個回彈的效果 100: { transform: `translate3d(0 ,0 , 0) scale(1)` } } // 動畫的一些配置 animations.registerAnimation({ name: 'move', animation, presets: { duration: 400, easing: 'linear' } }) //運(yùn)行動畫 animations.runAnimation(this.$refs.cdWrapper, 'move', done) }, afterEnter(){ //運(yùn)行完動畫之后,注銷掉動畫 animations.unregisterAnimation('move') this.$refs.cdWrapper.style.animation = '' }, // leave是指 cd從顯示到隱藏的動畫 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)聽transitionend 事件在 CSS 完成過渡后觸發(fā)done回調(diào) this.$refs.cdWrapper.addEventListener('transitionend', () => { done() }) }, afterLeave() { this.$refs.cdWrapper.style.transition = '' this.$refs.cdWrapper.style['transform'] = '' }
寫到這里,我們就把剛開始的效果給寫完啦!
但在寫js的keyframe的時候
我們還可以加上rotate,讓動畫效果有一個回彈效果
let animation = { 0: { transform: `translate3d(${x}px, ${y}px, 0) scale(${scale}) rotate(0deg)` }, 60: { transform: `translate3d(0 ,0 , 0) scale(1.1) rotate(365deg)` }, 100: { transform: `translate3d(0 ,0 , 0) scale(1) rotate(360deg)` } }
所有源碼
<template> <div class="index"> <transition @enter="enter" @after-enter="afterEnter" @leave="leave" @after-leave="afterLeave" > <div class="cd-box" ref="cdWrapper" v-show="fullScreen"> <img src="../assets/bj.png" alt="" class="bg"> </div> </transition> <button @click="switchMode" style="position:absolute;top:0;left:10px;">start</button> <transition> <div class="mini-box" v-show="!fullScreen"> <div class="mini-img"> <img src="../assets/bj.png" alt="" > </div> </div> </transition> </div> </template> <script> /* eslint-disable */ import animations from 'create-keyframe-animation' export default { components: {}, props: {}, data() { return { fullScreen: true } }, computed: {}, watch: {}, created() {}, mounted() { // const {x, y, scale} = this._getPosAndScale() console.log(this._getPosAndScale()) console.log(animations) }, methods: { switchMode() { this.fullScreen = !this.fullScreen }, _getPosAndScale() { const targetWidth = 40 const paddingLeft = 20 const paddingBottom = 20 const paddingTop = 0 const width = 300 const scale = targetWidth / width const x = -(window.innerWidth / 2 - paddingLeft) const y = window.innerHeight - paddingTop - paddingBottom - width / 2 return {x ,y , scale} }, enter(el, done) { const {x, y, scale} = this._getPosAndScale() let animation = { 0: { transform: `translate3d(${x}px, ${y}px, 0) scale(${scale}) rotate(0deg)` }, 60: { transform: `translate3d(0 ,0 , 0) scale(1.1) rotate(365deg)` }, 100: { transform: `translate3d(0 ,0 , 0) scale(1) rotate(360deg)` } } animations.registerAnimation({ name: 'move', animation, presets: { duration: 400, easing: 'linear' } }) 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})` // this.$refs.cdWrapper.style['transform'] = 'rotate(360deg)' // transitionend 事件在 CSS 完成過渡后觸發(fā) this.$refs.cdWrapper.addEventListener('transitionend', () => { done() }) }, afterLeave() { this.$refs.cdWrapper.style.transition = '' this.$refs.cdWrapper.style['transform'] = '' } } } </script> <style lang="stylus" scoped> .index background: #eee width: 100% height: 100% display : flex flex-direction: column justify-content : space-between align-items: center .cd-box display : flex justify-content : center align-items : center width: 300px height: 300px background: #eee border-radius: 50% &.v-enter-active, &.v-leave-active transition: all 0.4s &.v-enter, &.v-leave-to opacity: 0 .bg width: 300px height: 300px border-radius: 50% .mini-box position: absolute bottom: 0 right: 0 left: 0 display : flex align-items center border: 1px solid #555 width: 100% height: 40px box-sizing : border-box &.v-enter-active, &.v-leave-active transition: all 0.4s &.v-enter, &.v-leave-to transform: translate3d(0, 40px, 0) opacity: 0 .mini-img height: 40px width: 40px box-sizing : border-box img height: 100% width: 100% </style>
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
vue2基本響應(yīng)式實(shí)現(xiàn)方式之讓數(shù)組也變成響應(yīng)式
這篇文章主要介紹了vue2基本響應(yīng)式實(shí)現(xiàn)方式之讓數(shù)組也變成響應(yīng)式問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-04-04Vue3之toRefs和toRef在reactive中的一些應(yīng)用方式
這篇文章主要介紹了Vue3之toRefs和toRef在reactive中的一些應(yīng)用方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-03-03vue實(shí)現(xiàn)的上傳圖片到數(shù)據(jù)庫并顯示到頁面功能示例
這篇文章主要介紹了vue實(shí)現(xiàn)的上傳圖片到數(shù)據(jù)庫并顯示到頁面功能,結(jié)合實(shí)例形式分析了基于vue.js的數(shù)據(jù)庫操作及頁面圖片顯示相關(guān)操作技巧,需要的朋友可以參考下2018-03-03vue中用H5實(shí)現(xiàn)文件上傳的方法實(shí)例代碼
本篇文章主要介紹了vue中用H5實(shí)現(xiàn)文件上傳的方法實(shí)例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-05-05vue實(shí)現(xiàn)動態(tài)路由的方法及路由原理解析
這篇文章主要介紹了路由原理及vue實(shí)現(xiàn)動態(tài)路由,Vue Router 提供了豐富的 API,可以輕松地實(shí)現(xiàn)路由功能,并支持路由參數(shù)、查詢參數(shù)、命名路由、嵌套路由等功能,可以滿足不同應(yīng)用程序的需求,需要的朋友可以參考下2023-06-06vue?LogicFlow更多配置選項(xiàng)示例詳解
這篇文章主要為大家介紹了vue?LogicFlow更多配置選項(xiàng)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01vue項(xiàng)目中vue-i18n和element-ui國際化開發(fā)實(shí)現(xiàn)過程
這篇文章主要介紹了vue項(xiàng)目中vue-i18n和element-ui國際化開發(fā)實(shí)現(xiàn)過程,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2018-04-04