vue上下轮播组件简单实现
在vue社区里面没有找到特别好的上下轮播插件,基本都是图片的左右播放插件,质量也是参差不齐在实现这个组件之前,先抛出一个问题,如何在vue中实现dom的环形结构? 首先来看轮播组件的思路 红色部分:相当于放映机,也就是容器,overflow:hidden 绿色内容:相当于胶片,也就是dom 当放映机从上往下移动的时候,从视觉上看,就像...
在vue社区里面没有找到特别好的上下轮播插件,基本都是图片的左右播放插件,质量也是参差不齐
在实现这个组件之前,先抛出一个问题,如何在vue中实现dom的环形结构?
首先来看轮播组件的思路
红色部分:相当于放映机,也就是容器,overflow:hidden
绿色内容:相当于胶片,也就是dom
当放映机从上往下移动的时候,从视觉上看,就像是胶片在从下往上放映,ok,播的核心思路解决了。
现在的问题是怎么轮?
传统的jq控制dom的思想,你可以在第一张胶片移出放映机区域的时候,动态移动到最后一张胶片的后面,依此类推,形成了一个dom的环形结构。
那么在vue中如何用v-if v-show去实现这个环形结构呢?在不破坏文档流的情况下,暂时还没想到好的解决方案(所有子元素用绝对定位可以解决,动态计算所有子元素的position,这样破坏了文档流,不予考虑)
在vue的传统中,这个问题怎么解决呢?
1.数据驱动模板
vue的核心之一,数据驱动模版,循环播放映射的数据上
就是 [1,2,3,4,5,6] ---》[2,3,4,5,6,1] ---》 [3,4,5,6,1,2]
ok,通过改数据来实现循环播放的思路有了,就是把数组的头移动到数组的尾巴去,很简单
//这方法被窝删了,随意手打一下
setInterval(function(){
arr.push(arr[1]) //先加一个到尾巴
arr.shift() //然后去个头
},3000)
2.投机取巧
上面的方法有个很难搞定的地方,就是动画,数据掐头"去"尾后,dom重新生成了,那动画怎么办?
回归传统
先实现放映机向下移动的方式(用transition,tranform等等)
为了能实现循环,将胶卷复印一份跟在原先的尾部,这样放映机移动到最后一张胶卷的时候,仍能保证后面跟着第一张、第二张....看起来像是ending - > begin
最关键的就是如何处理临界点,也就是如何实现"dom的环形结构"
刚才已经说了,没想出来,所以当放映机放到最后一张的时候,作为放映员的你,就突然关闭了物理世界运动轨迹的规则,就把第一张胶卷,一下子,注意这个一下子!你就一下子把第一张复位了,同时取消这个运动轨迹,也就是通过设置transition的动画时间,当动画时间为0的时候,肉眼就捕捉不到这个改变了,天知地知你知我知,看得人都不知道。
说了这么多,看简单版本的代码如下:
<template>
<div :style="{height:height*lineNum + 'px'}" class="rollScreen_container" id ="rollScreen_container">
<ul class="rollScreen_list" :style = {transform:transform} :class="{rollScreen_list_unanim:num===0}">
<li class="rollScreen_once" v-for="(item,index) in contentArr" :key=index :style="{height:height+'px'}">
<span>{{item}}</span>
</li>
<li class="rollScreen_once" v-for="(item,index) in contentArr" :key=index+contentArr.length :style="{height:height+'px'}">
<span>{{item}}</span>
</li>
</ul>
</div>
</template>
<script>
export default {
props: {
height: {
default: 40,
type: Number
},
lineNum: {
default: 5,
type: Number
}
},
data: function () {
return {
contentArr: ['内容1', '内容2', '内容3', '内容4', '内容5', '内容6', '内容7'],
num: 0
}
},
computed: {
transform: function () {
return 'translateY(-' + this.num * this.height + 'px)'
}
},
created: function () {
let _this = this
setInterval(function () {
if (_this.num !== _this.contentArr.length) {
_this.num++
} else {
_this.num = 0
}
}, 3000)
}
}
</script>
<style>
.rollScreen_container{
display: inline-block;
position:relative;
overflow: hidden;
}
.rollScreen_list{
transition: 1s linear;
}
.rollScreen_list_unanim{
transition: none
}
</style>
复杂版本代码如下:
<template>
<div :style="{height:height*lineNum + 'px'}" class="rollScreen_container" id ="rollScreen_container">
<ul class="rollScreen_list" :style = {transform:transform} :class="{rollScreen_list_unanim:num===0}">
<li class="rollScreen_once" v-for="(item,index) in contentArr" :key=index :style="{height:height+'px',lineHeight:height+'px'}">
<span>{{item}}</span>
</li>
<li class="rollScreen_once" v-for="(item,index) in contentArr" :key=index+contentArr.length :style="{height:height+'px',lineHeight:height+'px'}">
<span>{{item}}</span>
</li>
<slot name="slide"></slot>
</ul>
</div>
</template>
<script>
export default {
props: {
height: {
default: 40,
type: Number,
required: true
}, // 每行元素的高度
lineNum: {
default: 5,
type: Number
}, // 显示行数
contentArr: {
default: null,
type: Array
}, // 简单文本轮播
dLength: {
default: null,
type: Number
}, // 自定义插槽内容的时候必须传自定义内容的长度
time: {
default: 3000,
type: Number // 定义轮播切换速度
}
},
data: function () {
return {
num: 0,
loopTime: this.time
}
},
computed: {
transform: function () {
return 'translateY(-' + this.num * this.height + 'px)'
}
},
beforeCreate: function () {
},
created: function () {
let _this = this
if ((this.contentArr != null && this.contentArr.length < this.lineNum) || (this.dLength != null && this.dLength < this.lineNum)) {
console.error('轮播显示行数不能超过数据总行数')
} else {
// 先判断轮播切换速度,如果小于动画播放时间则提示切换速度过快
if (_this.loopTime <= 1000) {
console.warn('轮播切换速度过快,至少大于1s')
_this.loopTime = 1000
}
// 两种轮播 第一种contentArr!= null 第二种自定义插槽
if (_this.contentArr !== null) {
setInterval(function () {
if (_this.num !== _this.contentArr.length) {
_this.num++
} else {
_this.num = 0
setTimeout(function () {
_this.num++
}, 50)
}
}, _this.loopTime)
} else if (_this.dLength !== null) {
setInterval(function () {
if (_this.num !== _this.dLength) {
_this.num++
} else {
_this.num = 0
setTimeout(function () {
_this.num++
}, 50)
}
}, _this.loopTime)
} else if (_this.dLength === null && _this.dLength === null) {
console.error('contentArr 和 dLength 均为空,rollScreen组件运行出错')
}
}
},
mounted: function () {
// 复制一份slot的节点,如果直接用同名slot会报错
if (this.dLength !== null) {
for (let i = 0; i < this.dLength; i++) {
this.$el.childNodes[0].appendChild(this.$slots.slide[i].elm.cloneNode(true))
}
}
}
}
</script>
<style>
.rollScreen_container{
display: inline-block;
position:relative;
overflow: hidden;
}
.rollScreen_list{
transition: 1s linear;
padding:0;
margin:0;
list-style: none;
}
.rollScreen_list_unanim{
transition: none
}
.rollScreen_once{
list-style: none;
}
</style>
更多推荐
所有评论(0)