Vue基础16之过度与动画、TodoList-动画效果

文章详细介绍了如何在Vue中实现过渡和动画效果,包括使用内置的transition组件和transition-group,以及结合第三方库animate.css创建复杂动画。通过示例代码展示了不同场景下的过渡动画,如单个元素的显示/隐藏、多个元素过渡和第三方动画库的集成。
摘要由CSDN通过智能技术生成

过度与动画

动画效果

App.vue

<template>
  <div class="bg">
    <button @click="isShow=!isShow">显示/隐藏</button>
    <transition appear>  <!--      appear意味着刷新完毕后会执行一遍进入动画-->
      <h1 v-show="isShow">你好啊!</h1>
    </transition>
    <transition name="hello">
      <h1 v-show="isShow">你好啊!</h1>
    </transition>
  </div>
</template>

<script>
export default {
  name: "App",
  data(){
    return{
      isShow:true
    }
  },
}
</script>

<style lang="less">
.bg{
  height: 937px;
  box-sizing: border-box;
}
h1{
  background-color: orange;
}
h1:last-child{
  background-color: skyblue;
}
//transition无名时候的进入动画
.v-enter-active{
  animation: trans 2s linear;
}
//transition无名时候的离开动画
.v-leave-active{
  animation: trans 2s linear reverse;  //reverse 倒着播放动画
}
@keyframes trans {
  from{
    transform: translateX(-100%)
  }to{
       transform: translateX(0);
     }
}

//hello的进入动画
.hello-enter-active{
  animation: trans 5s linear;
}
//hello的离开动画
.hello-leave-active{
  animation: trans 5s linear reverse;  //reverse 倒着播放动画
}
</style>

请添加图片描述

过度效果

App.vue

<template>
  <div class="bg">
<!--    <Animation></Animation>-->
    <TransitionEffect></TransitionEffect>
  </div>
</template>

<script>
import Animation from "@/components/Animation";
import TransitionEffect from "@/components/TransitionEffect";
export default {
  name: "App",
  components:{Animation,TransitionEffect}
}
</script>

<style lang="less">
.bg{
  height: 937px;
  box-sizing: border-box;
}
</style>

TransitionEffect.vue

<template>
  <div>
    <button @click="isShow=!isShow">显示/隐藏</button>
    <transition name="hello" appear>
      <h2 v-show="isShow">你好啊!</h2>
    </transition>
  </div>
</template>

<script>
    export default {
        name: "TransitionEffect",
        data(){
          return{
            isShow:false
          }
        }
    }
</script>

<style scoped>
h2{
  background-color: pink;
  /*transition: 2s linear;*/
}
/*详细的写法*/
/*!*进入的起点*!
.hello-enter{
  transform: translateX(-100%);
}
!*进入的终点*!
.hello-enter-to{
  transform: translateX(0);
}
!*离开的起点*!
.hello-leave{
  transform: translateX(0);
}
!*离开的终点*!
.hello-leave-to{
  transform: translateX(-100%);
}*/


/*合并写法*/
/*进入的起点,离开的终点*/
.hello-enter,.hello-leave-to{
  transform: translateX(-100%);
}
/*进入的终点,离开的起点*/
.hello-enter-to,.hello-leave{
  transform: translateX(0);
}
/*如果不在标签上写的话,就在这里写动画运动时长与速度*/
.hello-enter-active,.hello-leave-active{
  transition: 2s linear;
}

</style>

请添加图片描述

多个元素过度

App.vue

<template>
  <div class="bg">
<!--    <Animation></Animation>-->
<!--    <TransitionEffect></TransitionEffect>-->
    <MoreTransition></MoreTransition>
  </div>
</template>

<script>
import Animation from "@/components/Animation";
import TransitionEffect from "@/components/TransitionEffect";
import MoreTransition from "@/components/MoreTransition";
export default {
  name: "App",
  components:{MoreTransition, Animation,TransitionEffect}
}
</script>

<style lang="less">
.bg{
  height: 937px;
  box-sizing: border-box;
}
</style>

MoreTransition.vue

<template>
<div>
  <button @click="isShow=!isShow">显示/隐藏</button>
  <transition-group name="introduce">
    <h1 v-show="isShow" key="1" class="hello">你好啊!</h1>
    <h1 v-show="!isShow" key="2" class="name">张三</h1>
  </transition-group>
</div>
</template>

<script>
  export default {
    name: "MoreTransition",
    data(){
        return{
          isShow:true
        }
    }

  }
</script>

<style scoped>
.hello{
  background-color: orange;
}
.name{
  background-color: pink;
}
/* 进入的起点,离开的终点 */
.introduce-enter,.introduce-leave-to{
  transform: translateX(-100%);
}
/* 进入的终点,离开的起点 */
.introduce-enter-to,introduce-leave{
  transform: translateX(0);
}
.introduce-enter-active,.introduce-leave-active{
  transition: 2s linear;
}
</style>

请添加图片描述

集成第三方动画

安装

  1. npm官网 中找animate.css动画库
    在这里插入图片描述
  2. 安装animate.css
    执行命令

npm install animate.css

在这里插入图片描述

  1. 引入第三方库

import ‘animate.css’

在这里插入图片描述

  1. 第三方库官网

应用

App.vue
<template>
  <div class="bg">
<!--    <Animation></Animation>-->
<!--    <TransitionEffect></TransitionEffect>-->
<!--    <MoreTransition></MoreTransition>-->
    <ThirdAnimation></ThirdAnimation>
  </div>
</template>

<script>
import Animation from "@/components/Animation";
import TransitionEffect from "@/components/TransitionEffect";
import MoreTransition from "@/components/MoreTransition";
import ThirdAnimation from "@/components/ThirdAnimation";
export default {
  name: "App",
  components:{ThirdAnimation, MoreTransition, Animation,TransitionEffect}
}
</script>

<style lang="less">
.bg{
  height: 937px;
  box-sizing: border-box;
}
</style>

ThirdAnimation.vue
<template>
  <div>
    <button @click="isShow=!isShow">显示/隐藏</button>
    <transition
        name="animate__animated animate__bounce"
        enter-active-class="animate__heartBeat"
        leave-active-class="animate__backOutDown"
        appear>
      <h1 v-show="isShow">你好啊~</h1>
    </transition>
  </div>
</template>

<script>
import 'animate.css'
export default {
    name: "ThirdAnimation",
  data(){
      return{
        isShow:true
      }
  }
}
</script>

<style scoped>
h1{
  background-color: skyblue;
  text-align: center;
}
</style>

请添加图片描述

总结:Vue封装的过度与动画

  1. 作用:在插入、更新或移除DOM元素时,在合适的时候给元素添加样式类名。

  2. 图示:
    在这里插入图片描述

  3. 写法:
    (1)准备好样式:

    • 元素进入的样式:
      1.v-enter:进入的起点
      2.v-enter-active:进入过程中
      3.v-enter-to:进入的终点
    • 元素离开的样式:
      1.v-leave:离开的起点
      2.v-leave-active:离开过程中
      3.v-leave-to:离开的终点

(2)使用<transition>包裹要过度的元素,并配置name属性:

<transition name="hello">
  <h1 v-show="isShow">你好啊!</h1>
</transition>

(3)备注:若有多个元素需要过度,则需要使用:<transition-group>,且每个元素都要指定key

TodoList-过度与动画

在清单被添加时与删除时候加上动画

使用@keyframs写动画

MyItem.vue

<template>
    <transition name="todo" appear>
      <li @dblclick="editItem(todo)">
        <!--      <input type="checkbox" name="matter" id="" v-model="todo.done">-->
        <input type="checkbox" name="matter" id="" @change="checkDone(todo.id)" :checked="todo.done">
        &nbsp;
        <input type="text" ref="inputTitle"  :value="todo.title" class="editIn" v-show="todo.isEdit" @blur="ensureEdit($event,todo)" @keyup.enter="ensureEdit($event,todo)">
        <span class="todoTitle" v-show="!todo.isEdit">{{todo.title}}</span>
        <button class="delete" @click="deleteItem(todo.id)">删除</button>
        <button class="edit" @click="editItem(todo)">编辑</button>
      </li>
    </transition>
</template>

<script>
import pubsub from 'pubsub-js'
export default {
  name: "MyItem",
  props:['todo'],
  methods:{
    deleteItem(id){
      //消息的发布
      if(confirm('确认删除嘛?'))  pubsub.publish("deleteTodo",id)
    },
    checkDone(id){
      //触发公共组件的某个事件
      this.$bus.$emit("checkTodo",id)
    },
    //编辑
    editItem(todo){
      if(!todo.hasOwnProperty("isEdit")){
        this.$set(todo,'isEdit',true)
      }else{
        todo.isEdit=true
      }
      //由于代码是按顺序执行的,所以不会在执行完上面的代码之后就重新渲染页面
      //继续向下执行,但此时的input框还是隐藏状态,隐藏状态是没办法获取焦点的,因此使用定时器来延迟获取焦点
      // setTimeout(()=>{
      //   this.$refs.inputTitle.focus()
      // })

      this.$nextTick(function(){
        this.$refs.inputTitle.focus()
      })
    },
    //失去焦点确认编辑成功
    ensureEdit(e,todo){
      todo.isEdit=false
      if(!e.target.value.trim())  return alert("输入的内容不得为空")
      this.$bus.$emit("editTodo",todo.id,e.target.value)
    }
  }
}
</script>

<style scoped lang="less">
li{
  //height: 35%;
  //width: 96%;
  display: block;
  //background-color: pink;
  margin: auto;
  padding: 12px;
  border-top: 1px solid rgba(87, 87, 87, 0.3);
  //border-left: 1px solid rgba(87, 87, 87, 0.3);
  //border-right: 1px solid rgba(87, 87, 87, 0.3);
  box-sizing: border-box;
  //border-collapse: collapse;
  .editIn{
    font-size: 16px;
    border: 1px solid #ccc;
    padding: 5px;
    border-radius: 4px;
    box-sizing: border-box;
  }
  .editIn:focus{
    border-color: #66afe9;
    box-shadow: inset 0 1px 1px rgb(0 0 0 / 8%), 0 0 8px rgb(102 175 233 / 60%);
    outline: none;
  }
  .delete{
    background-color: #d9534f;
    float: right;
    padding: 3px 10px;
    color: white;
    border: 1px solid #d43f3a;
    border-radius: 5px;
    cursor: pointer;
    &:hover{
      background-color: #c9302c;
      border: 1px solid #ac2925;
    }
  }
  .edit{
    //background-color: #337ab7;
    float: right;
    padding: 3px 10px;
    margin-right: 5px;
    color: white;
    border: 1px solid #4cae4c;
    border-radius: 5px;
    cursor: pointer;
    &:hover{
      border: 1px solid #398439;
      //background-color: #286090;
      background-color: #449d44;
      border-color: #398439;
    }
    background-color: #5cb85c;
    //border-color: #4cae4c;
  }
  &:hover{
    background-color: rgba(0,0,0,0.1);
  }
}
/*添加时候位移变化 */
/*//进入的起点,离开的终点
.todo-enter,.todo-leave-to{
  transform: translateX(100%);
}
//进入的终点,离开的起点
.todo-enter-to,.todo-leave{
  transform: translateX(0);
}
.todo-enter-active,.todo-leave-active{
  transition: 1s linear;
}*/

//添加与删除时候透明度变化
.todo-enter-active{
  animation: trans 1s linear;
}
.todo-leave-active{
  animation: trans 1s linear reverse;
}
@keyframes trans {
  from{
    opacity: 0;
  }
  to{
    opacity: 1;
  }
}
</style>

请添加图片描述

使用.v-enter,.v-leave写动画

<template>
    <transition name="todo" appear>
      <li @dblclick="editItem(todo)">
        <!--      <input type="checkbox" name="matter" id="" v-model="todo.done">-->
        <input type="checkbox" name="matter" id="" @change="checkDone(todo.id)" :checked="todo.done">
        &nbsp;
        <input type="text" ref="inputTitle"  :value="todo.title" class="editIn" v-show="todo.isEdit" @blur="ensureEdit($event,todo)" @keyup.enter="ensureEdit($event,todo)">
        <span class="todoTitle" v-show="!todo.isEdit">{{todo.title}}</span>
        <button class="delete" @click="deleteItem(todo.id)">删除</button>
        <button class="edit" @click="editItem(todo)">编辑</button>
      </li>
    </transition>
</template>

<script>
import pubsub from 'pubsub-js'
export default {
  name: "MyItem",
  props:['todo'],
  methods:{
    deleteItem(id){
      //消息的发布
      if(confirm('确认删除嘛?'))  pubsub.publish("deleteTodo",id)
    },
    checkDone(id){
      //触发公共组件的某个事件
      this.$bus.$emit("checkTodo",id)
    },
    //编辑
    editItem(todo){
      if(!todo.hasOwnProperty("isEdit")){
        this.$set(todo,'isEdit',true)
      }else{
        todo.isEdit=true
      }
      //由于代码是按顺序执行的,所以不会在执行完上面的代码之后就重新渲染页面
      //继续向下执行,但此时的input框还是隐藏状态,隐藏状态是没办法获取焦点的,因此使用定时器来延迟获取焦点
      // setTimeout(()=>{
      //   this.$refs.inputTitle.focus()
      // })

      this.$nextTick(function(){
        this.$refs.inputTitle.focus()
      })
    },
    //失去焦点确认编辑成功
    ensureEdit(e,todo){
      todo.isEdit=false
      if(!e.target.value.trim())  return alert("输入的内容不得为空")
      this.$bus.$emit("editTodo",todo.id,e.target.value)
    }
  }
}
</script>

<style scoped lang="less">
li{
  //height: 35%;
  //width: 96%;
  display: block;
  //background-color: pink;
  margin: auto;
  padding: 12px;
  border-top: 1px solid rgba(87, 87, 87, 0.3);
  //border-left: 1px solid rgba(87, 87, 87, 0.3);
  //border-right: 1px solid rgba(87, 87, 87, 0.3);
  box-sizing: border-box;
  //border-collapse: collapse;
  .editIn{
    font-size: 16px;
    border: 1px solid #ccc;
    padding: 5px;
    border-radius: 4px;
    box-sizing: border-box;
  }
  .editIn:focus{
    border-color: #66afe9;
    box-shadow: inset 0 1px 1px rgb(0 0 0 / 8%), 0 0 8px rgb(102 175 233 / 60%);
    outline: none;
  }
  .delete{
    background-color: #d9534f;
    float: right;
    padding: 3px 10px;
    color: white;
    border: 1px solid #d43f3a;
    border-radius: 5px;
    cursor: pointer;
    &:hover{
      background-color: #c9302c;
      border: 1px solid #ac2925;
    }
  }
  .edit{
    //background-color: #337ab7;
    float: right;
    padding: 3px 10px;
    margin-right: 5px;
    color: white;
    border: 1px solid #4cae4c;
    border-radius: 5px;
    cursor: pointer;
    &:hover{
      border: 1px solid #398439;
      //background-color: #286090;
      background-color: #449d44;
      border-color: #398439;
    }
    background-color: #5cb85c;
    //border-color: #4cae4c;
  }
  &:hover{
    background-color: rgba(0,0,0,0.1);
  }
}
//添加时候位移变化
//进入的起点,离开的终点
.todo-enter,.todo-leave-to{
  transform: translateX(100%);
}
//进入的终点,离开的起点
.todo-enter-to,.todo-leave{
  transform: translateX(0);
}
.todo-enter-active,.todo-leave-active{
  transition: 1s linear;
}

/*//添加与删除时候透明度变化
.todo-enter-active{
  animation: trans 1s linear;
}
.todo-leave-active{
  animation: trans 1s linear reverse;
}
@keyframes trans {
  from{
    opacity: 0;
  }
  to{
    opacity: 1;
  }
}*/
</style>

请添加图片描述

不在MyItem中加,在MyList中加动画

MyItem.vue

<template>
<!--  <transition>-->
    <li @dblclick="editItem(todo)">
      <!--      <input type="checkbox" name="matter" id="" v-model="todo.done">-->
      <input type="checkbox" name="matter" id="" @change="checkDone(todo.id)" :checked="todo.done">
      &nbsp;
      <input type="text" ref="inputTitle"  :value="todo.title" class="editIn" v-show="todo.isEdit" @blur="ensureEdit($event,todo)" @keyup.enter="ensureEdit($event,todo)">
      <span class="todoTitle" v-show="!todo.isEdit">{{todo.title}}</span>
      <button class="delete" @click="deleteItem(todo.id)">删除</button>
      <button class="edit" @click="editItem(todo)">编辑</button>
    </li>
<!--  </transition>-->
</template>

<script>
import pubsub from 'pubsub-js'
export default {
  name: "MyItem",
  props:['todo'],
  methods:{
    deleteItem(id){
      //消息的发布
      if(confirm('确认删除嘛?'))  pubsub.publish("deleteTodo",id)
    },
    checkDone(id){
      //触发公共组件的某个事件
      this.$bus.$emit("checkTodo",id)
    },
    //编辑
    editItem(todo){
      if(!todo.hasOwnProperty("isEdit")){
        this.$set(todo,'isEdit',true)
      }else{
        todo.isEdit=true
      }
      //由于代码是按顺序执行的,所以不会在执行完上面的代码之后就重新渲染页面
      //继续向下执行,但此时的input框还是隐藏状态,隐藏状态是没办法获取焦点的,因此使用定时器来延迟获取焦点
      // setTimeout(()=>{
      //   this.$refs.inputTitle.focus()
      // })

      this.$nextTick(function(){
        this.$refs.inputTitle.focus()
      })
    },
    //失去焦点确认编辑成功
    ensureEdit(e,todo){
      todo.isEdit=false
      if(!e.target.value.trim())  return alert("输入的内容不得为空")
      this.$bus.$emit("editTodo",todo.id,e.target.value)
    }
  }
}
</script>

<style scoped lang="less">
li{
  //height: 35%;
  //width: 96%;
  display: block;
  //background-color: pink;
  margin: auto;
  padding: 12px;
  border-top: 1px solid rgba(87, 87, 87, 0.3);
  //border-left: 1px solid rgba(87, 87, 87, 0.3);
  //border-right: 1px solid rgba(87, 87, 87, 0.3);
  box-sizing: border-box;
  //border-collapse: collapse;
  .editIn{
    font-size: 16px;
    border: 1px solid #ccc;
    padding: 5px;
    border-radius: 4px;
    box-sizing: border-box;
  }
  .editIn:focus{
    border-color: #66afe9;
    box-shadow: inset 0 1px 1px rgb(0 0 0 / 8%), 0 0 8px rgb(102 175 233 / 60%);
    outline: none;
  }
  .delete{
    background-color: #d9534f;
    float: right;
    padding: 3px 10px;
    color: white;
    border: 1px solid #d43f3a;
    border-radius: 5px;
    cursor: pointer;
    &:hover{
      background-color: #c9302c;
      border: 1px solid #ac2925;
    }
  }
  .edit{
    //background-color: #337ab7;
    float: right;
    padding: 3px 10px;
    margin-right: 5px;
    color: white;
    border: 1px solid #4cae4c;
    border-radius: 5px;
    cursor: pointer;
    &:hover{
      border: 1px solid #398439;
      //background-color: #286090;
      background-color: #449d44;
      border-color: #398439;
    }
    background-color: #5cb85c;
    //border-color: #4cae4c;
  }
  &:hover{
    background-color: rgba(0,0,0,0.1);
  }
}
/*
//添加时候位移变化
//进入的起点,离开的终点
.todo-enter,.todo-leave-to{
  transform: translateX(100%);
}
//进入的终点,离开的起点
.todo-enter-to,.todo-leave{
  transform: translateX(0);
}
.todo-enter-active,.todo-leave-active{
  transition: 1s linear;
}
*/

/*//添加与删除时候透明度变化
.todo-enter-active{
  animation: trans 1s linear;
}
.todo-leave-active{
  animation: trans 1s linear reverse;
}
@keyframes trans {
  from{
    opacity: 0;
  }
  to{
    opacity: 1;
  }
}*/
</style>

MyList.vue

<template>
  <div>
    <ul>
      <div class="con">
        <transition-group name="todo" appear>
          <MyItem v-for="todo in todos" :todo="todo" :key="todo.id"/>
        </transition-group>
      </div>
    </ul>
  </div>
</template>

<script>
import MyItem from "@/components/MyItem";
export default {
  name:'MyList',
  components:{MyItem},
  props:['todos']
}
</script>

<style scoped lang="less">
ul{
  .con{
    //width: 95%;
    //margin: auto;
    border-bottom: 1px solid rgba(87, 87, 87, 0.3);
    border-left: 1px solid rgba(87, 87, 87, 0.3);
    border-right: 1px solid rgba(87, 87, 87, 0.3);
    margin: 0px 8px;
    //background-color: pink;
  }
}
//添加与删除时候透明度变化
.todo-enter-active{
  animation: trans 1s linear;
}
.todo-leave-active{
  animation: trans 1s linear reverse;
}
@keyframes trans {
  from{
    opacity: 0;
  }
  to{
    opacity: 1;
  }
}
</style>

请添加图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值