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


Posted in Javascript onApril 09, 2019

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

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>

计算偏移量(中心点到中心的偏移,图中红线距离)

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

// 获得偏移量,以及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的数值代表什么?见图

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

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

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

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

那么动画从 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>

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
YUI Compressor压缩JavaScript原理及微优化
Jan 07 Javascript
js获取RadioButtonList的Value/Text及选中值等信息实现代码
Mar 05 Javascript
js事件绑定快捷键以ctrl+k为例
Sep 30 Javascript
JavaScript中的类数组对象介绍
Dec 30 Javascript
JavaScript实现选择框按比例拖拉缩放的方法
Aug 04 Javascript
深入学习jQuery Validate表单验证
Jan 18 Javascript
微信小程序 网络API发起请求详解
Nov 09 Javascript
Node.js简单入门前传
Aug 21 Javascript
微信小程序 按钮滑动的实现方法
Sep 27 Javascript
JavaScript使用indexOf()实现数组去重的方法分析
Sep 04 Javascript
JS实现随机生成10个手机号的方法示例
Dec 07 Javascript
JavaScript点击按钮生成4位随机验证码
Jan 28 Javascript
基于three.js实现的3D粒子动效实例代码
Apr 09 #Javascript
Koa 中的错误处理解析
Apr 09 #Javascript
简单说说如何使用vue-router插件的方法
Apr 08 #Javascript
利用Bootstrap Multiselect实现下拉框多选功能
Apr 08 #Javascript
纯javascript实现选择框的全选与反选功能
Apr 08 #Javascript
详解小程序如何避免多次点击,重复触发事件
Apr 08 #Javascript
「中高级前端面试」JavaScript手写代码无敌秘籍(推荐)
Apr 08 #Javascript
You might like
PHP 中dirname(_file_)讲解
2007/03/18 PHP
php UBB 解析实现代码
2011/11/27 PHP
深入php中var_dump方法的使用详解
2013/06/24 PHP
smarty模板引擎基础知识入门
2015/03/30 PHP
CI分页类首页、尾页不显示的解决方法
2016/03/28 PHP
PHP验证终端类型是否为手机的简单实例
2017/02/07 PHP
提高网站信任度的技巧
2008/10/17 Javascript
如何确保JavaScript的执行顺序 之jQuery.html并非万能钥匙
2011/03/03 Javascript
最佳6款用于移动网站开发的jQuery 图片滑块插件小结
2012/07/20 Javascript
将json对象转换为字符串的方法
2014/02/20 Javascript
js的[defer]和[async]属性
2014/11/24 Javascript
JS检测移动端横竖屏的代码
2016/05/30 Javascript
JS正则表达式学习之贪婪和非贪婪模式实例总结
2016/12/26 Javascript
JS实现快速比较两个字符串中包含有相同数字的方法
2017/09/11 Javascript
jquery中ajax请求后台数据成功后既不执行success也不执行error的完美解决方法
2017/12/24 jQuery
Angular异步变同步处理方法
2018/08/13 Javascript
bootstrap table实现合并单元格效果
2018/12/24 Javascript
vue基于viewer实现的图片查看器功能
2019/04/12 Javascript
jQuery 常用特效实例小结【显示与隐藏、淡入淡出、滑动、动画等】
2020/05/19 jQuery
通过实例了解JS执行上下文运行原理
2020/06/17 Javascript
[48:31]DOTA2-DPC中国联赛 正赛 Dynasty vs XG BO3 第一场 2月2日
2021/03/11 DOTA
Python中的descriptor描述器简明使用指南
2016/06/02 Python
python 公共方法汇总解析
2019/09/16 Python
python 匿名函数与三元运算学习笔记
2020/10/23 Python
HTML最新标准HTML5总结(必看)
2016/06/13 HTML / CSS
利用Node实现HTML5离线存储的方法
2020/10/16 HTML / CSS
美国娱乐和流行文化商品店:FYE
2017/09/14 全球购物
应届生幼儿园求职信
2013/11/12 职场文书
网络技术专业推荐信
2014/02/20 职场文书
服务标语口号
2014/07/01 职场文书
厨师长岗位职责范本
2014/08/25 职场文书
继续教育个人总结
2015/03/03 职场文书
2015年财务人员工作总结
2015/04/10 职场文书
企业党建工作总结2015
2015/05/26 职场文书
php引用传递
2021/04/01 PHP
本地搭建minio文件服务器(使用bat脚本启动)的方法
2022/07/15 Servers