Vue动画实例代码分析

这篇“Vue动画实例代码分析”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Vue动画实例代码分析”文章吧。

成都创新互联公司服务项目包括利通网站建设、利通网站制作、利通网页制作以及利通网络营销策划等。多年来,我们专注于互联网行业,利用自身积累的技术优势、行业经验、深度合作伙伴关系等,向广大中小型企业、政府机构等提供互联网行业的解决方案,利通网站推广取得了明显的社会效益与经济效益。目前,我们服务的客户以成都为中心已经辐射到利通省份的部分城市,未来相信会继续扩大服务区域并继续获得客户的支持与信任!

Vue 动画

CSS 过渡

以显示和隐藏动画为例:


  
    Toggle
  
  
    hello

  
从隐藏到显示

当动画开始的时候,P 标签还是隐藏的,此时 Vue 会给 P 加上两个class:

.fade-enter {
   opacity: 0;
}
.fade-enter-active {
   transition: opacity 0.5s;
}

Vue动画实例代码分析

当动画开始后,会移除.fade-enter(在元素被插入之前生效,在元素被插入之后的下一帧移除),这个时候 P 标签的 opacity就恢复到 1,即显示,这个时候就会触发transition , 检测到opacity的变化,就会产生动画。当动画结束后,会移除 Vue 加上的 class(v-enter-to, v-enter-active)。

Vue动画实例代码分析

上面这个过程是怎么实现的呢?它主要用到了requestAnimationFrame这个api,我们自己可以实现一个简易版的动画,当生成下一帧的时候添加或删除某个类,从而形成动画效果。



  
    Document
    
      .box {
        width: 100px;
        height: 100px;
        background-color: red;
      }
      .enter {
        opacity: 0;
      }
      .mov {
        transition: opacity 5s linear;
      }
    
  
  
    
       

当生成下一帧的时候,会移除enter这个class,那么 div 就会显示出来,就会触发transition产生动画效果。

从显示到隐藏

当隐藏的时候也产生动画,如下图:

Vue动画实例代码分析

.fade-leave-to {
   opacity: 0;
}
.fade-leave-active {
   transition: opacity 0.5s;
}

刚开始 P 标签是显示的,因为此时fade-leave (在离开过渡被触发时立刻生效,下一帧被移除) 的样式是 opacity 是 1,执行到第二帧的时候加上fade-leave-to(在离开过渡被触发之后下一帧生效 ,与此同时 fade-leave 被删除),此时opacity 是 0,既然发生了属性的变化,transition就会监听到,从而形成动画。

这样显示和隐藏就形成了一个完整的动画。

原理是当你通过点击事件改变css属性,比如opacity时,transition会检测到这个变化,从而形成动画

CSS 动画

CSS 动画原理同 CSS 过渡类似,区别是在动画中 v-enter 类名在节点插入 DOM 后不会立即删除,而是在 animationend 事件触发时删除。




  Toggle show
  
    
      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris
      facilisis enim libero, at lacinia diam fermentum id. Pellentesque
      habitant morbi tristique senectus et netus.
    

  

当我们点击改变showfalse时,会在 P 标签上添加.bounce-leave-active这个class,这个类就会执行animation 动画,当动画执行完成后删除.bounce-leave-active

JavaScript 钩子动画



  
    ...
    
    
  
  
    
      
        Toggle
      
      
        
          Demo
        

      
    
       

Javascript 钩子动画一般用在比较复杂的动画上,而不是简单的过渡动画。后面我们会用很大的篇幅通过几个例子来说明用 Javascript 钩子动画是如何完成复杂动画的。

初始渲染的过渡

因为CSS过渡动画需要有个触发条件,比如opacity必须有一个变化,如果没有变化就不会触发。那么,可以通过 appear attribute 设置节点在初始渲染的过渡:


    

CSS 动画(animation)则不需要触发条件。

多个元素的过渡

一旦涉及到多个元素的过渡,那么就会出现旧元素和新元素进出的先后问题。 的默认行为是进入和离开同时发生,但是这样就会产生一些不协调的效果,所以 Vue 提供了过渡模式


    

但是这两个模式并不能完全满足实际需要,实际上我们可以定制我们要想的先后效果,比如后台管理系统中有一个面包屑导航栏,当改变路由的时候需要更改面包屑里面的内容,那么这个更改的动画可以让旧的元素向左滑出,新的元素从右边滑入。


  
           Toggle        
       // 一定要设置key            if Demo          else demo   

当有相同标签名的元素切换时,需要通过 key attribute 设置唯一的值来标记以让 Vue 区分它们,否则 Vue 为了效率只会替换相同标签内部的内容。即使在技术上没有必要,给在 transition 组件中的多个元素设置 key 是一个更好的实践。

多个组件的过渡

多个组件的过渡简单很多 - 我们不需要使用 key attribute。相反,我们只需要使用动态组件:


    


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; }

列表过渡

上面讲的动画都是针对单个节点,或者同一时间渲染多个节点中的一个,那么怎么同时渲染整个列表,比如使用 v-for

在这种场景中,使用 组件,这个组件的几个特点:

后面我们会通过一个例子演示如何使用

案例

路由动画

在后台管理系统中,当路由变化时,对应的组件内容也会发生变化,当在变化时加上一个动画,让整个页面效果更加自然。


  // 这里加了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 页面弹框从底部弹出动画

在 H5 页面开发中,一个常用的功能是点击一个按钮,隐藏的内容从屏幕底部弹出,同时弹出的时候有个动画效果,一般是缓慢上升。


  
    // 外面一层遮罩
    
      // 这里面才是内容
      
    
  

  
    Add
  


这个动画有两层,一层是最外层的内容,另一层是最里面部分的内容

列表删除动画

在 H5 页面开发中,如果在一个列表页中删除其中一个子项,要求有一个删除动画效果。


  
    
      
      
        delete
      
    
  


.item {
  height: 40px;
}
.list-enter-active,
.list-leave-active {
  transition: all 0.1s
}
.list-enter,
.list-leave-to {
  height: 0
}

复杂动画-案例1

一般复杂的动画,并不能用简单的 css 过渡或者 css 动画能实现的,需要使用 javascript钩子动画实现。

针对上面图片的动画进行拆解:

代码结构:

                               // 顶部区域...         
        
          // 中间区域...            
                   // 底部区域...                                           // 内容区域...              

实现第一个动画效果

// 这是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)

通过第一章节部分的学习,看懂这段动画代码应该不难。

实现第二个动画效果

要实现这个动画效果,必须要计算左下角的圆到中心部分的圆的 x 轴和 y 轴方向上的距离,因为H5页面在不同的手机屏幕下,这个距离是不同的,所以一开始就不能写死,只能通过 javascript 去动态的获取。

// 计算从小圆中心到大圆中心的距离以及缩放比例
_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
  }
}

这段代码细节可以不用看,只需要知道它是计算左下角小圆的原心到中心部分圆的原心的距离(x, y),以及根据圆的直径获取放大缩小倍数(scale)。

// 这个库可以让我们使用js来创建一个keyframe的动画,为什么要用js来生成呢?这是因为有些变化的属性需要动态的计算,而不是一开始就定好了
import animations from 'create-keyframe-animation'


// 动画钩子
// done:当动画执行完后执行done函数,然后跳到afterEnter钩子函数
enter(el, done) {
  const { x, y, scale } = this._getPosAndScale()
  // 对于大圆来说,进入的时机就是从小圆到小圆
  let animation = {
    0: {
      // 一开始大圆相对于小圆的位置,所以x为负数,y为整数
      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,并执行动画
  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})`
  // 这样写的目的是如果没有监听到动画结束的事件,那么我们自己就写一个定时器,400ms后执行done函数
  const timer = setTimeout(done, 400)
  // 监听动画结束
  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

至此,就完成小圆和大圆的联动动画,整体效果还是很惊艳的。

实现第三个动画

// 模板部分

  


// 逻辑部分
// 通过事件来控制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)

复杂动画-案例2

首先对动画进行拆解:

实现第一个动画

通过上面的学习,对这一个动画的实现应该不难。代码如下:


  // 小圆 - 
  
    0" @click.stop="decrease">
       - 
    
  
  0">{{food.count}}
  // 小圆 + 
   + 


.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)

实现第二个动画


  
    
      
        
      
    
  


这里创建是10个小球,小球开始的状态都是隐藏的。

// 点击加号调用这个函数,同时把加号的dom传递,这样就能知道小球运动的起点位置
onAdd(target) {
  // shopCart就是图中底部组件,执行drop函数
  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表示正在下落的小球,因为当快速点击时,会触发多个小球下落
      this.dropBalls.push(ball)
      return
    }
  }
},

因为小球的ball.show为true,那么就会触发对应的动画钩子函数,首先触发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)`
}

接着执行enter事件函数dropping:

dropping(el, done) {
  // 触发浏览器重绘,把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)`
  // 监听动画结束
  el.addEventListener('transitionend', done)
}

最后执行after-enter事件函数afterDrop:

afterDrop(el) {
  // 取出第一个小球,设置属性show为false,同时要设置el.style.display = 'none'
  const ball = this.dropBalls.shift()
  if (ball) {
    ball.show = false
    el.style.display = 'none'
  }
}

我们来梳理下流程:

.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

所以函数的执行顺序是:drop -> before-enter -> enter -> after-enter -> drop -> before-enter -> enter -> after-enter...,这样就形成了在页面中同时出现多个小球的动画。

注意:当我们设置show的属性为false就可以了,但是代码中同时也设置了el.style.display = 'none',如果不设置这个小球消失有一个延迟。

以上就是关于“Vue动画实例代码分析”这篇文章的内容,相信大家都有了一定的了解,希望小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注创新互联行业资讯频道。


文章标题:Vue动画实例代码分析
网站路径:http://scyanting.com/article/jgoeep.html

其他资讯