Vue.js --- transition 过渡动画

在 Vue 中,为了方便操作 DOM 的过渡和动画,Vue 封装了一个 transition 组件,以 Vue 专属标签的形式使用,在下列情形中,可以给任意元素或组件添加过渡/动画

  1. 条件渲染(v-if
  2. 条件展示(v-show
  3. 动态组件
  4. 组件根节点

当插入或删除在 transition 组件中的元素时,Vue 会做以下处理:

  1. 如果 transition 元素应用了 CSS 过渡 / 动画,在适当的时机添加或移除类名
  2. 如果元素挂载了 JavaScript 钩子函数,在适当的时机调用
  3. 如果既没有 CSS 类名,又没有钩子函数,则直接在浏览器逐帧动画的下一帧中被立即执行

transition 的使用

一般 transition 的使用,可以分为两个状态:

  1. 进入状态(过渡进入):v-if / v-show 的值从 false 变为 true 的过程
  2. 离开状态(过渡离开):v-if / v-show 的值从 true 变为 false 的过程
过渡的类名

根据官方文档,过渡的类名一共有 6 个:

  1. v-enter :定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧被移除
  2. v-enter-active :定义进入过渡生效时的状态。再整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成后被移除。这个类可以定义进入过渡的时间、延迟和速度曲线
  3. v-enter-to :定义进入过渡的结束状态,在元素被插入的下一帧生效(此时 v-enter 被移除),在过渡/动画完成后被移除
  4. v-leave :定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除
  5. v-leave-active :定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成后被移除。这个类可以定义离开过渡的时间、延迟和速度曲线
  6. v-leave-to :定义离开过渡的结束状态。在离开过渡被触发下一帧生效(此时 v-leave 被移除),在过渡/动画完成后移除

前三项属于过渡进入,后三项属于过渡离开。

官方已经明确了这 6 个类名的执行时间,下方的图可以更好地方便理解:
transition 过程
transition 过程

对于这些过渡的类名,如果 transition 标签没有名字,则 v- 是这些类名的前缀,如果 transition 有名字,例如:name="my-transition",那么 v- 将会被替换为 my-transition- ,例如:my-transition-enter

举个简单的案例:

<template>
  <div>
    <button type="button" @click="show = !show">往返动画按钮</button>
    <transition name="my-transition">
      <p v-show="show">我是要进行往返动画的文字</p>
    </transition>
  </div>
</template>

<script>
export default {
  data() {
    return {
      show: true
    };
  }
};
</script>

<style>
.my-transition-leave-to,
.my-transition-enter {
  opacity: 0;
}

.my-transition-enter-active,
.my-transition-leave-active {
  transition: opacity 1s;
}
</style>
JavaScript 钩子函数

使用 JavaScript 钩子函数,首先需要在 transition 元素上绑定一些 attribute

  1. v-on:before-enter / @before-enter
    表示进入动画开始之前,此时,动画尚未开始,可以在此处设置动画的初始样式

  2. v-on:enter / @enter
    表示进入动画开始之后,在这里设置动画开始之后的结束状态,还可以调用动画结束之后的回调函数

  3. v-on:after-enter / @after-enter
    表示进入动画结束之后的回调函数

  4. v-on:enter-cancalled / @enter-cancalled

  5. v-on:before-leave / @before-leave
    表示离开动画开始之前

  6. v-on:leave / @leave
    表示离开动画开始之后

  7. v-on:after-leave / @after-leave
    离开动画结束后得回调

  8. v-on:leave-cancalled / @leave-cancalled
    只适用于 v-show ,过渡停止

前 4 项属于过渡进入,后 4 项属于过渡离开。

举个实例:

<template>
  <div>
    <button type="button" @click="show = !show">动画按钮</button>
    <transition
      name="my-transition"
      @before-enter="beforeEnter"
      @enter="enter"
      @after-enter="afterEnter"
    >
      <p v-show="show">我是要进行半程动画的文字</p>
    </transition>
  </div>
</template>

<script>
export default {
  data() {
    return {
      show: false
    };
  },
  methods: {
	// 参数 el 是绑定对象的 DOM	
    beforeEnter(el) {
      console.log("开始");
      el.style.opacity = 0;
      el.style.transform = "translateX(0)";
    },
    // done 其实就是 afterEnter,这里的 done 函数是对 afterEnter 的引用
    enter(el, done) {
      console.log("进行中");

	  // el.offsetWidth 会强制动画刷新,没有实际的作用,但是,如果不写,出不来动画效果;
      el.offsetWidth;

      el.style.opacity = 1;
      el.style.transform = "translateX(100px)";
      el.style.transition = "all 1s";

      el.addEventListener("transitionend", done);
    },
    afterEnter(el) {
      console.log("结束-并恢复");
      this.show = false;
    }
  }
};
</script>

<style>
</style>

那么这里就会有一个问题,问什么没有 el.offsetWidth 就没有动画效果呢?从网上查阅到资料可知,根本原因是因为浏览器在更新视图的时候,会在当前 js 对 style 进行修改之后,对所有更改统一进行更新,这时,v-showdisplay: none(不支持 transition 效果)会对其他支持 transition 效果的属性有影响,导致过渡效果失效。解决方法有两种:

  1. 添加一行代码 el.offsetWidth ,因为这会引起 CSS 的重绘,导致被操作元素的 display 属性先一步变为 block
  2. 将过渡效果的代码包到 setTimeout 中,利用异步使 display 属性变化先生效。
使用第三方样式

transition 的过渡类名,支持我们用自定义的方式进行声明。我们可以通过以下这些 transition 标签的 attribute 来自定义过渡的类名:

  1. enter-class
  2. enter-alive-class
  3. enter-to-class
  4. leave-class
  5. leave-alive-class
  6. leave-to-class

他们的优先级高于普通类名,这对于 Vue.js 的过渡系统,和其他第三方 css 动画库结合使用十分方便,例如 Animate.css 。

初始化动画

以上说到的几种动画,都是需要执行某种操作之后,动画效果才能出发,那么有没有页面加载完毕后就能立即出发的动画呢?除了 animation 之外,还可以使用 transition 标签的 appear 属性。可以使用自定义类名JavaScript 钩子函数两种设置方式。

自定义类名:

<template>
  <div>
    <transition
      appear
      appear-class="custom-appear-class"
      appear-to-class="custom-appear-to-class"
      appear-active-class="custom-appear-active-class"
    >
      <p>我是初始化动画文字</p>
    </transition>
  </div>
</template>

<script>
export default {};
</script>

<style>
.custom-appear-class {
  transform: translateX(100px);
}
.custom-appear-active-class {
  transition: all 0.5s;
}
</style>

JavaScript 钩子函数:

<template>
  <div>
    <transition
      appear
      @before-appear="beforeAppear"
      @appear="appear"
      @after-appear="afterAppear"
  	  @appear-cancelled="appearCancelled"
    >
      <p>我是初始化动画文字</p>
    </transition>
  </div>
</template>

<script>
export default {
  methods: {
    beforeAppear(el) {},
    appear(el, done) {
      el.offsetWidth;
      el.style.transform = "translateX(100px)";
      el.style.transition = "all 1s";

      el.addEventListener("transitionend", done);
    },
    afterAppear(el) {},
    appearCancelled(el) {}
  }
};
</script>

<style></style>

多元素过渡 和 多组件过渡

对于在多个元素之间相互切换的过程中,也可以使用 transition 设置过渡动画:

<transition name="my-transition">
  <p @click="show = !show" v-if="show">on</button>
  <button @click="show = !show" v-else>off</button>
</transition>

值得注意的是,如果进行切换的几个标签名相同,需要利用 key 属性来让 Vue 区分它们,否则,Vue 为了效率只会替换相同标签内部的内容:

<transition name="my-transition">
  <button @click="show = !show" v-if="show" key="on">on</button>
  <button @click="show = !show" v-else key="off">off</button>
</transition>

在切换过程中,还会引出另一个问题,两个元素在切换过程中,一个离开过渡的时候另一个开始进入过渡。这是因为 transition 的默认行为 - 进入和离开同时发生。显然,同时生效的进入和离开的过渡不能满足所有要求,所以 Vue 提供了过渡模式 mode 属性:

  1. in-out :先进行过渡进入,完成之后再进行过渡离开。
  2. out-in :先进行过渡离开,完成之后再进行过渡进入。

例如:

<transition name="my-transition" mode="out-in">
  <button @click="show = !show" v-if="show" key="on">on</button>
  <button @click="show = !show" v-else key="off">off</button>
</transition>

相对于多元素过渡,多组件过渡就显得简单很多,我们不需要使用 key 属性进行区分,可以使用动态组件:

<transition name="my-transition" mode="out-in">
  <component :is="flag ? component1 : component2"></component>
</transition>

列表过渡

目前为止,关于过渡我们已经讨论了:

  1. 单个节点
  2. 同一时间渲染多个节点中的一个(多元素 和 多组件过渡)

那么怎么同时渲染整个列表,比如使用 v-for ?在这种场景中,要使用 transition-group 。使用之前,要先注意以下几点:

  1. 不同于 transition ,它会以一个真实元素呈现:默认为一个 span 。可以通过 tag 属性更换为其他元素。
  2. 过渡模式不可用,因为我们不再相互切换特有的元素。
  3. 内部元素总是需要提供唯一的 key attribute 值。
  4. CSS 过渡的类将会应用在内部的元素中,而不是这个组 / 容器本身。

在列表过渡中,进入和离开的过渡依旧使用之前一样的 CSS 类名。例如:

<template>
  <div>
    <button @click="add">Add</button>
    <button @click="remove">Remove</button>
    <transition-group name="list" tag="p">
      <span v-for="item in items" :key="item" class="list-item">{{ item }}</span>
    </transition-group>
  </div>
</template>

<script>
export default {
  data() {
    return {
      items: [1, 2, 3, 4, 5, 6, 7, 8, 9],
      nextNum: 10
    };
  },
  methods: {
    randomIndex: function() {
      return Math.floor(Math.random() * this.items.length);
    },
    add: function() {
      this.items.splice(this.randomIndex(), 0, this.nextNum++);
    },
    remove: function() {
      this.items.splice(this.randomIndex(), 1);
    }
  }
};
</script>

<style>
.list-item {
  display: inline-block;
  margin-right: 10px;
}

.list-enter,
.list-leave-to {
  opacity: 0;
  transform: translateY(30px);
}

.list-enter-active,
.list-leave-active {
  transition: all 0.5s;
}
</style>

组件还有一个特殊之处。不仅可以进入和离开动画,还可以改变定位。要使用这个新功能只需了解新增的 v-move 类名,它会在元素的改变定位的过程中应用。像之前的类名一样,可以通过 name 属性来自定义前缀,也可以通过 move-class 属性手动设置。

v-move 对于设置过渡的切换时机和过渡曲线非常有用,你会看到如下的例子:

<template>
  <div>
    <button v-on:click="shuffle">shuffle</button>
    <transition-group name="list" tag="ul">
      <li v-for="item in items" :key="item" class="list-item">{{ item }}</li>
    </transition-group>
  </div>
</template>

<script>
export default {
  data() {
    return {
      items: [1, 2, 3, 4, 5, 6, 7, 8, 9]
    };
  },
  methods: {
    shuffle: function() {
      this.items = this.items.sort(() => Math.random() - 0.5);
    }
  }
};
</script>

<style>
.list-move {
  transition: transform 1s;
}
</style>
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值