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

系列

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

神图镇楼

一、前言

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


//注意,index.js的引入一定要在jQuery下面,否则使用jQuery会报错。因为前端代码都是自上而下进行编译执行。

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

根据上节末尾的分析:

jQuery实现视差滚动轮播代码解析之 JavaScript_第1张图片
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闭包
    $('').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)