④ vue 中的动画

vue 动画

1 使用 Vue 实现基础的 CSS 过渡与动画效果

1.1 过渡

<style>
  .transition {
    transition: 3s background-color ease;
  }
  .blue {
    background: blue;
  }
  .green {
    background: green;
  }
</style>
<script>
  const app = Vue.createApp({
    data() {
      return {
        animate: {
          transition: true,
          blue: true,
          green: false
        }
      }
    },
    methods: {
      handleClick() {
        this.animate.blue = !this.animate.blue
        this.animate.green = !this.animate.green
      }
    },
    template: `
      <div>
        <div :class="animate">hello world</div>
        <button @click="handleClick">切换</button>
      </div>
    `
  })
  const vm = app.mount('#root')
</script>
  • 通过数据来控制元素的 DOMstyle
<style>
  .transition {
    transition: 3s background-color ease;
  }
</style>
<script>
  const app = Vue.createApp({
    data() {
      return {
        styleObj: {
          background: 'blue'
        }
      }
    },
    methods: {
      handleClick() {
        if(this.styleObj.background === 'blue') {
          this.styleObj.background = 'green'
        } else if(this.styleObj.background === 'green') {
          this.styleObj.background = 'blue'
        }
      }
    },
    template: `
      <div>
        <div class="transition" :style="styleObj">hello world</div>
        <button @click="handleClick">切换</button>
      </div>
    `
  })
  const vm = app.mount('#root')
</script>

1.2 动画

<style>
  @keyframes leftToRight {
    0% {
      transform: translateX(-100px);
    }
    50% {
      transform: translateX(-50px);
    }
    0% {
      transform: translateX(0px);
    }
  }
  .animation {
    animation: leftToRight 3s;
  }
</style>
<script>
  const app = Vue.createApp({
    data() {
      return {
        animate: {
          animation: false
        }
      }
    },
    methods: {
      handleClick() {
        this.animate.animation = !this.animate.animation
      }
    },
    template: `
      <div>
        <div :class="animate">hello world</div>
        <button @click="handleClick">切换</button>
      </div>
    `
  })
  const vm = app.mount('#root')
</script>

2 使用 transition 标签实现单元素组件的过渡和动画效果

const app = Vue.createApp({
  data() {
    return {
      show: false
    }
  },
  methods: {
    handleClick() {
      this.show = !this.show
    }
  },
  // <transition> --- `v-`
  // <transition name='hello'> --- `hello-`
  // v-if v-show
  template: `
    <div>
      <transition>
        <div v-show="show">hello world</div>
      </transition>
      <button @click="handleClick">切换</button>
    </div>
  `
})
const vm = app.mount('#root')

2.1 过渡

  .v-enter-from {
    opacity: 0;
  }
  .v-enter-to {
    opacity: 1;
  }
  .v-leave-to {
    opacity: 0;
  }
  .v-enter-active, 
  .v-leave-active {
    transition: opacity 2s ease-in;
  }

2.2 动画

@keyframes shake {
  0% {
    transform: translateX(-100px);
  }
  50% {
    transform: translateX(-50px);
  }
  100% {
    transform: translateX(50px);
  }
}
.v-enter-active, 
.v-leave-active {
  animation: shake 2s;
}

2.3 自定义 class 类名

<style>
  @keyframes shake {
    0% {
      transform: translateX(-100px);
    }
    50% {
      transform: translateX(-50px);
    }
    100% {
      transform: translateX(50px);
    }
  }
  .hello, 
  .bye {
    animation: shake 2s;
  }
</style>
<script>
  const app = Vue.createApp({
    data() {
      return {
        show: false
      }
    },
    methods: {
      handleClick() {
        this.show = !this.show
      }
    },
    template: `
      <div>
        <transition
          enter-active-class="hello"
          leave-active-class="bye"
        >
          <div v-if="show">hello world</div>
        </transition>
        <button @click="handleClick">切换</button>
      </div>
    `
  })
  const vm = app.mount('#root')
</script>

2.4 使用第三动画库 animate

官网

<link
  rel="stylesheet"
  href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"
/>
<script>
  const app = Vue.createApp({
    data() {
      return {
        show: false
      }
    },
    methods: {
      handleClick() {
        this.show = !this.show
      }
    },
    template: `
      <div>
        <transition
          enter-active-class="animate__animated animate__bounce"
          leave-active-class="animate__animated animate__flash"
        >
          <div v-if="show">hello world</div>
        </transition>
        <button @click="handleClick">切换</button>
      </div>
    `
  })
  const vm = app.mount('#root')
</script>

2.5 动画 + 过渡

@keyframes shake {
  0% {
    transform: translateX(-100px);
  }
  50% {
    transform: translateX(-50px);
  }
  100% {
    transform: translateX(50px);
  }
}
.v-enter-from {
  color: red;
}
.v-enter-active {
  animation: shake 2s;
  transition: color 3s ease-in;
}
.v-leave-active {
  color: red;
  animation: shake 2s;
  transition: all 3s ease-in;
}
  • 动画 & 过渡同时存在,<transition> 添加属性 type 实现以动画 animation | 过渡 transition 的时间为准结束该过程
const app = Vue.createApp({
  template: `
    <div>
      <transition type="transition">
        <div v-if="show">hello world</div>
      </transition>
      <button @click="handleClick">切换</button>
    </div>
  `
})
  • 动画 & 过渡同时存在,<transition> 添加属性 duration 实现以指定时间 (ms为单位) 为准
const app = Vue.createApp({
  template: `
    <div>
      <transition :duration="{ enter: 1000, leave: 3000 }">
        <div v-if="show">hello world</div>
      </transition>
      <button @click="handleClick">切换</button>
    </div>
  `
})

2.6 通过数据来控制元素的 DOM 和 style

const app = Vue.createApp({
  data() {
    return {
      show: false
    }
  },
  methods: {
    handleClick() {
      this.show = !this.show
    },
    handleBeforeEnter(el) {
      el.style.color = "red"
    },
    handleEnter(el, done) {
      const animation = setInterval(() => {
        const color = el.style.color
        if(color === 'red') {
          el.style.color = 'green'
        } else {
          el.style.color = 'red'
        }
      }, 1000);
      setTimeout(() => {
        clearInterval(animation)
        done() // 通知 handleAfterEnter
      }, 5000);
    },
    handleAfterEnter() {
      alert(123)
    }
  },
  template: `
    <div>
      <transition 
        :css="false"
        @before-enter="handleBeforeEnter"
        @enter="handleEnter"
        @after-enter="handleAfterEnter"
        @before-leave="handleBeforeLeave"
        @leave="handleLeave"
        @after-leave="handleAfterLeave"
      >
        <div v-show="show">hello world</div>
      </transition>
      <button @click="handleClick">切换</button>
    </div>
  `
})
const vm = app.mount('#root')

3 组件和元素切换动画的实现

3.1 多个单元素标签之间的切换

  • 动画同时进行
<style>
  .v-enter-from,
  .v-leave-to {
    opacity: 0;
  }
  .v-enter-active,
  .v-leave-active {
    transition: all 3s ease-in;
  }
  .v-enter-to,
  .v-leave-from {
    opacity: 1;
  }
</style>
<script>
  const app = Vue.createApp({
    data() {
      return { show: false }
    },
    methods: {
      handleClick() {
        this.show = !this.show
      }
    },
    template: `
      <div>
        <transition>
          <div v-if="show">hello world</div>
          <div v-else="show">bye world</div>
        </transition>
        <button @click="handleClick">切换</button>
      </div>
    `
  })
  const vm = app.mount('#root')
</script>

1. <transition> 添加属性 mode 实现多元素的过渡顺序

  1. 先进场再出场:mode="in-out"

  2. 先出场再进场:mode="out-in"

2. <transition> 添加属性 appear 实现初始动画效果

3.2 多个单组件之间的切换

const ComponentA = {
 template: '<div>hello world</div>'
}
const ComponentB = {
 template: '<div>bye world</div>'
}
const app = Vue.createApp({
 data() {
   return { show: false }
 },
 methods: {
   handleClick() {
     this.show = !this.show
   }
 },
 components: {
   'component-a': ComponentA,
   'component-b': ComponentB,
 },
 template: `
   <div>
     <transition mode="out-in" appear>
       <component-a v-if="show">hello world</component-a>
       <component-b v-else="show">bye world</component-b>
     </transition>
     <button @click="handleClick">切换</button>
   </div>
 `
})
const vm = app.mount('#root')
  • 动态组件
const ComponentA = {
  template: '<div>hello world</div>'
}
const ComponentB = {
  template: '<div>bye world</div>'
}
const app = Vue.createApp({
  data() {
    return { component: 'component-a' }
  },
  methods: {
    handleClick() {
      if(this.component === 'component-a') {
        this.component = 'component-b'
      } else {
        this.component = 'component-a'
      }
    }
  },
  components: {
    'component-a': ComponentA,
    'component-b': ComponentB,
  },
  template: `
    <div>
      <transition mode="out-in" appear>
        <component :is="component" />
      </transition>
      <button @click="handleClick">切换</button>
    </div>
  `
})
const vm = app.mount('#root')

4 列表动画

  • <transition-group>
<style>
  .list-item {
    display: inline-block;
    margin-right: 10px;
  }
</style>
<script>
  const app = Vue.createApp({
    data() {
      return { list: [1, 2, 3] }
    },
    methods: {
      handleClick() {
        this.list.unshift(this.list.length + 1)
      }
    },
    template: `
      <div>
        <transition-group>
          <span class="list-item" v-for="item in list" :key="item">{{ item }}</span>
        </transition-group>
        <button @click="handleClick">切换</button>
      </div>
    `
  })
  const vm = app.mount('#root')
</script>

4.1 新增项的动画效果

.v-enter-from {
  opacity: 0;
  transform: translateY(30px);
}
.v-enter-active {
  transition: all 1s ease-in;
}
.v-enter-to {
  opacity: 1;
  transform: translateY(0);
}

4.2 所有项的动画效果

.v-enter-from {
  opacity: 0;
  transform: translateY(30px);
}
.v-enter-active {
  transition: all 1s ease-in;
}
.v-enter-to {
  opacity: 1;
  transform: translateY(0);
}
.v-move {
  transition: all 1s ease-in;
}

5 状态动画

  • 通过 数据 控制动画
const app = Vue.createApp({
  data() {
    return { 
      number: 1,
      animateNumber: 1
    }
  },
  methods: {
    handleClick() {
      this.number = 10;
      if(this.animateNumber < this.number) {
        const animation = setInterval(() => {
          this.animateNumber += 1;
          if(this.animateNumber === 10) {
            clearInterval(animation)
          }
        }, 100);
      }
    }
  },

  template: `
    <div>
      <span>{{ animateNumber }}</span>
      <button @click="handleClick">增加</button>
    </div>
  `
})
const vm = app.mount('#root')
posted on 2022-03-16 11:35  pleaseAnswer  阅读(69)  评论(0编辑  收藏  举报