Vue——图片轮播组件

Notices: 这是我一个项目中的一个子组件,要展示的数据、图片地址等的都在父组件data中。所以后面的讲述都是基于从父组件获取的参数进行处理。(如需将这个SlideShow写成一个单独的主组件,将本文使用的data写在这个组件的data中)

必备知识:

  1. 写轮播页面:HTML+CSS
  2. vue-cli:创建项目
  3. Vue基础:模板语法、计算属性、侦听器、渲染方式(条件渲染、列表渲染)、组件
  4. 深入组件:组件注册、父子组件的交互方式、自定义事件
  5. vue过渡:进入、离开过渡
  6. CSS动画:CSS3 transfrom 属性

效果图:

组件构成:

  • 利用Html 和 CSS 写一个基本的图片轮播页面

这部分很简单吧,常规的显示图片及轮播数字下标。CSS按照自己喜欢的样式随便调整。(最后面会给出我写的完整的CSS样式)

<!-- 布局 -->
<template>
  <div class="slide-show">
    <div class="slide-img" >
      <a href="" >
          <img src="" alt="">
      </a>
    </div>
    <h3>{{ title }}</h3>
    <ul class="slide-page" >
      <li>&lt;</li>
      <li>
        <a href="">1</a>
        <a href="">2</a>
        <a href="">3</a>
      </li>
      <li>&gt;</li>
    </ul>
  </div>
</template>

 

  • 加入响应式的数据驱动

(1)引入数据:

我这里轮播了三张图,需要多一点轮播的,直接加在data中。将轮播的三组数据放在sildes数组中。

我们父组件中的数据:

Tips: 这里加载图片的路径必须使用require引入,方便webpack打包。

data(){  
    return{
      slides:[
        {
          src:require('../assets/slide1.jpg'),
          title:"男人帮特色",
          href:'detail/****'
        },
        {
          src:require('../assets/slide2.jpg'),
          title:"女神养成计划",
          href:'detail/###'
        },
        {
          src:require('../assets/slide3.jpg'),
          title:"有腔调的品味",
          href:'detail/###'
        }
      ]
}

(2)数据驱动

依据slide数组,利用v-for列表渲染,v-bind绑定img的src等

随着nowIndex的动态变化显示不同的图片,在nowIndex初始化为0

(3)添加点击翻页等事件:左右箭头点击上下翻页、点击数字切换到相应的图片

在methods中定义跳转方法goto(index),跳转到index索引图片页

goto(index){
     this.nowIndex = index
    }

 

所以 上翻页只需要修改传入的参为preIndex,依据当前的index决定上翻页的Index,(第一页的上翻页为最后一页,考虑循环),所以这里利用计算属性computed;同理,下翻页。

computed:{
    preIndex(){
      if(this.nowIndex == 0){
        return this.slides.length-1
      }else{
        return this.nowIndex - 1
      }
    },
    nextIndex(){
      if(this.nowIndex == this.slides.length-1){
        return 0
      }else{
        return this.nowIndex + 1
      }
    }
  }

(4)图片自动轮播

使用javascript的setInterval方法实现间隔10ms自动轮播  。指定的时间间隔重复执行代码。(鼠标放在图片上,需要停止动画效果,所以需要利用clearInterval()清除效果)

事件间隔在父级组件指定(v-bind绑定),利用props接收来自父级的数据

runInv需要在加载后调用,利用生命周期中的mounted实现调用

mounted:在模板渲染成html后调用,通常是初始化页面完成后,再对html的dom节点进行一些需要的操作。

props:{
    slides:{
      type:Array,
      default:[]//初始值为空
    },
    inv:{//父级传递
      type:Number,
      default:1000
    }
  },

 

methods:{
    //幻灯片自动切换
    runInv(){
      this.invId = setInterval(()=>{
        this.goto(this.nextIndex)
      },this.inv)
    },
    clearInv(){
      clearInterval(this.invId)
    }
  },
  mounted(){
    this.runInv()
  }
  • 定义动画

上面的轮播有点生硬,所以我们加上vue过渡效果。

有某一时刻是同时存在两张照片的(CSS中利用overflow:hidden隐藏溢出的图片),所以有两个<img>标签,利用v-if条件渲染让只有一个图片出现

<!-- 动画 -->
        <transition name="slide-trans">
          <img v-if="isShow" :src="slides[nowIndex].src" alt="slides[nowIndex].title">
        </transition>
        <transition name="slide-trans-old">
          <img v-if="!isShow" :src="slides[nowIndex].src" alt="slides[nowIndex].title">
        </transition>
.slide-trans-enter-active{
  transition: all 1s;
}
.slide-trans-enter{
  transform: translateX(1200px);
}
.slide-trans-old-leave-active{
    transition: all 1s;
    transform: translateX(-1200px);
}

vue组件完整代码:

<!-- 布局 -->
<template>
  <div class="slide-show" @mouseover="clearInv" @mouseout="runInv">
    <!-- v-for="item in slides" -->
    <div class="slide-img" >
      <a :href="slides[nowIndex].href" >

        <!-- 动画 -->
        <transition name="slide-trans">
          <img v-if="isShow" :src="slides[nowIndex].src" alt="slides[nowIndex].title">
        </transition>
        <transition name="slide-trans-old">
          <img v-if="!isShow" :src="slides[nowIndex].src" alt="slides[nowIndex].title">
        </transition>

        
      </a>
    </div>
    <h3>{{ slides[nowIndex].title }}</h3>

    <ul class="slide-page" >
      <li @click="goto(preIndex)">&lt;</li>
      <li v-for="(item,index) in slides" @click="goto(index)">
        <a href="" :class="{on:index == nowIndex}">{{index + 1}}</a>
      </li>
      <li @click="goto(nextIndex)">&gt;</li>
    </ul>
  </div>
</template>
<script>
export default {
  // props:子组件接受的什么属性
  props:{
    slides:{
      type:Array,
      default:[]//初始值为空
    },
    inv:{//父级传递
      type:Number,
      default:1000
    }
  },
  data(){
    return{
      nowIndex:1,
      isShow:true
    }
  },
  computed:{
    preIndex(){
      if(this.nowIndex == 0){
        return this.slides.length-1
      }else{
        return this.nowIndex - 1
      }
    },
    nextIndex(){
      if(this.nowIndex == this.slides.length-1){
        return 0
      }else{
        return this.nowIndex + 1
      }
    }
  },
  methods:{
    goto(index){
      this.isShow = false
      setTimeout(()=>{
        this.isShow = true
        this.nowIndex = index
        //index传给父组件,实现交互
        // this.$emit('onchange',index)
        //
      },10)
    },
    //幻灯片自动切换
    runInv(){
      this.invId = setInterval(()=>{
        this.goto(this.nextIndex)
      },this.inv)
    },
    clearInv(){
      clearInterval(this.invId)
    }
  },
  mounted(){
    this.runInv()
  }
}
</script>
<style scoped>
.slide-trans-enter-active{
  transition: all 1s;
}
.slide-trans-enter{
  transform: translateX(1200px);
}
.slide-trans-old-leave-active{
    transition: all 1s;
    transform: translateX(-1200px);
}
/* 淘宝css初始化 */
body, h1, h2, h3, h4, h5, h6, hr, p, blockquote, dl, dt, dd, ul, ol, li, pre, form, fieldset, legend, button,
  input, textarea, th, td { margin:0; padding:0; }
  body, button, input, select, textarea { font:12px/1.5tahoma, arial, \5b8b\4f53; }
  h1, h2, h3, h4, h5, h6{ font-size:100%; }
  address, cite, dfn, em, var { font-style:normal; }
  code, kbd, pre, samp { font-family:couriernew, courier, monospace; }
  small{ font-size:12px; }
  ul, ol { list-style:none; }
  a { text-decoration:none; }
  a:hover { text-decoration:underline; }
  sup { vertical-align:text-top; }
  sub{ vertical-align:text-bottom; }
  legend { color:#000; }
  fieldset, img { border:0; }
  button, input, select, textarea { font-size:100%; }
  table { border-collapse:collapse; border-spacing:0; }


.slide-show{
  height: 400px;
  width: 1200px;
  position: relative;
  overflow: hidden;
}
.slide-show h3{
  width: 100%;
  position: absolute;
  color: #fff;
  background-color: #000;
  opacity: 0.7;
  bottom: 0px;
  padding: 10px 0px;
  text-indent: 20px;
  font-weight: 500;
}
.slide-img img{
  width: 1200px;
  position: absolute;
  top: 0;
}
.slide-page{
  right: 15px;
  bottom: 0px;
  position: absolute;
}
.slide-page .on{
  text-decoration: underline;
}
.slide-page li{
  list-style: none;
  float: left;
  display: inline-block;
  padding: 0 10px;
  cursor: pointer;
  color: #fff;
  font-size: 14px;
  height: 32px;
}
.slide-page li a{

  display: block;
  float:left;
  color: #fff;
  text-decoration: none;
}
.slide-page li:hover{
  color: #1fdd88;
}
.slide-page a:hover{
  color: #1fdd88;
}
</style>