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

系列

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鼓励一下😀

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342