首页 > 编程 > JavaScript > 正文

Vue中使用create-keyframe-animation与动画钩子完成复杂动画

2019-11-19 11:51:20
字体:
来源:转载
供稿:网友

本篇文章主要介绍了 Vue中使用create-keyframe-animation与动画钩子完成复杂动画,分享给大家

 

如何实现这个动画?

 效果分析

点`start`的时候,我们把整个动画拆分为两种效果(过渡和动画)。

1. 中间cd消失,下方播放条显示,这是属于`过渡`

2. `过渡`开始的同时,cd同时移动、放大、缩小到左下方播放条 ,这属于`动画`

上面的效果是【过渡】加【动画】同时使用完成的

  • 对于第一种【过渡】,我们用vue中transition标签,加设置v-enter、v-leave-to、v-enter-active、v-leave-enter即可完成
  • 对于第二种【动画】,我们就要用keyframe来完成了。

这里我们先完成第一种过渡

vue中模板节点

<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的布局按效果图那些布置。

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变量改变的时候,就会触发【过渡】

这里我们完成第二种动画

首先安装插件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的数值代表什么?见图

 

这里x为什么是负的,y是正的呢?

因为 浏览器的坐标系的中心点是在左上角 的,如图

那么动画从 cd中心到左下角,X偏移为负,y偏移为正

然后用animations插件执行动画钩子

 // 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'] = '' }

写到这里,我们就把刚开始的效果给写完啦!

但在写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 完成过渡后触发  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>

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持武林网。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表