vue上下轮播组件简单实现

在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>

猜你喜欢

转载自blog.csdn.net/dkr380205984/article/details/80492067
今日推荐