likes
comments
collection
share

vue3中JavaScript 动画的使用

作者站长头像
站长
· 阅读数 16

在上一篇文章vue3中transition-group的使用中,我们使用transition-group对列表使用了动画,现在又有一个新需求,当删除时需要实现一个图标飞到废纸篓的动画,如图所示:

vue3中JavaScript 动画的使用

要实现这个效果,使用css过渡是无法实现的,因此就需要用到vue提供的JavaScript动画了。

实现的思路也很简单,我们放一个单独存在的动画元素并且藏起来,也就是那个图标文件。当点击删除图标的时候,我们把这个动画元素移动到鼠标的位置,再飞到废纸篓里藏起来就可以了。

在 Vue 的 transition 组件里,我们可以分别设置 before-enterenterafter-enter 三个函数来更精确地控制动画。

<template>
    <span class="dustbin">
      🗑
    </span>
<div class="animate-wrap">
    <transition @before-enter="beforeEnter" @enter="enter" @after-enter="afterEnter">
        <div class="animate" v-show="animate.show">
            📋
        </div>
    </transition>
</div>
</template>

<script setup>

let animate = reactive({
  show:false,
  el:null
})
function beforeEnter(el){
      let dom = animate.el
      let rect = dom.getBoundingClientRect()
      let x = window.innerWidth - rect.left - 60
      let y = rect.top - 10
      el.style.transform = `translate(-${x}px, ${y}px)`
}
function enter(el,done){
      document.body.offsetHeight
      el.style.transform = `translate(0,0)`
      el.addEventListener('transitionend', done)
}
function afterEnter(el){
      animate.show = false
      el.style.display = 'none'
}
function removeTodo(e,i){
  // 把当前删除的元素赋值给动画el属性
  animate.el = e.target
  // 图标文件显示
  animate.show = true
  // 删除
  todos.value.splice(i,1)
}
</script>
<style>
.dustbin {
  font-size: 20px;
  position: fixed;
  right: 10px;
  top: 10px;
}
.animate-wrap .animate{
    position :fixed;
    right :10px;
    top :10px;
    z-index: 100;
    // 这里仍然使用了css的过渡
    transition: all 0.5s linear;
}
</style>

当删除其中一项,把当前的dom元素复制给animate.el,同时设置animate.show = true,此时图标文件显示,当显示时就会执行动画钩子函数:

beforeEnter

这个函数主要做了两件事:

  1. 计算删除元素在页面中的位置dom.getBoundingClientRect()
  2. 把图标文件通过transform移动到删除元素所在位置

enter

这个函数也主要做了两件事:

  1. 首先执行document.body.offsetHeight,强制浏览器重绘一次,把图标文件显示在删除元素的位置上。
  2. 然后把图标文件移动到原有位置translate(0,0),最后监听动画结束事件。

afterEnter

这个函数主要是让图标文件隐藏,这里两行代码的功能是一样的:

animate.show = false
el.style.display = 'none'

那为什么要写两次呢?只写animate.show = false不行吗?

如果只写animate.show = false,图标文件移动垃圾桶后会有一个停顿然后消失,如果加上el.style.display = 'none'图标文件一到垃圾桶后会立刻消失,不会产生停顿,这样动画更加顺滑。

这样就完成了吗?当我们删除最后一个时,会产生一个bug,如图所示:

vue3中JavaScript 动画的使用

发现,删除最后一个时图标文件跑到左上角去了?这是为什么呢?

因为当只有最后一项时,删除它执行渲染操作,同时animate.show = true,此时动画的元素图标文件出现,执行beforeEnter函数,此时要删除的元素已经被删除了,所以dom.getBoundingClientRect()的位置变为(0, 0),因为最后一项已经没有了。

因此el.style.transform = translate(-${x}px, ${y}px)会在左上角出现。怎么办呢?

可以先让图标文件先出现,然后在下一个tick执行删除todos.value.splice(i, 1)操作即可。

setTimeout(() => {
      todos.value.splice(i, 1)
    }, 20)

除了使用setTimeout外,还可以使用nextTick()

转载自:https://juejin.cn/post/7238423148554616893
评论
请登录