移动端无缝滑屏实现

  1 布局
    布局的包裹器:占满视口的宽度
    滑屏元素(动态生成,根据图片的数量)
    注意:1、querySelector获取的是静态列表,动态添加元素后要重新获取一遍
         2、有时候绘制跟不上js引擎的绘制,解决方法:定时器


  2 基本滑屏
    拿到元素和手指一开始(点击到布局包裹器上时)的位置;
    拿到元素的实时位置,计算手指实时的偏移量,将偏移量实时同步给滑屏元素;
  3 版本
    定位版(调整滑屏元素的left偏移量)
      offsetLeft:累加的过程
      -- 抽象图片的下标
      -- 抽象滑屏元素的位置
    2d变换版(单独的图层,触发的重绘重排代价较小,利用transform,translate)
      translate的变换不会同步到offsetLeft
         解决办法:
            -- 变量,设置一个变量专门去同步translate,但是这会让业务逻辑变得复杂
            -- 定义css函数,一定一个css函数,专门处理transform中设置的各种变换的值
              利用节点的属性来管理变换类型,及其所对应的值
              2个参数:
                读取操作
              3个参数:
                设置操作
                循环节点的属性
  4 无缝自动滑屏
    无缝:复制一组图片,当点击第一组的第一张图片的时候,瞬间跳到第二组的第一张
                     当点击第二组的最后一张时,瞬间跳转到第一组的最后一张
    自动滑屏:循环定时器
                函数包裹(重启定时器)
                清定时器(循环定时器的逻辑没有必要开启多次,暂停逻辑)
    自动滑屏和无缝的冲突:
        使用同一个index变量就可以
                
      
    html文件中js代码如下:其中引入的cl.js文件是将动态生成结构、自动滑屏和同步translate(css函数)代码抽取出来的js文件。
<script type="text/javascript" src="js/cl.js"></script>
<script type="text/javascript">
  window.onload = function () {
    document.addEventListener('touchstart',function (ev) {
      ev = ev || event
      ev.preventDefault()
    })

    var arr = ["img/01.jpg","img/02.jpg","img/03.jpg","img/04.jpg","img/05.jpg"]
    cl.carousel(arr)
  }
</script>

cl.js文件代码:

!(function (w) {
  // 将函数绑给window的cl属性上
  w.cl = {};
  w.cl.css = function(node,type,val) {
    // 判断节点transform对象是否存在,不存在则添加
    if (typeof node === "object" && typeof node["transform"] == "undefined") {
      node["transform"] = {};
    }
    if(arguments.length >= 3){// 写操作
      //设置
      var text = "";
      // 加入两次传入同样属性的变换,则会被覆盖
      // 只会更改改变的值,之前没有改变的值则不会改变
      node["transform"][type] = val;
      // in操作符,会查找对象实例本身及原型链上所有的属性,可能会对后续操作产生影响
      for (item in node["transform"]) {
        if(node["transform"].hasOwnProperty(item)){
          switch (item){
            case "translateX":
            case "translateY":
              text += item + "(" + node["transform"][item] + "px)";
              break;
            case "scale":
              text += item + "(" + node["transform"][item] + ")";
              break;
            case "roate":
              text += item + "(" + node["transform"][item] + "deg)";
              break;
          }
        }
      }
      // 兼容性问题
      node.style.transform = node.style.webkitTransform = text;
    }else if(arguments.length == 2){
      // 读操作
      // 此时只传入了2个参数,第3个参数并没有用上,所以可以利用未使用的第3个参数返回结果
      val = node["transform"][type];
      if (typeof val === 'undefined'){
        switch (type){
          case "translateX":
          case "translateY":
          case "rotate":
            val = 0;
            break;
          case "scale":
            val = 1;
            break;
        }
      }
      return val;
    }
  }
  w.cl.carousel = function (arr) {
    // 布局
    var carouselWrap = document.querySelector(".carousel-wrap");
    if(carouselWrap){
      var pointsLength = arr.length;

      // 无缝
      var needCarousel = carouselWrap.getAttribute('needCarousel');
      needCarousel = needCarousel == null? false:true;
      //console.log(needCarousel);
      if(needCarousel){
        arr = arr.concat(arr);
      }
      var ulNode = document.createElement('ul');
      var styleNode = document.createElement('style')
      ulNode.classList.add('list');
      for(var i=0;i<arr.length;i++){
        ulNode.innerHTML += "<li><a href='javascript:;'><img src="+ arr[i] + " alt='picture'></a></li>"
      }
      styleNode.innerHTML = ".carousel-wrap > .list{width:"+ (arr.length*100) +"%;}.carousel-wrap > .list > li{width:"+ (1/arr.length*100) +"%;}"
      carouselWrap.appendChild(ulNode);
      document.head.appendChild(styleNode);
      var imgNode = document.querySelector('.carousel-wrap > .list > li > a > img')
      // 如果不设置定时器,可能获取到img时,还没有渲染完,此时无法获取其高度
      setTimeout(function () {
        carouselWrap.style.height = imgNode.offsetHeight + 'px';
      },100)
      var pointsWrap = document.querySelector('.carousel-wrap > .points-wrap');
      if(pointsWrap) {
        for (var i = 0; i < pointsLength; i++) {
          if(i == 0){
            pointsWrap.innerHTML += "<span class='active'></span>";
          } else {
            pointsWrap.innerHTML += "<span></span>";
          }

        }
        var pointsSpan = document.querySelectorAll('.carousel-wrap > .points-wrap > span');
      }

      // 滑屏
      /*
      * 1 拿到元素一开始的位置
      * 2 拿到手指一开始点击的位置
      * 3 拿到手指move的实时距离
      * 4 将手指移动的距离加给元素
      */
      var index = 0;
      // 手指的位置
      var startX = 0;
      // 元素的位置
      var elementX = 0;
      // 保存实时偏移量
      //var translateX = 0;
      carouselWrap.addEventListener('touchstart',function (ev) {
        ev = ev || event;
        // changedTouches是列表,取一个就够了
        var touchC = ev.changedTouches[0];
        ulNode.style.transition = 'none';
        // 无缝逻辑,点击第一组的第一张是瞬间跳到第二组的第一张
        //点击第二组的最后一张时,瞬间跳到第一组的最后一张
        // index代表ul的位置
        // 无缝
        if(needCarousel){
          var index = cl.css(ulNode,"translateX")/document.documentElement.clientWidth;
          if(-index === 0){
            index = -pointsLength;
          } else if (-index === arr.length-1) {
            index = -pointsLength + 1;
          }
          cl.css(ulNode,"translateX",index*document.documentElement.clientWidth);
        }
        startX = touchC.clientX;
        //elementX = ulNode.offsetLeft;
        //elementX = translateX;
        elementX = cl.css(ulNode,"translateX")

        // 停止自动轮播
        clearInterval(timer)
      })
      carouselWrap.addEventListener('touchmove',function (ev) {
        ev = ev || event;
        var touchC = ev.changedTouches[0]
        // 获取滑动时手指的位置
        var nowX = touchC.clientX;
        // 手指移动的距离
        var disX = nowX - startX;
        //translateX = elementX + disX
        //ulNode.style.left = elementX + disX + 'px';
        cl.css(ulNode,"translateX",elementX+disX);
      })
      carouselWrap.addEventListener('touchend',function (ev) {
        ev = ev || event;
        //var index = ulNode.offsetLeft/document.documentElement.clientWidth;
        //var index = translateX/document.documentElement.clientWidth;
        index = cl.css(ulNode,"translateX")/document.documentElement.clientWidth;
        index = Math.round(index)
        if(index > 0){
          index = 0
        } else if (index < -arr.length+1) {
          index = -arr.length+1
        }
        syncPoints(index)
        ulNode.style.transition = '.5s left';
        //ulNode.style.left = index * (document.documentElement.clientWidth) + 'px'
        // translate造成的元素偏移并不会同步到offsetLeft上,因为两者不在同个一个图层
        //translateX = index * (document.documentElement.clientWidth)
        //ulNode.style.transform = "translateX("+ translateX + "px)" ;
        cl.css(ulNode,"translateX",index * (document.documentElement.clientWidth));
        // 开启自动轮播
        if(needAuto){
          auto();
        }
      })

      // 自动轮播
      var timer = 0;
      var needAuto = carouselWrap.getAttribute('needAuto')
      needAuto = needAuto == null ? false:true;
      //console.log(needAuto);
      if(needAuto){
        auto();
      }
      function auto() {
        clearInterval(timer)
        timer = setInterval(function () {
          if(index === -arr.length+1){
            ulNode.style.transition = "none";
            index = -arr.length/2+1;
            cl.css(ulNode,"translateX",index*document.documentElement.clientWidth)
          }
          setTimeout(function () {
            index--;
            ulNode.style.transition = "1s transform"
            syncPoints(index)
            cl.css(ulNode,"translateX",index*document.documentElement.clientWidth)
          },50)

        },2000)
      }

      // 同步小圆点的函数
      function syncPoints(index) {
        if(!pointsWrap){
          return;
        }
        // 同步小圆点
        for (var i = 0; i < pointsSpan.length; i++) {
          pointsSpan[i].classList.remove('active');
        }
        pointsSpan[-index%pointsLength].classList.add('active');
      }
    }
  }
})(window)
发布了50 篇原创文章 · 获赞 5 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_31207499/article/details/81980828
今日推荐