缘由:某天在某群瞟了一眼,有人在找轮播图,,,
想想,自己来写一个,(可能的话,分别用vue、react、angular)
找了某老师用原生js写的轮播图,研究了一下,研究的结果大体如下:
html文档结构:
外层一个div 容器,包裹 2 个子元素,第一个子元素div包裹一个Ul列表,每个li元素包裹一个img元素,即需要显示的图片。第二个子元素是一个ul列表,具体效果见下面的截图:
解析:
1, 第2个ul列表,即按钮,由代码动态生成。
2,设置计时器,每3秒调用移动函数,此函数重设图片列表容器list的top值
3,按钮添加Mouseover事件,触发事件,则图片切换至按钮关联图片
tips:在css中,已经把class为list的图片容器div设置为 position:relative;设置图片ul为position:absolute;
此时,5张图上由上到下,排列为一张大图片,,只需要定时更改ul的style属性top,即可显示不同的图片。
------------------------------------------------------分割线---------------------------------------------------------------------------
现在改为用vue来重构
1,在图片列表ul上,绑定style为一个样式对象:stlyes。此styles是一个对象字面量,包含一个属性top,默认值为0。
<ul :style="styles"> <li><img src="./img/01.jpg" width="490" height="170"></li> <li><img src="./img/02.jpg" width="490" height="170" /></li> <li><img src="./img/03.jpg" width="490" height="170" /></li> <li><img src="./img/04.jpg" width="490" height="170" /></li> <li><img src="./img/05.jpg" width="490" height="170" /></li> </ul>
留意ul列表所绑定的动态属性,我们将通过定时更改Ul图片列表的top值,来达到让图片动起来的效果。
a首先,让图片动起来,为此,我们需要先定义 cutover 函数,此函数会调用移动函数 startMove,让图片动起来,我们给此函数传入了一个移动值,即需要移动的距离,170为图片高度,这里硬编码了,原生应用中是通过js来获取图片的高度的,index则是图片索引,表明需要移动至第几张图片
cutover(){ this.startMove(-(this.index * 170)) },
定义了此函数后,需要先初始化,也就是把它放在组件钩子函数 created中;
created(){ this.cutover(); this.playTimer = setInterval(this.next,3000) }
b 要让图片连续不断的动起来,还需要一个计时器,此计时器调用一个next函数,此函数作了这样几件事:根据bOrder,一个bool值,来判断图片索引值index应该递增/递减,接着,判断Index值是否溢出,因为图片总共 5 张,则index 的值被限制在了 0 至4之间。(这里硬编码了)随后调用 cutover
next(){ this.bOrder ? this.index++ : this.index--; this.index <= 0 && (this.index=0,this.bOrder=true); this.index >= 4 && (this.index=4, this.bOrder = false); this.cutover(); },
c到此,图片加载后,每隔 3 秒钟,就会自动向上移动,移动至第5张图片后,随后向下移动,依次显示图片。
如果打开浏览器,会发现图片切换时,很生硬,因为移动得很快,要想让移动显得柔和一点,需要调整下移动图片的速度,不能一次到位。所以负责移动的函数 startMove()又做了以下一些事情 :
startMove(distance){ clearInterval(this.timer); this.timer=setInterval(()=> this.doMove(distance),30) }, doMove(distance){ var iSpeed = (distance - parseInt(this.styles.top)) / 10; iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed); parseInt(this.styles.top)==distance ? clearInterval(this.timer) : this.styles.top = parseInt(this.styles.top) + iSpeed + 'px'; }
2 我们还需要实现一个效果,即图片上一列按钮,分别关联着对应的图片,当鼠标按钮mouseover到按钮,会显示对应的图片
a首先,不再直接操作dom来生成按钮列表,改为在template中布局:
<ul class="count"> <li v-for="i in 5" :key="i-1" @mouseover="handleButtonOver(i-1)" :class="classes(i-1)" >{{ i}}</li> </ul>
仔细阅读上面的代码,可以看到,我们在每一个li元素上,为mouseover事件绑定了一个处理器,绑定了一个动态类,这里使用了一个函数,而没有使用计算性属性,因为函数才能接受参数
handleButtonOver(num){ this.index = num; this.cutover(); }, classes(num){ return[ { 'current':this.index==num } ] },
至此,我们的轮播图应用大体成型了。
完整的代码见下图:
<template> <div id="box" @mouseover="handleOver" @mouseout="handleOut"> <div class="list" > <ul :style="styles"> <li><img src="./img/01.jpg" width="490" height="170"></li> <li><img src="./img/02.jpg" width="490" height="170" /></li> <li><img src="./img/03.jpg" width="490" height="170" /></li> <li><img src="./img/04.jpg" width="490" height="170" /></li> <li><img src="./img/05.jpg" width="490" height="170" /></li> </ul> </div> <ul class="count"> <li v-for="i in 5" :key="i-1" @mouseover="handleButtonOver(i-1)" :class="classes(i-1)" >{{ i}}</li> </ul> </div> </template> <script> //import HelloWorld from './components/HelloWorld.vue' export default { name: 'App', components: { }, data(){ return{ styles:{ top:0 }, index:0, bOrder:true, timer:null, playTimer:null, } }, methods:{ handleButtonOver(num){ this.index = num; this.cutover(); }, classes(num){ return[ { 'current':this.index==num } ] }, handleOver(){ clearInterval(this.playTimer); }, handleOut(){ this.playTimer = setInterval(this.next,3000); }, cutover(){ this.startMove(-(this.index * 170)) }, next(){ this.bOrder ? this.index++ : this.index--; this.index <= 0 && (this.index=0,this.bOrder=true); this.index >= 4 && (this.index=4, this.bOrder = false); this.cutover(); }, startMove(distance){ clearInterval(this.timer); this.timer=setInterval(()=> this.doMove(distance),30) }, doMove(distance){ var iSpeed = (distance - parseInt(this.styles.top)) / 10; iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed); parseInt(this.styles.top)==distance ? clearInterval(this.timer) : this.styles.top = parseInt(this.styles.top) + iSpeed + 'px'; } }, created(){ this.cutover(); this.playTimer = setInterval(this.next,3000) } } </script>