预览地址
图片的轮播
假设需要轮播三张图片(1,2,3),以前的思路就如图所示,添加两个节点。通过索引(index)的切换实现组件的无缝轮播。
这种想法的确可行,而且实现出来效果还不错。 缺点在于
- 大量的dom操作。
- 代码逻辑相对挺复杂,量也更多。
- 重构或添加新功能会更麻烦
现在的思路
创建两个组件分别为 carousel
和 carousel-item
结构如下
<x-carousel :selected.sync="selected">
<x-carousel-item name="1">
<div class="box"> 1div>
x-carousel-item>
<x-carousel-item name="2">
<div class="box">2div>
x-carousel-item>
<x-carousel-item name="3">
<div class="box">3div>
x-carousel-item>
x-carousel>
复制代码
selected
即为显示的内容的name
,用sync
做一个"双向绑定"。这样子父组件就可以通过 updated
钩子和$children
来实时通知子组件,从而控制内容的展示。
updated(){
this.updateChildrens(this.selected)
}
methods:{
updateChildrens(){
//被选中的那个可以显示了,其他的关闭
}
}
复制代码
也就是说 carousel
负责数据通信,而carousel-item
只需完成动画过渡效果就行了,这样逻辑就非常清晰了。
这里当然就存在动画正向与反向的问题,需要两种方向不同的切入切出的动画。 carousel
需要做一次判断然后在updateChildrens
的时候就告诉子组件方向。
进入方向的判断
- 需要一个变量记录上一次的
selected
数值,假设就为oldSelected
和newSelected
- 自动轮播是默认正向的(往后播放),到最后一个的时候回到第一个应该也是正向的
- 圆点(图片索引图标)选取切换,只需判断两次变量的大小就行
- 方向键切换(箭头图标),和自动轮播同理,方向应时刻和箭头方向一致
解决跳过中间图片的问题
不管轮播图数量多少,这里始终只在两张图里面切换。这样就涉及到一个问题就是会 跳过中间的图片
首先carousel-item
有一个默认的图片过渡时间,这里可以通过计算oldSelected
和newSelected
之间的差值来确定跳过图片的数量。当然也有动画方向的问题。
clickSelected(newSelected){
clearInterval(this.timer2)
if(oldSelected===newSelected)return
lastSelected = oldSelected
// .............
this.‘控制时长的函数’(lastSelected,newSelected)
},
'控制时长的函数'(lastSelected,newSelected){
//........
let newIndex = newSelected
let animationDuration = '计时器的间隔时长'
theIndex = ‘下一个展示的图片索引’
//.......
this.duration = duration
this.'carousel组件'.forEach(vm=>vm.duration=duration)
this.$emit('update:selected',names[theIndex])//通知一下父组件将要展示的下一个图片的索引
if(theIndex===newIndex)return
this.timer2 = setInterval(()=>{
if(theIndex===newIndex){
this.clearAndSet()
}
this.$emit('update:selected',names[theIndex])
oldIndex>newIndex?theIndex--:theIndex++
},duration*animationDuration)
}
复制代码
基本就能完成跳过中间图片的这样子的问题了,后面的click
改为 hover
触发功能就很简单了。
Card卡片化
需要默认三个同时出现的图片,这意味着需要一个数组。 但是依然不需要改变selected
的数据类型(还是字符串)。这种情况用传递数组只会添加许多不必要的麻烦和降低性能,像是需要做深拷贝,遍历判断这类的。 因为这个
应该出现的图片的数组里面的
index
都是连号的。这个判断只需让子组件来做就行了。
现在在carousel-item
通过计算得到一个数组
this.cardSelected = [selected-1,selected,selected+1]
if(`最后一张图`){
//.....
}else if(`第一张图`){
}
复制代码
现在实时显示的三张图片的数组已经有了,我只需要分配好他们的位置(左边,中间,右边
'我是决定位置的函数'(){
let [index,position] = [this.cardSelected.indexOf(Number(this.name)),['left','main','right']]
return `position-${position[index]}`
}
复制代码
简单的两行就搞定了。
然后绑定一下
:class="{......,[我是决定位置的函数]:card}">
复制代码
剩下的定位还是动画什么的,都可以交给css去完成了。
&.position-left{
width: 50%;
position: absolute;
top: 0;
left: -10px;
transform:scale(0.82);
}
&.position-main{
width: 50%;
transform: translateX(50%);
position: relative;
z-index: 3;
}
&.position-right{
transform: translateX(100%) scale(0.82);
width: 50%;
position: absolute;
top: 0;
left: 10px;
}
复制代码
最后就是点击两侧图片会切换
调用父组件的方法就ok了
'调用父组件的方法'(){
let [direction,index] = [this.'我是决定位置的函数'.slice(9,16),this.$parent.selectedIndex]
if(direction==='main')return
let move = {left:'back', right:'go'}
this.$parent.'我是父组件的方法'(index,move[direction])
}
复制代码
尚未完善的细节
其实我认为动画还是有一点点瑕疵的,后面会在css上修改一下,顺便简单调整样式和更换动态svg。最后,有待加强的地方希望大佬们指出来交流,要是觉得还行的话,给我的项目点个star就是最好的了。