美文网首页移动开发作家群(719776724)分享专题前端知识
jQuery实现视差滚动轮播代码解析之 JavaScript

jQuery实现视差滚动轮播代码解析之 JavaScript

作者: CoderXLL | 来源:发表于2019-04-24 18:45 被阅读58次

    系列

    jQuery实现视差滚动轮播代码解析之 HTML+CSS

    神图镇楼

    一、前言

             接上一章,HTML+CSS针对此完成了他们的使命,接下来该JS大显神通了。上一章最后我们也分析了JS要弥补HTML+CSS不能实现的哪些逻辑,所以本章我们就讲解如何使用JS处理这些逻辑。
             因为是使用jQuery这个框架(对原生JS的优雅封装,使JS更简洁地调用),所以先在body后引入jQuery框架。我采用的是cdn的方式引入。并创建index.js用以处理本项目的js逻辑。
    请注意我在代码块里的注释很重要,是精髓

    <script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
    //注意,index.js的引入一定要在jQuery下面,否则使用jQuery会报错。因为前端代码都是自上而下进行编译执行。
    <script type="text/javascript" src="js/index.js"></script>
    

    二、根据选择器获取所有需要用到的标签

    根据上节末尾的分析:

    js需要处理的逻辑.png
    JS是针对轮播部分进行的逻辑操作。所以我们首先通过dom操作获取到所有轮播部分的标签。

    jQuery处理dom节点是在documentready闭包中。这与原生JS的window.onload有本质区别。网上有很多这两者的比较,介绍的也很详细。

    $(document).ready(function () {
        //在这里开始JS操作
        startJSAction();
    });
    
    //获取所有轮播部分的标签
    var startJSAction = function(){
         //获取轮播容器标签banner
         var banner = $("#banner");
        //获取banner下的所有大图容器(大图ul标签)
        var banner_images = $(".banner_images", banner);
        //获取所有轮播图片容器下的子标签(ul下的li标签集合)
        var images = banner_images.children();
        //记录轮播图数量
        imagesCount = images.length;
        //获取前置后置标签
        var bannerGuide_last = $(".bannerGuide_last", banner);
        var bannerGuide_next = $(".bannerGuide_next", banner);
        //获取3张背景图
        var bannerBg01 = $(".bannerBg01", banner);
        var bannerBg02 = $(".bannerBg02", banner);
        var bannerBg03 = $(".bannerBg03", banner);
        //获取缩略图容器(缩略图ul标签)
        var banner_thumbnails = $(".banner_thumbnails", banner);
        //获取缩略图容器下的所有子标签(ul下的li标签集合)
        var thumbnails = banner_thumbnails.children();
        //获取loading框和banner_content
        var banner_loading = $(".banner_loading", banner);
        //获取轮播主体容器
        var banner_content = $(".banner_content", banner);
        //获取轮播主体容器下的所有图片(包括大图与缩略图)
        // var allImages = $("img", banner_content);
        var allImages = banner_content.find("img");
        //记录当前轮播图展示位置
        currentIndex = 0;
    };
    

    三、JS处理遗留逻辑

    1.所有图片加载完成后,隐藏加载框,显示轮播主体

    隐藏加载框,显示轮播主体很简单。上面我们通过dom操作已经获取到了加载框和轮播主体。通过jQuery语法如下:

    //隐藏加载框
    banner_loading.hide();
    //显示轮播主体
    banner_content.show();
    

    但是关键问题是怎么获取到所有图片加载完成这个节点!!!
    我们知道jQuery中有个load函数,其有个闭包参数是作为img标签中图片加载完成的回执(这种说法实际是不严谨的,load函数可以监听所有有url的加载完成的回执)。
    但是这里有个问题,在浏览器中,加载后的图片有可能会被缓存。也就是说下次load闭包因为缓存问题就不能监听到回执。
    这种情况会导致下次浏览器再次加载轮播图,就不会执行到“隐藏加载框,显示轮播主体及其以后的逻辑”。这是个灾难~
    解决流程:
    程序员肯定有他屌丝的办法去解决一切看似不可能完成的问题~

    • 遍历轮播主体中所有图片标签。
    • 遍历时创建一个新的img标签,并拥有和轮播主体一样的src属性
      这样,新的img标签在每次加载组件时就不会担心缓存问题。我们在load闭包中,通过已加载图片的数量与原图片数量进行比较,就可以获取所有图片加载完成的节点。
      代码如下:
    //遍历这些图片
    loaded = 0; 
    allImages.each(function () {
        //新建img标签,并将原图片的src赋值给新的img标签
        //新建的原因,是避免原img缓存后,不执行load闭包
        $('<img/>').load(function () {
            ++loaded;
            if (loaded == imagesCount * 2) { //说明加载完成
                   //...这里处理之后的所有逻辑
            }
        }).attr('src', $(this).attr('src'));
    });
    
    2.重新设置大图与前置后置标签布局

    上次也分析了,我们的大图容器ul标签下的li标签因为设置了左浮动,所以变为行内块级标签。所以我们要根据大图数量设置ul标签的宽度,使其内部的行内块级标签不用换行。

    //获取窗口宽度
    var window_width = $(window).width();
    var set_width = function (banner_images,
                              images,
                              imagesCount,
                              bannerBg01,
                              bannerBg02,
                              bannerBg03,
                              firstImage_width,
                              bannerGuide_last,
                              bannerGuide_next) {
        //根据图片数量计算出需要的总宽度
        var contentSize_Width = imagesCount * window_width;
        //设置大图容器宽度(大图ul标签宽度)
        banner_images.width(contentSize_Width + 'px');
        //设置所有的图片容器的子标签的宽度(li标签宽度)
        images.width(window_width + 'px');
        //设置背景图的宽度
        bannerBg01.width(contentSize_Width + 'px');
        bannerBg02.width(contentSize_Width + 'px');
        bannerBg03.width(contentSize_Width + 'px');
        //计算出前后置标签的绝对位置
        var guidePosition = (window_width - firstImage_width) * 0.5 + 3;
        bannerGuide_last.css({"left": guidePosition + 'px'});
        bannerGuide_next.css({"right": guidePosition + 'px'});
    };
    
    3. 计算缩略图绝对位置,设置随机偏移角度,并为其绑定事件

    CSS中,设置了每个缩略图的li标签相对于其容器ul为绝对定位,但是并没有设置其定位的值,所以目前缩略图都是重叠在一起的。我们要通过图片数量计算定位,将其均匀分布在屏幕中。

    banner_thumbnails.css({
      //设置缩略图容器宽度
      "width": firstImage_width + 'px',
      //设置margin-left,让其居中显示
      "margin-left": -firstImage_width * 0.5 + 'px'
    });
    //遍历缩略图
    thumbnails.each(function (index) {
      //计算缩略图之间的间距
      var thumbnailSpace = firstImage_width / (thumbnails.length + 1);
      //随机偏移角度
      var angle = Math.floor(Math.random()*41)-20;
      $(this).css({
         //设置绝对定位,使其均匀分布
        "left": thumbnailSpace * (index + 1) - $(this).width() * 0.5,
        //设置随机偏移
        "transform": 'rotate('+ angle +'deg)'
      });
      //为每个缩略图绑定指定事件
      $(this).bind('mouseenter', function () {
        //相当于设置hover伪类
        $(this).stop().animate({top:'-10px'}, 100);
      }).bind('mouseleave', function () {
        $(this).stop().animate({top:'0px'}, 100);
      }).bind('click', function () {
        //绑定点击事件
        currentIndex = index;
        //轮播滚动到指定大图
        scrollTo(currentIndex, banner_images, bannerBg01, bannerBg02, bannerBg03);
        对应缩略图高亮显示
        thumbHighlight(thumbnails.eq(currentIndex));
      });
    });
    
    4. 轮播滚动至指定位置,并设置对应缩略图高亮

            因为大图容器是相对于轮播容器的绝对定位,所以滚动到指定位置,我们可以动态更改大图容器(ul标签)的left值,就可以满足需求。
            而缩略图高亮,我们在CSS阶段就考虑到了,有种带selected类选择器的就是高亮状态,所以我们只需要设置其是否拥有这个类选择器,从而改变其高亮状态。

    var window_width = $(window).width();
    //大图滚动至指定位置
    var scrollTo = function (currentIndex,
                             banner_images,
                             bannerBg01,
                             bannerBg02,
                             bannerBg03) {
        //设置当前大图需要在X轴的左偏移量
        var contentOffsetX = -window_width * currentIndex;
        //动画更新大图容器left值
        banner_images.stop().animate({
            left: contentOffsetX + 'px'
        }, 1000, 'swing');
        //动画更新背景一偏移量
        bannerBg01.stop().animate({
            left: contentOffsetX * 0.5 + 'px'
        }, 1000, 'swing');
        //动画更新背景二偏移量
        bannerBg02.stop().animate({
            left: contentOffsetX * 0.25 + 'px'
        }, 1000, 'swing');
        //动画更新背景三偏移量
        bannerBg03.stop().animate({
            left: contentOffsetX * 0.125 + 'px'
        }, 1000, 'swing');
    };
    

    有很多小伙伴很好奇什么是视差轮播。其实这里的代码已经说明了一切,因为有三张背景图。且轮播滚动的时候,三张背景图的偏移量不同步,就产生了视差的效果

    //设置缩略图高亮
    var thumbHighlight = function (thumbElement) {
        //兄弟节点都移除selected类选择器
        thumbElement.siblings().removeClass('selected');
        //自身添加selected类选择器
        thumbElement.addClass('selected');
    };
    
    5.为前置后置绑定点击事件
    //为前置标签绑定点击事件
    bannerGuide_next.bind('click', function () {
      //切到最后,变为初始的第一张
      ++currentIndex;
      if (currentIndex >= imagesCount) {
        currentIndex = 0;
      }
      //滚动至指定大图
      scrollTo(currentIndex, banner_images, bannerBg01, bannerBg02, bannerBg03);
      //设置指定缩略图高亮
      thumbHighlight(thumbnails.eq(currentIndex));
    });
    //..为后置标签绑定点击事件
    

    四、代码优化重构

    上面的一系列js操作,实际上已经完成了项目的需求,但是却不够优雅。因为我一开始就称这个小项目可以作为一个组件,作为组件肯定不只是要满足这一个需求,所以我们要对代码进行重构。
    设置jQuery对象的默认参数

    $.fn.banner.defaults = {
            //自动轮播时长(为0不进行自动轮播)
            autoDuration: 0,
            //动画类型
            easing: 'swing'
            //...后期扩展组件功能进行相应增加
    };
    

    定义一个自执行函数,并以jQuery作为参数传入。
    在自执行函数中,为所有jQuery对象扩展一个新的函数,此函数就是处理轮播的js逻辑。

    这个扩展的新函数,类似于iOS中的分类。而且js中返回其本身,好处是可以继续点语法。也就是延续链式编程

    //这里jQuery为实参,$为形参
    (function ($) {
        //扩展一个名为banner的jQuery函数
        $.fn.banner = function(options){
            //对jQuery对象传入的参数与默认参数进行合并。
            //通过opts,就可以对组件功能进行任意扩展
            var opts = $.extend({}, $.fn.banner.defaults, options);
            //遍历jquery实例,并返回其本身
            //返回本身的好处是可以继续链式编程
            return this.each(function () {
                  startJSAction(opts);
            });
        };
    }(jQuery);
    
    $(document).ready(function () {
        //获取轮播容器,调用新扩展的名为banner的jQuery函数
        $("#banner").banner({});
    });
    

    六、总结

    因为算是web前端入门者,所以也没有过多地理论性地阐述为什么要这么做。我们先做的就是理解这些代码是什么意思,吸收体会这样写的好处,并慢慢接受对我来说是新语言的思想。
    以上是我本人使用jQuery完成的第一个小项目。解释并简单说明了每一行代码。作为新人,通过这两篇文章基本上能让你看懂这些代码的含义。如果发现问题,请不吝指明,谢谢。
    最后再次声明:这是Demo,如果您觉得文章与代码对您学习js有了那么一些提升,请点个赞,并给个Star鼓励一下😀

    相关文章

      网友评论

        本文标题:jQuery实现视差滚动轮播代码解析之 JavaScript

        本文链接:https://www.haomeiwen.com/subject/rbhggqtx.html