點`start`的時候,咱們把整個動畫拆分爲兩種效果(過渡和動畫)。
1. 中間cd消失,下方播放條顯示,這是屬於`過渡`
2. `過渡`開始的同時,cd同時移動、放大、縮小到左下方播放條 ,這屬於`動畫`
上面的效果是【過渡】加【動畫】同時使用完成的
複製代碼
v-enter、v-leave-to、v-enter-active、v-leave-enter
便可完成keyframe
來完成了。<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>
// 下面播放狀態框
<div class="mini-player-box" v-show="!fullScreen">
// 狀態看裏面的圖片 (動畫的時候圖片結束位置)
<div class="mini-img">
<img src="../assets/bj.png" alt="" >
</div>
</div>
</transition>
</div>
</template>
複製代碼
結構很簡單,基本就是兩個大div
,而後把div的佈局按效果圖那些佈置。javascript
.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變量改變的時候,就會觸發【過渡】css
npm i create-keyframe-animation
這個插件是用js寫css的keyframe動畫用的,至於爲何keyframe不在css裏面寫呢?那是由於屏幕大小不同,會致使須要移動的px不同,因此要動態計算。<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>
複製代碼
// 得到偏移量,以及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的數值表明什麼?見圖 html
由於瀏覽器的座標系的中心點是在左上角
的,如圖vue
那麼動畫從cd中心到左下角,X偏移爲負,y偏移爲正java
// enter是指當 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'
}
})
//運行動畫
animations.runAnimation(this.$refs.cdWrapper, 'move', done)
},
afterEnter(){
//運行完動畫以後,註銷掉動畫
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})`
// 監聽transitionend 事件在 CSS 完成過渡後觸發done回調
this.$refs.cdWrapper.addEventListener('transitionend', () => {
done()
})
},
afterLeave() {
this.$refs.cdWrapper.style.transition = ''
this.$refs.cdWrapper.style['transform'] = ''
}
複製代碼
寫到這裏,咱們就把剛開始的效果給寫完啦!npm
但在寫js的keyframe的時候瀏覽器
咱們還能夠加上rotate,讓動畫效果有一個回彈效果app
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 完成過渡後觸發 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>
複製代碼