系列
一、前言
接上一章,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是针对轮播部分进行的逻辑操作。所以我们首先通过dom操作获取到所有轮播部分的标签。
jQuery处理dom节点是在document的ready闭包中。这与原生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鼓励一下😀