vue 之 Transition && 各种动画实现,一文让你会动画

在开发中,如果没有动画的话,整个页面内容的显示和隐藏会非常的生硬!不好看,为了给予一定的用户体验,进入今天的主题

  • React框架本身并没有提供任何动画相关的API,所以如果需要使用的话可以使用一个第三方库react-transition-group
  • Vue中为我们提供了一些内置的组件和对应的API来完成动画 

一、Transition组件

1.Transition组件的原理

插入或删除包含在transition组件中的元素时,vue将会做以下处理

就是 : 会自动把类加入到 transition组件下的根元素中,transition里面只能放单个标签( 组件 )

添加或者删除的class,常用的是如下六个 

进入 :

离开 : 


2、Transition组件中使用transition

代码

<template>
  <div class="box">
    <button @click="isShow = !isShow">切换</button>
    <!-- 用 transition 包裹一下,取个名字run,类名的前缀就是run-->
    <transition name="run">
      <!-- 内部元素或组件的显示和隐藏,会触发transition效果 -->
      <div v-if="isShow">
        <span>123123123123</span>
      </div>
    </transition>
  </div>
</template>

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

<style lang="scss" scoped>
// 元素开始进入的状态 | 元素离开结束的状态
.run-enter-from,
.run-leave-to {
  opacity: 0;
}
// 元素进入结束的状态 | 元素开始离开的状态。     这里不写也可以!!!!!!
.run-enter-to,
.run-leave-from {
  opacity: 1;
}
// 元素进入 | 结束时,过渡的效果
.run-enter-active,
.run-leave-active {
  // 过渡动画的使用
  transition: opacity 2s linear 0s;
}
</style>

效果 

说明


3、Transition组件中使用animation

代码

<style lang="scss" scoped>
/**
* 如果是动画效果,不止一个状态改变
* 就只需设置动态jike
* */
.run-enter-active {
  animation: run-scale 1s linear 0s;
}
// 离开的时候设置成相反哒
.run-leave-active {
  animation: run-scale 1s linear 0s reverse;
}
@keyframes run-scale {
  0% {
    transform: scale(0);
  }
  50% {
    transform: scale(1.3);
  }
  100% {
    transform: scale(1);
  }
}
</style>

效果


4、Transition组件的type属性

发生在 : 当我们同时使用transition和animation的情况下

代码

<style lang="scss" scoped>
// 元素开始进入的状态 | 元素离开结束的状态
.run-enter-from,
.run-leave-to {
  opacity: 0;
}
// 元素进入结束的状态 | 元素开始离开的状态
.run-enter-to,
.run-leave-from {
  opacity: 1;
}
// 元素进入 | 结束时,过渡的效果
.run-enter-active,
.run-leave-active {
  // 过渡动画的使用
  transition: opacity 2s linear 0s;
}
.run-enter-active {
  animation: run-scale 2s linear 0s;
}
// 离开的时候设置成相反哒
.run-leave-active {
  animation: run-scale 2s linear 0s reverse;
}
@keyframes run-scale {
  0% {
    transform: scale(0);
  }
  50% {
    transform: scale(1.3);
  }
  100% {
    transform: scale(1);
  }
}
</style>

效果

tip : Vue为了知道过渡的完成,内部是在监听transitionend和animationend

如果只使用了其中一个,Vue能自动识别类型和设置监听

但如果同时使用,可能会出现问题

  • 可能某一个动画执行结束,另外一个动画还没有结束 ( 时间设置的不一样 )

解决方案

  • 设置type属性为transition和animation来明确指定告知vue监听的类型,以哪个时间为准
<template>
  <div class="box">
    <button @click="isShow = !isShow">切换</button>
    <!-- 这里指定以哪个时间为标准 -->
    <transition name="run" type="transition || animation">
      <div v-if="isShow">
        <span>123123123123</span>
      </div>
    </transition>
  </div>
</template>

题外话 :其实一般设置一样的时间,而且,有动画了为啥要设置过渡呢,是个神奇的属性~


5、Transition组件的duration属性( 用的比较少 )

基本设置 : 同时设置进入和离开的时间

<transition name="run" :duration="1000">
  <div v-if="isShow">
    <span>123123123123</span>
  </div>
</transition>

一旦这么使用了,css中设置的 transitionend和animationend的时间就无效了,以duration时间为准

对象设置 : 分别设置进入和离开的时间

<transition name="run" :duration="{ enter: 800, leave: 1000 }">
  <div v-if="isShow">
    <span>123123123123</span>
  </div>
</transition>

6、 Transition组件的mode属性

如果transition组件中包裹的是两个元素( 组件 ),相互显示,就会发生很丑的事情

代码

<template>
  <div class="box">
    <button @click="isShow = !isShow">切换</button>
    <transition name="run">
      <div v-if="isShow">
        <span>one</span>
      </div>
      <div v-else>
        <span>two</span>
      </div>
    </transition>
  </div>
</template>

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

<style lang="scss" scoped>
.run-enter-active {
  animation: run-scale 2s linear 0s;
}
// 离开的时候设置成相反哒
.run-leave-active {
  animation: run-scale 2s linear 0s reverse;
}
@keyframes run-scale {
  0% {
    transform: scale(0);
  }
  50% {
    transform: scale(1.3);
  }
  100% {
    transform: scale(1);
  }
}
</style>

效果 

缘由和解决

原因 : 因为默认情况下,进入和离开动画,是同时发生的

解决 : 设置mode属性

<transition name="run" mode="in-out || out-in">
  <div v-if="isShow">
    <span>one</span>
  </div>
  <div v-else>
    <span>two</span>
  </div>
</transition>

mode : in-out

mode : out-in


7、 Transition组件的appear属性

如果想要一刷新页面就展示动画,需加上

<transition name="run" mode="out-in" appear>
  <div v-if="isShow">
    <span>one</span>
  </div>
  <div v-else>
    <span>two</span>
  </div>
</transition>

8、 Transition组件的回调函数

<transition
  @before-enter="beforeEnter"
  @enter="enter"
  @after-enter="afterEnter"
  @enter-cancelled="enterCancelled"
  @before-leave="beforeLeave"
  @leave="leave"
  @after-leave="afterLeave"
  @leave-cancelled="leaveCancelled"
  :css="false"
>
  <div v-if="isShow">
    <span>one</span>
  </div>
</transition>

:css="false". 会让Vue跳过css检测,提高性能,同时防止过渡过程中受css样式的影响

  • before-enter :  动画进入之前触发                 ----        from,初始化操作
  • enter :  动画正在进入时触发                         ----        active,写js,执行具体的动画
  • after-enter :  动画进入之后触发                    ----        to,结束,收尾工作
  • enter-cancelled :  动画进入失败触发

  • before-leave :  动画离开之前触发
  • leave :  动画正在离开时触发
  • after-leave :  动画离开之后触发
  • leave-cancelled :  动画离开失败触发

el : 执行动画的那个元素

done : 当使用javaScript来执行过渡动画时,需要进行done回调,否则它们将会被同步调用,过渡会立刻完成 


  接下来使用其他封装好的第三方动画库啦~

 

二、使用animate.css第三方动画库

Animate.css | A cross-browser library of CSS animations.

1.安装

npm install animate.css

2.导入

在main.js中进行导入

import 'animate.css'

3.使用

01-在css中使用

代码

<template>
  <div class="box">
    <button @click="isShow = !isShow">切换</button>
    <transition name="run" appear>
      <div v-if="isShow">
        <span>one</span>
      </div>
    </transition>
  </div>
</template>

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

<style lang="scss" scoped>
.run-enter-active {
  animation: lightSpeedInRight 2s linear 0s;
}
.run-leave-active {
  animation: lightSpeedOutRight 2s linear 0s;
}
</style>

效果 

说明 

02-使用class类名

这里需使用enter-active-class 和 leave-active-class,css部分可以不写啦

代码

可以不用写name了,如果写了class类名,类名的优先级会更高

<template>
  <div class="box">
    <button @click="isShow = !isShow">切换</button>
    <transition enter-active-class="animate__animated animate__backInDown" leave-active-class="animate__animated animate__backOutLeft">
      <div v-if="isShow">
        <span>one</span>
      </div>
    </transition>
  </div>
</template>

效果 

说明 


三、使用gsap库 ( JS库 )

 GreenSock - Docs - Gsap

某些情况下,如果希望通过JavaScript来实现动画效果,那么可以使用gsap库来完成

  • GSAP : The GreenSock Animation Platform ( GreenSock动画平台 ) 的缩写
  • 可以通过JavaScript为CSS属性、SVG、Canvas等设置动画,并且兼容浏览器

1.安装

npm install gsap   

2.导入

 在组件内部导入即可

import gsap from 'gsap'

3.使用

需要使用enter和leave回调函数

<template>
  <div class="box">
    <button @click="isShow = !isShow">切换</button>
    <transition appear @enter="enter" @leave="leave" :css="false">
      <div v-if="isShow">
        <span>one</span>
      </div>
    </transition>
  </div>
</template>
<script>
// 导入
import gsap from 'gsap'
export default {
  data() {
    return {
      isShow: true
    }
  },
  methods: {
    enter(el, done) {
      // 从哪里来
      gsap.from(el, {
        // 缩小为0
        scale: 0,
        // 透明度
        opacity: 0,
        // 持续时间
        duration: 1,
        // translatex便宜200
        x: 200,
        // 这样设置比较好
        onComplete: done
      })
    },
    /**
     * 这里只需要制定最开始的状态,不用制定后面的状态,有点不好理解
     */
    leave(el, done) {
      // 到哪里去
      gsap.to(el, {
        scale: 0,
        opacity: 0,
        duration: 1,
        x: -200,
        onComplete: done
      })
    }
  }
}
</script>
<style lang="scss" scoped></style>

4.效果 

5.gsap额外补充 ( 数字变化效果 )

代码

<template>
  <div class="box">
    <input type="number" step="100" v-model="count" />
    <div>
      <span class="text-color">{{ showNumber.toFixed(0) }}</span>
    </div>
  </div>
</template>
<script>
import gsap from 'gsap'
export default {
  data() {
    return {
      count: 0,
      showNumber: 0
    }
  },
  watch: {
    // 监听输入值的改变
    count(newValue) {
      // 把当前this存进去,实时更改showNumber的值
      gsap.to(this, { duration: 0.5, showNumber: newValue })
    }
  }
}
</script>
<style lang="scss" scoped>
.box {
  padding: 200px;
  background-color: skyblue;
  box-shadow: 0 0 100px 0 skyblue;
  input {
    border: 3px solid rgb(7, 172, 237);
    border-radius: 10px;
    padding: 5px 10px;
    width: 100px;
    background-color: skyblue;
  }
  .text-color {
    display: inline-block;
    background-image: linear-gradient(to right, orange, purple);
    background-clip: text;
    color: transparent;
    font-size: 50px;
  }
}
</style>

效果


四、列表的过渡 transition-group

在上面的文章中,过渡动画只是针对单个元素或者单个组件的,如果希望渲染的是一个列表,并且该列表中添加、删除数据也希望有动画执行

使用 transition-group : 

1.列表数字增加删除效果

01-代码 

<template>
  <div class="box">
    <div>
      <button @click="addNum">添加</button>
      <button @click="removeNum">删除</button>
    </div>
    <transition-group tag="p" name="run">
      <span class="item" v-for="item in dataList" :key="item">{{ item }}</span>
    </transition-group>
  </div>
</template>
<script>
export default {
  data() {
    return {
      dataList: [1, 2, 3, 4, 5, 6, 7, 8, 9],
      defaultNumber: 10
    }
  },
  methods: {
    // 随机位置增加一个数字
    addNum() {
      this.dataList.splice(this.randomIndex(), 0, this.defaultNumber++)
    },
    // 随机位置删除一个数字
    removeNum() {
      this.dataList.splice(this.randomIndex(), 1)
    },
    // 随机取一个位置
    randomIndex() {
      return Math.floor(Math.random() * this.dataList.length)
    }
  }
}
</script>
<style lang="scss" scoped>
.item {
  display: inline-block;
  margin-right: 10px;
  display: inline-block;
  background-image: linear-gradient(to right, orange, purple);
  background-clip: text;
  color: transparent;
  font-size: 30px;
}
.run-enter-from,
.run-leave-to {
  opacity: 0;
  transform: translateY(50px);
}
.run-enter-active,
.run-leave-active {
  transition: all 1s linear 0s;
}
</style>

效果 

02.效果优化 

优化点 : 新增和删除的节点是有动画,但是对于其他需要移动的节点是没有动画的

可以通过新增的v-move的class来完成动画,它会在元素改变位置的过程中应用

css代码

<style lang="scss" scoped>
.item {
  display: inline-block;
  margin-right: 10px;
  display: inline-block;
  background-image: linear-gradient(to right, orange, purple);
  background-clip: text;
  color: transparent;
  font-size: 30px;
}
.run-enter-from,
.run-leave-to {
  opacity: 0;
  transform: translateY(50px);
}
.run-enter-active,
.run-leave-active {
  transition: all 1s linear 0s;
}
// 移除的时候需要加上
.run-leave-active {
  position: absolute;
}
// 加上这个
.run-move {
  transition: transform 1s linear 0s;
}
</style>

效果

03.增加洗牌效果

one-安装lodash库 

npm install lodash

two-导入

import _ from 'lodash'

three-使用

<template>
  <div class="box">
    <div>
      <button @click="addNum">添加</button>
      <button @click="removeNum">删除</button>
      <button @click="shuffleNum">洗牌</button>
    </div>
    <transition-group tag="p" name="run">
      <span class="item" v-for="item in dataList" :key="item">{{ item }}</span>
    </transition-group>
  </div>
</template>
<script>
import _ from 'lodash'
export default {
  data() {
    return {
      dataList: [1, 2, 3, 4, 5, 6, 7, 8, 9],
      defaultNumber: 10
    }
  },
  methods: {
    // 随机位置增加一个数字
    addNum() {
      this.dataList.splice(this.randomIndex(), 0, this.defaultNumber++)
    },
    // 随机位置删除一个数字
    removeNum() {
      this.dataList.splice(this.randomIndex(), 1)
    },
    // 洗牌
    shuffleNum() {
      this.dataList = _.shuffle(this.dataList)
    },
    // 随机取一个位置
    randomIndex() {
      return Math.floor(Math.random() * this.dataList.length)
    }
  }
}
</script>
<style lang="scss" scoped>
.item {
  display: inline-block;
  margin-right: 10px;
  display: inline-block;
  background-image: linear-gradient(to right, orange, purple);
  background-clip: text;
  color: transparent;
  font-size: 30px;
}
.run-enter-from,
.run-leave-to {
  opacity: 0;
  transform: translateY(50px);
}
.run-enter-active,
.run-leave-active {
  transition: all 1s linear 0s;
}
// 移除的时候需要加上
.run-leave-active {
  position: absolute;
}
// 加上这个属性
.run-move {
  transition: transform 1s linear 0s;
}
</style>

fore-效果  

2.列表的交替动画效果

01-用css实现

代码

<template>
  <div class="box">
    <input type="text" v-model="keyWords" />
    <transition-group tag="ul" name="run" class="nav">
      <li class="item" v-for="item in filterData" :key="item">{{ item }}</li>
    </transition-group>
  </div>
</template>

<script>
export default {
  data() {
    return {
      keyWords: '',
      dataList: ['abc', 'bac', 'aec', 'qqw', 'qbf', 'aaa', 'afa']
    }
  },
  computed: {
    // 赛选一下
    filterData() {
      return this.dataList.filter((item) => item.includes(this.keyWords))
    }
  }
}
</script>

<style lang="scss" scoped>
.run-enter-from,
.run-leave-to {
  opacity: 0;
  transform: translateX(100px) rotate(180deg);
}
.run-enter-active,
.run-leave-active {
  transition: all 1s linear 0s;
}
.box {
  padding: 100px 0;
  background-color: skyblue;
  box-shadow: 0 0 100px 0 skyblue;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  input {
    border: 3px solid rgb(7, 172, 237);
    border-radius: 10px;
    padding: 5px 10px;
    width: 100px;
    background-color: skyblue;
  }
  .text-color {
    display: inline-block;
    background-image: linear-gradient(to right, orange, purple);
    background-clip: text;
    color: transparent;
    font-size: 50px;
  }
}
.nav {
  width: 100%;
  display: flex;
  list-style: none;
  margin: 0;
  padding: 0;
  text-align: center;
  .item {
    flex: 1;
  }
}
</style>

效果 

ps : 那么是同时消失,同时出现,实现不了交替效果

02-使用js实现动画

通过delay属性,实现交替效果

代码

<template>
  <div class="box">
    <input type="text" v-model="keyWords" />
    <transition-group tag="ul" name="run" class="nav" @enter="enter" @leave="leave" appear>
      <!-- 使用data-*这样的属性,给每个动画元素加上index -->
      <li class="item" v-for="(item, index) in filterData" :data-index="index" :key="item">
        {{ item }}
      </li>
    </transition-group>
  </div>
</template>

<script>
import gsap from 'gsap'
export default {
  data() {
    return {
      keyWords: '',
      dataList: ['abc', 'bac', 'aec', 'qqw', 'qbf', 'aaa', 'afa'],
      // 抽取一下动画参数
      transitionOption: {
        opacity: 0,
        scale: 0.5,
        height: 0,
        rotate: 360
      }
    }
  },
  computed: {
    // 赛选一下
    filterData() {
      return this.dataList.filter((item) => item.includes(this.keyWords))
    }
  },
  methods: {
    enter(el, done) {
      gsap.from(el, {
        ...this.transitionOption,
        // 设置延迟时间,因为是交替,所以每个都要不一样
        delay: el.dataset.index * 0.2,
        xPercent: -20,
        onComplete: done
      })
    },
    leave(el, done) {
      gsap.to(el, {
        ...this.transitionOption, // 设置延迟时间,因为是交替,所以每个都要不一样
        delay: el.dataset.index * 0.2,
        xPercent: 20,
        onComplete: done
      })
    }
  }
}
</script>

<style lang="scss" scoped>
.box {
  padding: 100px 0;
  background-color: skyblue;
  box-shadow: 0 0 100px 0 skyblue;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  input {
    border: 3px solid rgb(7, 172, 237);
    border-radius: 10px;
    padding: 5px 10px;
    width: 100px;
    background-color: skyblue;
  }
  .text-color {
    display: inline-block;
    background-image: linear-gradient(to right, orange, purple);
    background-clip: text;
    color: transparent;
    font-size: 50px;
  }
}
.nav {
  width: 100%;
  // display: flex;
  list-style: none;
  margin: 0;
  padding: 0;
  text-align: center;
  position: relative;
  .item {
    flex: 1;
  }
}
</style>

效果 

五、数字滚动效果

使用 vue-count-to 库,和gsap不同的是可以有数字分割符

1. 安装

npm i vue-count-to

2. 封装

<!-- //?模块说明 =>  数字滚动效果模块 -->
<template>
  <div class="number-board-layout">
    <count-to
      :class="{ underline: underline }"
      :separator="separator"
      :prefix="unit"
      :suffix="after"
      :endVal="innerNumber"
      :decimals="decimals"
      :duration="1500"
    />
  </div>
</template>
<script>
import CountTo from 'vue-count-to';
export default {
  name: 'numberBoard',
  components: { CountTo },
  props: {
    // 数字
    number: {
      type: [Number, String],
      default: 0,
      required: true
    },
    // 默认小数点
    decimals: {
      type: Number,
      default: 0
    },
    // 数字分隔符
    separator: {
      type: String,
      default: ','
    },
    // 前缀
    unit: String,
    // 后缀
    after: String,
    // 数字是否存在下划线
    underline: {
      type: Boolean,
      default: false
    }
  },
  computed: {
    // 内部数字
    innerNumber() {
      // 如果是字符串,转换成数字
      return +this.number;
    }
  }
};
</script>
<style lang="scss" scoped>
.number-board-layout {
  .underline {
    padding-bottom: 5px;
    border-bottom: 2px solid #ff5d1c;
  }
}
</style>

3. 使用

<numberBoard :number="3000000" />

4. 效果 

六、写在最后

🌟 🌟  谢谢聪明美丽帅气的你看到我的文章,支持一下可否

👍 👍  您的点赞,就是我动力的源泉,让我跨过彼岸

⭐️ ⭐️  您的收藏,就是我期待的远方,让我目标坚定

✏️ ✏️  您的评论,就是我心灵的医生,让我完善己身

  • 84
    点赞
  • 209
    收藏
    觉得还不错? 一键收藏
  • 44
    评论
手风琴动画是指在一组元素中,其中一个元素被展开时,其他元素同时被收缩,形成一个类似于手风琴的效果。在 Vue 移动端开发中,可以使用 transition 动画实现手风琴效果。 首先,在 HTML 中定义一组需要展开和收缩的元素,可以使用 v-for 指令来动态渲染元素,并绑定一个 active 变量表示当前展开元素的索引: ``` <div class="accordion"> <div v-for="(item, index) in items" :key="index" :class="{ active: index === active }"> <div class="title" @click="toggle(index)"> {{ item.title }} </div> <div class="content"> {{ item.content }} </div> </div> </div> ``` 其中,toggle 方法用于切换展开和收缩状态: ``` methods: { toggle(index) { this.active = index === this.active ? -1 : index; } } ``` 接下来,在 CSS 中定义手风琴动画的样式,使用 transform 属性实现元素的展开和收缩: ``` .accordion { .title { cursor: pointer; } .content { overflow: hidden; transition: height 0.3s ease-out; &.active { height: auto; transition: height 0.3s ease-in; } } } ``` 其中,active 类用于控制元素的展开和收缩状态,当元素被激活时,添加 active 类,并将高度设置为 auto,实现元素的展开效果;当元素被取消激活时,移除 active 类,并将高度设置为 0,实现元素的收缩效果。 最后,在 Vue 中使用 transition 组件包裹元素,实现动画效果: ``` <transition name="accordion"> <div v-for="(item, index) in items" :key="index" :class="{ active: index === active }"> <div class="title" @click="toggle(index)"> {{ item.title }} </div> <div class="content"> {{ item.content }} </div> </div> </transition> ``` 其中,name 属性用于指定动画的名称,可以在 CSS 中定义对应的动画样式。 这样就完成了手风琴动画实现

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值