jdc_dkh_jxicustomer
Version:
JDC_dkh_jxicustomer ==== 产品经理:田宇,弥磊 设计:罗婷 交互:张钊
692 lines (579 loc) • 23.6 kB
JavaScript
/**
* @file slide切换组件
* @version 1.0.0
* @author 龙泉 <yangtuan2009@126.com>
*/
(function (factory) {
if (typeof define === 'function' && define.amd) {
// AMD module
define(['jquery'], factory);
}
else if(typeof module !== "undefined" && module.exports) {
// Node/CommonJS
// Seajs build
factory(require('jquery'));
}
else {
// 浏览器全局模式
factory(jQuery);
}
})(function ($) {
(function(window, undefined){
/**
* slide切换组件
* @param {Object} config 配置参数(基本参数请参见 Slider.config,这里注释所列的参数说明为当前函数所扩展的参数 )
* @param {string} [config.easing] 进行动画效果时的缓动算法,默认为"swing"
* @param {string} [config.effect] 切换方式,默认无效果,"none"-无效果,"fade"-淡入淡出,"fadeIn"-仅淡入效果,scrollx"-垂直滚动,"scrolly"-水平滚动
* @param {boolean} [config.seamless] 是否为无缝结构(默认为:false)
* @param {boolean} [config.allowBlank] 非无缝结构下,是否允许最后一组的留白(默认为:false)
* @param {boolean} [config.doResize] 是否在窗口大小变更的情况下调整内容项的宽度(默认为:false,仅使用于scrollx效果,当幻灯片宽度与文档视图宽度一致时默认开启)
* @param {Function} callback 每次执行切换操作时所触发的回调函数
* @return {jQuery} 原jQuery对象
*/
$.fn.slide = function(config, callback)
{
config = $.extend({ easing: "swing", effect: "none" }, config);
return this.each(function(i, ele){
var oSlide = new Slider(ele, config, callback);
if(oSlide.valid) {
oSlide.cfg.effect.indexOf("scroll") >= 0 ? doSlide(oSlide) : doSwitch(oSlide);
}
// window.console && console.info(oSlide);
});
};
/**
* 处理$.fn.slide组件的滑动效果 ( scrollx scrolly )
* @param {Object} oSlide Slide对象的实例
* @return {undefined}
*/
function doSlide(oSlide)
{
// 获取实例对象的相关属性
var $lists = oSlide.lists,
$parent = oSlide.parent,
cansee = oSlide.cansee,
cfg = oSlide.cfg,
seamless = cfg.seamless,
scrollLen = cfg.scrollLen,
pages;
// 无缝结构时,slide组件的切换页数将会有所变更
pages = oSlide.pages = seamless ? Math.ceil($lists.length / scrollLen) : oSlide.pages;
// 计算所需的基本长度
var isScrollx = cfg.effect === "scrollx",
scrollBase = isScrollx ? $lists.outerWidth(true) : $lists.outerHeight(true), // 单个切换单元的宽度或高度
scrollValue = scrollBase * scrollLen, // 单次滚动所需移动的距离
scrollLeave = (cansee - scrollLen) * scrollBase, // 单次滚动后当前可视区域所剩下的距离
remainder = ($lists.length % scrollLen),
unitLeft = remainder === 0 ? 0 : scrollLen - remainder, // 最后一组切换单元还差几个列表项才构成完整的切换队列
unitLeftValue = scrollBase * unitLeft,
scrollMax = scrollValue * (pages - 1) - (cfg.allowBlank ? 0 : unitLeftValue), // 最后一页切换所构成的最大运动值
seamlessDistance = 0; // 无缝结构:附加的距离长度
// 重置切换所需的基本结构
(function(){
if(seamless){
var $clonePrefix, $cloneAfter, attr, amount = $lists.length;
if(unitLeft !== 0){
attr = isScrollx ? "marginRight" : "marginBottom";
$lists.last().css(attr, function(i, value){
return parseInt(value) + unitLeftValue;
});
}
$clonePrefix = $lists.slice(-cansee + unitLeft).clone(true);
$clonePrefix.attr("data-clone", function(i){ return amount - ($clonePrefix.length - i); });
$cloneAfter = $lists.slice(0, cansee).clone(true);
$cloneAfter.attr("data-clone", function(i){ return i; });
$lists = $parent.prepend($clonePrefix).append($cloneAfter).children();
seamlessDistance = scrollBase * cansee;
scrollMax = scrollValue * (pages - 1) + seamlessDistance;
}
$parent.css("position") === "static" && $parent.css("position", "relative");
if(isScrollx){
var listW = $lists.width();
$lists.css({"float": "left", "width": listW });
$parent.css("width", scrollBase * $lists.length + (seamless ? unitLeftValue * 2 : 0));
// 单屏显示的内容项在自适应宽度的情况下,当浏览器调整窗口尺寸时需要重新调整宽度
if(cansee === 1 && (cfg.doResize || listW === $(window).width())){
$(window).resize(function(){
scrollValue = scrollBase = $parent.parent().width();
scrollMax = scrollValue * (pages - 1);
if(seamless){
seamlessDistance = scrollBase * cansee;
scrollMax += seamlessDistance;
}
$lists.css("width", scrollBase);
$parent.css("width", scrollBase * $lists.length);
oSlide.process(oSlide.old, oSlide.old, "must"); // 对幻灯片的位置进行重置
});
}
}
})();
// 指定回调处理函数
oSlide.init(function(cur, old, action){
var scrollDistance, data,
duration = action === "must" ? 0 : cfg.duration;
// 针对无缝结构的特殊处理
if(seamless){
var direct = isScrollx ? "left" : "top";
if(cur === 0 && old === pages - 1 && action === "next"){
$parent.css(direct, -scrollLeave); //最后到第一个
}
else if(cur === pages - 1 && old === 0 && action === "prev"){
$parent.css(direct, -scrollMax - seamlessDistance + (cansee - scrollLen) * scrollBase); //第一个到最后
}
}
// 执行效果选择
scrollDistance = cur === pages - 1 ? scrollMax : scrollValue * cur + seamlessDistance;
data = isScrollx ? {left: -scrollDistance} : {top: -scrollDistance};
$parent.stop().animate(data, duration, cfg.easing, function(){ oSlide.done() });
});
}
/**
* 处理 $.fn.slide 组件的其它效果(fade fadeIn none)
* @param {Object} oSlide Slide对象的实例
* @return {undefined}
*/
function doSwitch(oSlide)
{
var $lists = oSlide.lists,
cfg = oSlide.cfg,
effect = cfg.effect,
easing = cfg.easing,
scrollLen = cfg.scrollLen,
done = function(){ oSlide.done() };
// 淡入淡出
if(effect === "fade"){
oSlide.parent.css("position") === "static" && oSlide.parent.css("position", "relative");
$lists.css("position") !== "absolute" && $lists.css({position: "absolute", left: 0, top: 0});
$lists.hide().eq(oSlide.cur).show();
oSlide.init(function(cur, old, action){
var duration = action === "must" ? 0 : cfg.duration;
$lists.eq(old).stop(true, true).css('z-index', 1).fadeOut(duration, easing, done);
$lists.eq(cur).css('z-index', 0).show();
});
}
// 淡入效果
else if(effect === "fadeIn"){
oSlide.init(function(cur, old, action){
$lists.hide().slice(scrollLen * cur, scrollLen * (cur + 1)).fadeIn(action === "must" ? 0 : cfg.duration, easing, done);
});
}
// 无效果切换
else{
cfg.duration = 0; // 重置duration配置为0
oSlide.init(function(cur){
$lists.hide().slice(scrollLen * cur, scrollLen * (cur + 1)).show();
done();
});
}
}
/**
* Slide切换处理
* @param {DOM|jQuery|string} ele 外围容器的对象表示(DOM对象、jQuery对象、jQuery选择器)
*
* @param {Object} config 配置参数(将布尔型参数以及部分特殊参数的原始值以undefined的形式存在)
*
* @param {jQuery} [config.lists] 轮播图列表所处容器的 selector,默认值:".j_slideLists"
* @param {jQuery} [config.tags] 分页标签所处容器的 selector,默认值:".j_slideTags"
* @param {jQuery} [config.posCur] 显示当前页码的容器的 selector,默认值:".j_slideCur"
* @param {jQuery} [config.posPages] 显示总页数的容器的 selector,默认值:".j_slidePages"
* @param {jQuery} [config.btnPrev] 上一页按钮的 selector,默认值:".j_slidePrev"
* @param {jQuery} [config.btnNext] 下一页按钮的 selector,默认值:".j_slideNext"
*
* @param {string} [config.trigger] 分页标签的触发事件,默认值:"mouseenter"(鼠标滑入切换)
* @param {string} [config.tagClass] 标签项选中时附加的类名状态,默认值:"current"
* @param {string} [config.btnClass] 按钮被禁用时附加的类名状态,默认值:"disabled"
* @param {number} [config.duration] 切换效果的持续时间,默认值:400(毫秒)
* @param {number} [config.interval] 自动切换时的时间间隔,默认值:5000(毫秒)
* @param {number} [config.hoverDelay] 鼠标悬浮切换时,分页标签的响应延迟时间,默认值:0(毫秒)
*
* @param {number} [config.cur] 默认显示的内容项的索引,默认值:0
* @param {number} [config.scrollLen] 每次切换将滚动的单位长度,默认值:0,表示可视区域的所有列(横向)或所有行(纵向)。
*
* @param {boolean} [config.keepTags] 是否保留分页标签的原始内容,默认值:false(分页标签项将由程序自动生成)
* @param {boolean} [config.btnDisable] 是否设置在首页时禁用上一页按钮切换,在尾页时禁用下一页按钮切换,默认值:false
* @param {boolean} [config.auto] 是否执行自动切换,默认值:false
* @param {boolean} [config.lazyload] 是否启用图片的懒加载功能,默认值:false
* @param {boolean} [config.beLock] 当处于轮播的运动状态时,是否将当前轮播置为锁定状态(按钮点击将不会触发切换,但分页标签可以),默认值:false
*
* @param {string} [config.imgAttr] 启用图片的懒加载时,用于表示真实url的自定义属性,默认值:"data-slide-img"
*
* @param {Function} callback 在每次执行切换操作之前所触发的回调函数——function(cur, old){},cur参数表示当前需要显示的索引项,old参数表示前一个显示的索引项,action表示执行的相关动作(must , prev , next),this指向对象本身
*
* @return {Object}
*/
function Slider(ele, config, callback)
{
if(typeof config === "function"){
callback = config;
config = undefined;
}
this.ele = $(ele);
this.cfg = $.extend({}, Slider.config, config);
typeof callback === "function" && (this.callback = callback);
this.valid = true;
this.predefine();
}
Slider.config = {
trigger: "mouseenter",
tagClass: "current",
btnClass: "disabled",
duration: 400,
interval: 5000,
cur: 0,
scrollLen: 0,
// hoverDelay: 0,
// keepTags: false,
// btnDisable: false,
// auto: false,
// lazyload: false,
// beLock: false,
// imgAttr: 'data-slide-img',
// lists: '.j_slideLists',
// tags: '.j_slideTags',
// posCur: '.j_slideCur',
// posPages: '.j_slidePages',
// btnPrev: '.j_slidePrev',
// btnNext: '.j_slideNext'
};
Slider.prototype = {
// 预定义
predefine: function()
{
var that = this,
cfg = that.cfg,
$parent = that.ele.find(cfg.lists || ".j_slideLists"),
$wrap = $parent.parent(),
$lists = $parent.children(),
amount = $lists.length,
scrollLen = cfg.scrollLen,
cansee, cols, rows, pages;
// 计算可视内容项的情况
cols = Math.round($wrap.width() / $lists.outerWidth(true)) || 1;
rows = Math.round($wrap.height() / $lists.outerHeight(true)) || 1;
cansee = cols * rows;
scrollLen = cfg.scrollLen ? scrollLen : cansee;
pages = Math.ceil((amount - (cansee - scrollLen)) / scrollLen);
// 只有当切换页数大于1才做切换处理
if(pages < 2 || amount <= cansee){
that.valid = false;
that.init();
return;
}
// 参数重置
if("ontouchstart" in window){
cfg.trigger = "click";
}
cfg.cur = cfg.cur >= pages ? pages - 1 : (cfg.cur || 0);
cfg.scrollLen = scrollLen;
// 附加公共成员
that.lists = $lists; // 内容列表
that.parent = $parent; // 内容列表的父级容器
that.pages = pages; // 可切换的总页数
that.cansee = cansee; // 可视切换内容的行数或列数
that.old = cfg.cur; // 保存当前已有显示内容的索引值
},
// 初始化
// 参数process:绑定负责Slide切换的处理函数——function(cur, old, action){},cur参数表示当前需要显示的索引项,old参数表示已有显示的索引项,action表示执行的相关动作(must , prev , next),this指向对象本身
init: function(process)
{
var that = this,
cfg = that.cfg,
$ele = that.ele,
$tags = $ele.find(cfg.tags || ".j_slideTags"),
$posCur = $ele.find(cfg.posCur || ".j_slideCur"),
$posPages = $ele.find(cfg.posPages || ".j_slidePages"),
$btnPrev = $ele.find(cfg.btnPrev || ".j_slidePrev"),
$btnNext = $ele.find(cfg.btnNext || ".j_slideNext"),
imgAttr;
if(that.valid){
if($tags.length){
that.tags = $tags;
that.bindTags();
}
if($btnPrev.length){
that.btnPrev = $btnPrev;
that.btnNext = $btnNext;
that.bindBtns();
}else{
cfg.btnDisable = false;
}
if($posCur.length){
that.posCur = $posCur;
$posPages.html(that.pages);
}
cfg.auto && that.bindAuto();
that.bindTouch();
that.process = typeof process === "function" ? process : $.noop;
that.show(cfg.cur, "must");
$tags.add($posCur.parent()).add($btnPrev.parent()).show();
}else{
$tags.add($posCur.parent()).add($btnPrev.parent()).remove();
if(cfg.lazyload) {
imgAttr = cfg.imgAttr || "data-slide-img";
that.doLoadImg($('[' + imgAttr + ']'), imgAttr);
}
}
},
// 重置标签项
rewriteTags: function()
{
var that = this,
cfg = that.cfg,
pages = that.pages,
$tags = that.tags;
!cfg.keepTags && (function(){
var str = "",
i = 0,
tagName = $tags[0].nodeName.toLowerCase() === "ul" ? "<li>" : "<span>",
tagNameEnd = tagName.replace("<", "</");
for(; i < pages; ){
str += tagName + (++i) + tagNameEnd;
}
$tags.html(str);
})();
that.tags = $tags.children().removeClass(cfg.tagClass).each(function(i, ele){
$(ele).data("i", i);
});
},
// 绑定标签操作
bindTags: function()
{
var that = this,
cfg = that.cfg,
trigger = cfg.trigger,
ismousehover = trigger === "mouseenter" || trigger === "mouseover",
stopDelay, $tags, func;
// 重置标签项
that.rewriteTags();
$tags = that.tags;
// 绑定标签项的事件处理
if(ismousehover && cfg.hoverDelay){
func = function(){
var $ele = $(this);
clearTimeout(stopDelay);
stopDelay = setTimeout(function(){
that.show($ele.data("i"));
}, cfg.hoverDelay);
};
$tags.on("mouseleave", function(){
clearTimeout(stopDelay);
});
}else{
func = function(){
that.show($(this).data("i"));
};
}
$tags.on(trigger, func);
// 单击事件触发的情况下,标签项的链接首次不会被访问
!ismousehover && $tags.has("a").on("click", "a", function(e){
if(!$(this).parent().hasClass(cfg.tagClass)){
e.preventDefault();
}
});
},
// 绑定按钮操作
bindBtns: function()
{
var that = this,
old = that.old,
cfg = that.cfg,
btnClass = cfg.btnClass,
btnDisable = cfg.btnDisable,
$btnPrev = that.btnPrev,
$btnNext = that.btnNext;
//绑定前后按钮的事件处理
$btnPrev.on("click", function(){
(!btnDisable || !$(this).hasClass(btnClass)) && that.prev();
return false;
});
$btnNext.on("click", function(){
(!btnDisable || !$(this).hasClass(btnClass)) && that.next();
return false;
});
if(btnDisable){
if(old === 0){
$btnPrev.addClass(btnClass);
}else if(old === that.pages - 1){
$btnNext.addClass(btnClass);
}
}
},
// 绑定自动切换处理
bindAuto: function()
{
var that = this;
// 自动切换时,鼠标悬浮在标题或者内容的上方时将暂停切换操作
// 当鼠标移出后,恢复自动切换
that.ele.on({
"mouseenter": function(){
that.pause();
},
"mouseleave": function(){
that.play();
}
});
},
// 切换至上一页
prev: function()
{
var cur = this.old - 1;
cur = cur < 0 ? this.pages - 1 : cur;
this.show(cur, "prev");
},
// 切换至下一页
next: function()
{
var cur = this.old + 1;
cur = cur >= this.pages ? 0 : cur;
this.show(cur, "next");
},
// 切换到指定位置
// cur:接下来需要显示的索引项
// action:相关动作的标识,"must"-强制执行, "prev"-上一页,"next"-下一页,undefined-标签项切换
show: function(cur, action)
{
var that = this,
old = that.old,
cfg = that.cfg,
btnClass = cfg.btnClass,
tagClass = cfg.tagClass;
if(old === cur && action !== "must" || that.isLocked) return;
if(cfg.beLock && action !== undefined){
that.isLocked = true;
}
if(cfg.btnDisable){
that.btnPrev.toggleClass(btnClass, cur === 0);
that.btnNext.toggleClass(btnClass, cur === that.pages - 1);
}
that.tags && that.tags.eq(old).removeClass(tagClass).end().eq(cur).addClass(tagClass);
that.posCur && that.posCur.html(cur + 1);
cfg.lazyload && that.loadImg(cur);
that.callback && that.callback.call(that, cur, old, action);
that.process.call(that, cur, old, action);
that.old = cur;
},
// 在每次切换完成后执行
done: function()
{
var that = this,
cfg = that.cfg;
if(cfg.beLock){
that.isLocked = false;
}
if(cfg.auto){
that.tStartAuto = $.now();
that.auto();
}
},
// 置为锁定状态,不响应其他切换事件的触发
lock: function()
{
this.isLocked = true;
},
// 解除锁定状态
unlock: function()
{
this.isLocked = false;
this.done();
},
// 暂停自动
pause: function()
{
this.isPaused = true;
},
// 恢复自动
play: function()
{
this.isPaused = false;
!this.isLocked && this.auto(true);
},
// 开启自动
auto: function(byPlay)
{
var that = this,
interval = that.cfg.interval;
clearTimeout(that.stopDoAuto);
if(!that.isPaused){
// 如果暂停自动切换持续时间较长,当恢复自动切换时,应减少延时触发的时间
if(byPlay && that.tStartAuto !== undefined){
interval = interval - ($.now() - that.tStartAuto);
}
that.stopDoAuto = setTimeout(function(){
!that.isPaused && that.next();
}, interval);
}
},
// 图片懒加载处理
loadImg: function(index)
{
var that = this, cfg = that.cfg;
if(that.hasClone === undefined){
that.hasClone = that.lists.first().prev().length > 0;
that.imgLoaded = " ";
}
if(that.imgLoaded.indexOf(" " + index + " ") >= 0) return;
that.imgLoaded += index + " ";
var attr = cfg.imgAttr || "data-slide-img",
selector = "[" + attr + "]",
start = index * cfg.scrollLen,
end = start + that.cansee,
$clone = that.hasClone ? that.parent.children(getCloneSelector(start, end)) : undefined,
$operator = that.lists.slice(start, end).add($clone).find(selector);
that.doLoadImg($operator, attr);
if($.trim(that.imgLoaded).split(" ").length === that.pages){
delete that.hasClone;
delete that.imgLoaded;
cfg.lazyload = false;
}
},
// 执行图片加载
doLoadImg: function($operator, attr)
{
$operator.each(function(index, ele){
var src = ele.getAttribute(attr);
ele.tagName.toLowerCase() === "img" ? ele.setAttribute("src", src) : ele.style.backgroundImage = "url(" + src + ")";
ele.removeAttribute(attr);
});
},
//绑定触屏事件
bindTouch: function()
{
if(!("ontouchstart" in window)) return;
var that = this,
$ele = that.ele,
ele = $ele[0],
width = Math.floor($ele.width() / 4),
moveX = 0,
startX;
width = width > 80 ? 80 : width;
ele.addEventListener("touchstart", function(e){
startX = e.pageX;
});
ele.addEventListener("touchmove", function(e){
moveX = e.pageX - startX;
(Math.abs(moveX) > 20) && e.preventDefault();
});
ele.addEventListener("touchend", function(e){
if(Math.abs(moveX) > width){
moveX > 0 ? that.prev() : that.next();
}
});
}
};
window.Slider = Slider;
/**
* 图片懒加载处理中用于处理获取当前切换项的拷贝元素
* @param {number} start 起始索引值
* @param {number} end 结束索引值
* @return {string} 用于查找拷贝元素所对应的选择器字符串
*/
function getCloneSelector(start, end)
{
var selector = '';
for(var i = start; i < end; i++){
selector += ',[data-clone="' + i + '"]';
}
return selector.substring(1);
}
})(window);
});