Notices: 这是我一个项目中的一个子组件,要展示的数据、图片地址等的都在父组件data中。所以后面的讲述都是基于从父组件获取的参数进行处理。(如需将这个SlideShow写成一个单独的主组件,将本文使用的data写在这个组件的data中)
必备知识:
效果图:
组件构成:
这部分很简单吧,常规的显示图片及轮播数字下标。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><</li> <li> <a href="">1</a> <a href="">2</a> <a href="">3</a> </li> <li>></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)"><</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)">></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>