# Vue动画效果

# 单行消息水平循环滚动

页面顶部或者底部消息提示文字,一直在水平无限循环滚动,一般使用 animation 动画一直循环滚动就可以了。

.scroll {
  width: 100%;
  height: 40px;
  overflow: hidden;
  background-color: #ccc;
}

.scroll p {
  font-size: 20px;
  color: coral;
  line-height: 36px;
  animation: adScroll 5s linear infinite;
  animation-fill-mode: forwards;
}

@keyframes adScroll {
  0% {
    transform: translateX(100%);
  }
  100% {
    transform: translateX(-80%);
  }
}
<div class="scroll">
  <p>我是广告内容!!!!!</p>
</div>

# 多行无缝循环滚动

在一个固定容器中,显示多行内容垂直方向无限循环滚动,还是和单行滚动一样,使用 animation ,但是需要再多加一倍的元素来进行首位连接才行,单单一个列表无法进行衔接。这种方式知不知道元素个数都可以使用。

.wrap {
  width: 400px;
  height: 150px;
  margin: 0 auto;
  border: 1px solid #999;
  overflow: hidden;
}
.list {
  width: 100%;
  animation: infiniteScroll 3s linear infinite normal;
}
.list-item {
  width: 100%;
  height: 50px;
  font-size: 20px;
  color: coral;
  line-height: 36px;
  text-align: center;
  background-color: #f2f2f2;
}

@keyframes infiniteScroll {
  0% {
    transform: translateY(0%);
  }
  100% {
    transform: translateY(-100%);
  }
}
<div class="wrap">
  <div class="list">
    <div class="list-item" v-for="(item, index) in lists" ::key="index">
      <span>{{item}}</span>
    </div>
  </div>
  <div class="list">
    <div class="list-item" v-for="(item, index) in lists" ::key="index">
      <span>{{item}}</span>
    </div>
  </div>
</div>

# 广告停顿滚动效果

在固定容器中,显示一行内容,像轮播图一样,滚动一下停止一段时间后接着无缝滚动,最直接的办法就是使用插件 swiper 进行加载,也可以 <transition> 组件来进行模拟,但需要借助定时器 setInterval 来进行定时更新显示内容。

.wrap {
  position: relative;
  width: 400px;
  height: 100px;
  margin: 0 auto;
  border: 1px solid #999;
  overflow: hidden;
}

.list-item {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 9;
  
  width: 100%;
  height: 100px;
  font-size: 20px;
  color: coral;
  line-height: 90px;
  text-align: center;
  background-color: #f2f2f2;
}

.fade-enter-active, .fade-leave-active {
  transition: all 1s linear;
}

/* 定义离开和进入效果 */
.fade-enter {
  transform: translateY(100%);
  opacity: 0;
}
.fade-leave-to {
  transform: translateY(-100%);
  opacity: 0;
}
<div id="app">
  <div class="wrap">
    <transition name="fade">
      <div v-if="activeIndex == index" class="list-item" v-for="(item, index) in lists" :key="index">
        <span>{{item}}</span>
      </div>
    </transition>
  </div>
</div>
var vm = new Vue({
  el: '#app',
  data: {
    activeIndex: 0,
    timer: null,
    lists: [
      '我是广告内容1',
      '我是广告内容2',
      '我是广告内容3',
      '我是广告内容4',
      '我是广告内容5',
    ]
  },
  mounted() {
    this.startScroll();
  },
  methods: {
    startScroll: function() {
      // 定时切换显示内容
      this.timer = setInterval(() => {
        this.activeIndex++
        if (this.activeIndex >= this.lists.length) {
          this.activeIndex = 0
        }
      }, 2000);

      this.$once('hook:beforeDestroy', function () {
        clearInterval(this.timer)
      })
    }
  }
})

# 多个元素切换效果

多个元素切换效果使用 transition的多个元素过渡,结合过渡模式 mode 或者绝对定位来实现多种效果,mode过渡模式虽然看起来效果好一点,但是效果不够连贯,一个消失后才显示另一个。要想过渡比较连贯,一个开始消失,另一个就开始显示另一个可以使用 position: absolute; 让所有元素都浮动起来,这样切换效果就会连贯起来了。

一个滑动离开一个滑动进入效果:

上面的广告效果就是使用此方法进行实现的。

.fade-enter-active, .fade-leave-active {
  transition: all 1s;
}
/* 定义离开和进入效果 */
.fade-enter {
  transform: translateX(100%);
  opacity: 0;
}
.fade-leave-to {
  transform: translateX(-100%);
  opacity: 0;
}
/* 元素都浮动起来 */
.btn {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 9;
}
<div id="app">
  <button @click="isEditing = !isEditing">切换状态</button>
  <transition name="fade">
    <button class="btn" v-if="isEditing" key="Save">Save</button>
    <button class="btn" v-if="!isEditing" key="Edit">Edit</button>
  </transition>
</div>

一个滑动离开后一个再显示效果:

配合 mode="out-in" 模式使用,不使用绝对定位

.fade-enter-active, .fade-leave-active {
  transition: all 1s;
}
/* 定义离开和进入效果 */
.fade-enter {
  opacity: 0;
}
.fade-leave-to {
  transform: translateX(-100%);
  opacity: 0;
}
<div id="app">
  <button @click="isEditing = !isEditing">切换状态</button>
  <transition name="fade" mode="out-in">
    <button class="btn" v-if="isEditing" key="Save">Save</button>
    <button class="btn" v-if="!isEditing" key="Edit">Edit</button>
  </transition>
</div>

一个显示一个消失效果:

绝对定位 和 mode="out-in" 模式使用哪个模式都可以,两个一起使用也可以。

.fade-enter-active, .fade-leave-active {
  transition: all 1s;
}
/* 定义离开和进入效果 */
.fade-enter, .fade-leave-to {
  opacity: 0;
}
.btn {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 9;
}
<div id="app">
  <button @click="isEditing = !isEditing">切换状态</button>
  <transition name="fade" mode="out-in">
    <button class="btn" v-if="isEditing" key="Save">Save</button>
    <button class="btn" v-if="!isEditing" key="Edit">Edit</button>
  </transition>
</div>

多个组件实现切换过渡:

多个组件的过渡不需要使用 key 属性。只需要使用动态组件就可以了,其他的设置效果和多个元素设置效果一样。

<transition name="fade">
  <component :is="componentName"></component>
</transition>

# 订单切换效果

还是使用 transition 组件加上绝对定位实现。具备了轮播图基本的滚动效果,左右按钮切换和切换模式可以再添加。手势滑动也已经添加。

 * {
  margin: 0;
  padding: 0;
  border: 0;
  box-sizing: border-box;
  border: none;
  outline: none;
}
.wrap {
  position: relative;
  width: 100%;
  height: 500px;
  margin: 0 auto;
  border: 1px solid #999;
  overflow: hidden;
}
.wrap-nav {
  display: flex;
  width: 100%;
  height: 50px;
  overflow: auto;
  cursor: pointer;
}
.nav-item {
  width: 100px;
  height: 100%;
  line-height: 50px;
  text-align: center;
}
.nav-active {
  font-size: 20px;
  font-weight: bold;
}
.wrap-container {
  position: relative;
  width: 100%;
  height: 400px;
}
.list-item {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 9;
  width: 100%;
  height: 100%;
  font-size: 20px;
  color: coral;
  line-height: 90px;
  text-align: center;
  background-color: #f2f2f2;
}

/* 左滑 */
.fadel-enter-active, .fadel-leave-active {
  transition: all 0.6s linear;
}
.fadel-enter {
  transform: translateX(100%);
  opacity: 1;
}
.fadel-leave-to {
  transform: translateX(-100%);
  opacity: 0;
}
/* 右滑 */
.fader-enter-active, .fader-leave-active {
  transition: all 0.6s linear;
}
/* 定义离开和进入效果 */
.fader-enter {
  transform: translateX(-100%);
  opacity: 1;
}
.fader-leave-to {
  transform: translateX(100%);
  opacity: 0;
}
<div id="app">
  <div class="wrap">
    <div class="wrap-nav">
      <div class="nav-item" :class="{'nav-active': activeIndex == index}" v-for="(item, index) in navList" @click="switchNav(index)">{{item}}</div>
    </div>
    <div class="wrap-container">
      <transition :name="transitionName">
        <div v-if="activeIndex == index" class="list-item" v-for="(item, index) in detailList" :key="index" 
          @touchstart="touchStart" @touchmove="touchMove" @touchend="touchEnd">
          <span>{{item}}</span>
        </div>
      </transition>
    </div>
  </div>
</div>
var vm = new Vue({
  el: '#app',
  data: {
    activeIndex: 0,
    timer: null,
    transitionName: 'fadel',
    navList: ['待付款', '待收货', '待评价', '全部订单', '售后'],
    detailList: [
      '我是待付款',
      '我是待收货',
      '我是待评价',
      '我是全部订单',
      '我是售后',
    ],
    touchPageX: 0,
    touchPageY: 0,
    touchScale: 0,
    touchPageXMounts: 0,
    touchPageYMounts: 0, 
  },
  methods: {
    switchNav: function(index) {
      if (this.activeIndex === index) return
      if (this.activeIndex > index) {
        this.transitionName = 'fader'
      } else {
        this.transitionName = 'fadel'
      }
      this.activeIndex = index
    },
    touchStart: function(event) {
      this.touchPageX = event.targetTouches[0].pageX
      this.touchPageY = event.targetTouches[0].pageY
    },
    touchMove: function(event) {
      this.touchPageXMounts = this.touchPageX - event.targetTouches[0].pageX
      this.touchPageYMounts = this.touchPageY - event.targetTouches[0].pageY
    },
    touchEnd: function(event) {
      if (Math.abs(this.touchPageYMounts) > Math.abs(this.touchPageXMounts)) return
      var screenWidth = screen.width
      this.touchScale = parseFloat(this.touchPageXMounts / screenWidth).toFixed(2)
      if (Math.abs(this.touchScale) < 0.20) return
      if (this.touchScale > 0.20) this.touchLeft()
      if (this.touchScale < -0.20) this.touchRight();
    },
    touchLeft: function() {
      this.transitionName = 'fadel'
      this.activeIndex++
      if (this.activeIndex >= this.detailList.length) {
        this.activeIndex = 0
      }
    },
    touchRight: function() {
      this.transitionName = 'fader'
      this.activeIndex--
      if (this.activeIndex < 0) {
        this.activeIndex = this.detailList.length - 1
      }
    }
  }
})