/*! * powerswitch.js by sucaiweb(.com) * under mit license * you can use powerswitch to switch anything */ (function(window, $, undefined) { var isanimation = typeof history.pushstate == "function"; // 一些方法 $.powerswitch = function(elements, options) { $(elements).powerswitch(options); }; $.extend($.powerswitch, { getrelative: function(trigger, params) { trigger = $(trigger); // 没有数据源,回家带孩子 if (trigger.length == 0) return $(); // 元素数组 var arrtarget = [], ismoretoone = false; trigger.each(function(index, element) { var selector = $(this).attr(params.attribute) || ($(this).attr("href") || "").split("#")[1]; if (selector && arrtarget[selector] != true) { var target = $(); if (/^\w+$/.test(selector)) { target = $("#" + selector); // 如果属性值作为id没有对应元素,就作为类名选择器使用 if (target.length === 0) { target = $("." + selector); } // 如果类名选择器也没有对应的元素,作为选择器使用 if (target.length === 0) { target = $(selector); } } else { // 纯选择器 target = $(selector); } target.each(function(index, element) { arrtarget.push(element); }); // 设置标志量,避免重复 arrtarget[selector] = true; } else if (arrtarget[selector] == true) { ismoretoone = true; } }); // 顺便判断下是否是多对一的关系 trigger.data("ismoretoone", ismoretoone); return $(arrtarget); }, transition: function(target, duration, isreset) { var transform = "transform " + duration + "ms linear"; if (isanimation == false) return; // css3 transition设置 if (isreset == true) { target.css("webkittransition", "none").css("transition", "none") .data("hastransition", false); } else if (!target.data("hastransition")) { target.css({ webkittransition: "-webkit-" + transform, webkitbackfacevisibility: "hidden", transition: transform, backfacevisibility: "hidden" }).data("hastransition", true); } }, translate: function(target, key, value) { // 偏移值设置 var valuetransform = "translate"+ key +"("+ value +")"; isanimation? target.css("webkittransform", valuetransform).css("transform", valuetransform): target.css(key == "x"? { left: value }: { top: value }); }, animation: function(targethide, targetshow, params) { var container = null, that = this, noanimate = params.animation == "none"; // 动画相关的几个小方法 var funtransform = function(target, key, value) { // 如果value是纯数值 if (parseint(value) === value) value += "px"; // ie10+等现代浏览器 if (isanimation) { // css3驱动动画 that.transition(target, params.duration, noanimate); // 动画触发等 that.translate(target, key, value); // console.log(value); } else { // ie6-ie9这些老弱病残 // left/top target[noanimate? "css": "animate"](key == "x"? { left: value }: { top: value }, params.duration); } }; // 因为是万能切换,显然情况就比较复杂 // 可以是列表元素动画,也可以是容器元素动画 // 容器元素动画又分为两种,scroll和transform(ie6-9 left/top代替),自动判断 // 列表元素动画也有几种,transform, fade, 和slide(toggle模式下专用) // 根据是否有target这个参数,决定是容器动画还是列表动画 // 为了智能,容器动画根据一定的机制自动判断动画类型,这在carousel模式下很有用 // 智能判断的条件是:params.animation == "auto" // 动画的终点值与动画类型相关 // 列表元素动画使用百分比,是定制无需关心 // 容器元素动画的最终位置通过"data-position"存储访问 if ((targetshow && targetshow.length) || (targethide && targethide.length)) { // 列表动画 // 一般用在选项卡,手风琴效果 // 有一些限制规则: // 1. 如果是多选模式,即一次可以有多个面板展开(手风琴效果),不支持transform移动动画 // 因此,此时,无动画显示 if (params.toggle == true && params.animation == "translate") { params.animation = "none"; } switch (params.animation) { case "translate": { // 移动 // 比较前后移动元素的索引大小确定前后位置, // 用来决定移动的方向 var indexhide = targethide.data("index"), indexshow = targetshow.data("index"); var objdirection = { "vertical": "y", "horizontal": "x" }; if (indexhide != undefined && indexshow != undefined) { // 确定动画起点或终点位置是正的100%还是负的100% var hundred = 100, isnext = true; // 共存在三种切换情况 // 1. 定时 // 2. 点击选项卡触发 // 3. 点击前进后退按钮触发 if (params.prevornext) { switch (params.prevornext.attr("data-type")) { case "prev": { isnext = false; break; } case "next": { isnext = true; break; } default: { // 这是点击选项卡 // 根据前后的位置确定方向 isnext = indexhide < indexshow; } } } hundred = (isnext * 2 - 1 ) * 100; // 清除可能的transition // 因为动画的需要元素要改一下起始位置 // 由于之前css3 transition的设置,这种位置变化会有动画效果,而我们需要的是瞬间移动(用户看不到的那种) that.transition(targetshow.show(), params.duration, true); // 要显示的元素乾坤大挪移到我们希望的位置 that.translate(targetshow, objdirection[params.direction], hundred + "%"); // 动画触发了,一个移走,一个移入 settimeout(function() { funtransform(targethide, objdirection[params.direction], -1 * hundred + "%"); funtransform(targetshow, objdirection[params.direction], "0%"); }, 17); // 清除触发源 params.prevornext = null; } else { // 索引缺失,直接显示隐藏 targethide.hide(); targetshow.show(); } break; } case "slide": { // 手风琴slideup/slidedown效果 if (params.duration != "sync") { if (targethide) targethide.slideup(params.duration); if (targetshow) targetshow.slidedown(params.duration); } else { if (targethide) { targethide.slideup("normal", function() { if (targetshow) targetshow.slidedown(); }); } else if (targetshow) { targetshow.slidedown(); } } break; } case "fade": { // 淡入淡出效果 if (params.duration != "sync") { if (targethide) targethide.fadeout(params.duration); if (targetshow) targetshow.fadein(params.duration); } else { if (targethide) { targethide.fadeout("normal", function() { if (targetshow) targetshow.fadein(); }); } else if (targetshow) { targetshow.fadein(); } } break; } case "visibility": { // visibility隐藏与显示 if (targethide) targethide.css("visibility", "hidden"); if (targetshow) targetshow.css("visibility", "visible"); break; } default: { // "auto", "none" 或其他乱七八糟类型直接display显隐 if (targethide) targethide.hide(); if (targetshow) targetshow.show(); } } } else if (params.container && params.container.length) { var position = params.container.data("position"); container = params.container.get(0); // 容器动画 // 各种模式都可能出现 // 以下为各种动画类型的条件处理 if (params.direction == "vertical") { // 根据容器是否存在滚动高度 if (container.scrollheight - container.clientheight >= math.max(position.top, 1)) { // scroll模式 params.animation == "auto"? params.container.animate({scrolltop: position.top}): params.container.scrolltop(position.top); } else { // transform模式 funtransform(params.container, "y", -1 * position.top) } } else { // 水平方向 if (container.scrollwidth - container.clientwidth >= math.max(position.left, 1)) { // scroll模式 params.animation == "auto"? params.container.animate({"scrollleft": position.left}): params.container.scrollleft(position.left); } else { // transform模式 funtransform(params.container, "x", -1 * position.left) } } } } }); $.fn.powerswitch = function(options) { // 默认参数 var defaults = { direction: "horizontal", eventtype: "click", // 其他可选参数:hover classadd: "", // 如果没有样式变化,请使用任意类名代替 classremove: "", classprefix: "", // eg. "prefix" → prefix_disabled, prefix_prev, prefix_play, prefix_pause, prefix_next attribute: "data-rel", animation: "auto", // 其他可选值:"none|display", "visibility", "translate", "fade", "slide" duration: 250, // 动画持续时间,单位毫秒, 如果使用"sync"则表示同步 container: null, autotime: 0, // 自动播放时间 number: "auto", // 每次切换的数目 hoverdelay: 200, toggle: false, onswitch: $.noop }; // 最终参数 var params = $.extend({}, defaults, options || {}); // 一些全局类名 $.each(["disabled", "prev", "play", "pause", "next"], function(index, key) { key = $.trim(key); var upperkey = key.slice(0, 1).touppercase() + key.slice(1), paramskey = "class" + upperkey, lastchar = params.classprefix.slice(-1); if (params[paramskey] === undefined) { if (params.classprefix) { // 根据classprefix中是否含关键字符(下划线或短横线)做判断 if (/\-/g.test(params.classprefix)) { params[paramskey] = lastchar == "-"? (params.classprefix + key): [params.classprefix, key].join("-"); } else if (/_/g.test(params.classprefix)) { params[paramskey] = lastchar == "_"? (params.classprefix + key): [params.classprefix, key].join("_"); } else { // 驼峰-大小写组合 params[paramskey] = params.classprefix + upperkey; } } else { params[paramskey] = key; } } }); // 一些全局变量 some global variables // 选中的触发项 the selected item var indexselected = params.indexselected || -1, numswitch = parseint(params.number) || 1, // hover延时处理的计时器 the timer for hover delay hovertimer = null, // 自动播放的定时器 autoplaytimer = null, // 切换主体元素们 elerelatives = $(), // 主体元素的长度 lenrelatives = 0; // 据说搞个变量速度更快~ var self = $(this); // 无触发源,两种可能性 // 1. 选择器挂掉了 // 2. 单纯的自动播放,例如滚动新闻功能 if (self.length == 0) { // 如果是情况1,直接回家 if (params.container == null || params.autotime == 0) return self; } elerelatives = $.powerswitch.getrelative(self, params); if ((lenrelatives = elerelatives.length) == 0) return self; // 确定indexselected // 只有当未设定,或者不是toggle模式的时候 if (indexselected == -1 && params.toggle == false) { if (params.classadd) { // 通过添加类名确定 self.each(function(index, element) { if (indexselected != -1) return; if ($(element).hasclass(params.classadd)) indexselected = index; }); } else { // 通过关联面板的显隐确定 elerelatives.each(function(index, element) { if (indexselected != -1) return; if (params.animation == "visibility") { if ($(element).css("visibility") != "hidden") indexselected = index; } else if ($(element).css("display") != "none") { indexselected = index; } }); } } var ismoretoone = false, eleprev = $(), elenext = $(), eleprevornext = $(); var funstateprevnext = function(indexwill) { // 后退按钮的状态 if (indexwill <= 0) { eleprev.addclass(params.classdisabled).removeattr("title").attr("disabled", "disabled"); } else { eleprev.removeclass(params.classdisabled).attr("title", eleprev.data("title")).removeattr("disabled"); } // 前进按钮的状态 // 规则如下: // (总条目 - indexselected位置值) / 每次切换的条数 是否大于 1 if ((lenrelatives - indexwill) / numswitch > 1) { elenext.removeclass(params.classdisabled).attr("title", elenext.data("title")).removeattr("disabled"); } else { elenext.addclass(params.classdisabled).removeattr("title").attr("disabled", "disabled"); } } // 判断是否是多对一的关系 if (self.eq(0).data("ismoretoone") == true) { ismoretoone = true; // 如果不是无限滚动 if (params.classdisabled) { eleprev = self.eq(0), elenext = self.eq(1); eleprev.data("title", eleprev.attr("title")); elenext.data("title", elenext.attr("title")); // 初始按钮的状态 funstateprevnext(indexselected); // 滚动位置 if (indexselected <= 0 && params.container) { $(params.container).scrollleft(0).scrolltop(0); } } else if (params.container) { // 无限滚动 // 克隆并载入 elerelatives.clone().insertafter(elerelatives.eq(lenrelatives - 1)); // 重新确定关联元素们 elerelatives = $.powerswitch.getrelative(self, params); // more → one下之前点击的按钮 // 用来确定自动播放(如果有)的方向 // 默认是next方向 eleprevornext = self.eq(1); } else { // 伪多对1,动画只能是fade或普通显隐 eleprev = self.eq(0), elenext = self.eq(1); eleprevornext = elenext; } } // 判断是否1对多 var isonetomore = false; if (self.length == 1 && lenrelatives > 1) { isonetomore = true; } // 切换的核心,所有的切换都要走这一步 // 面向切换面板元素设计的切换方法 var funswitchable = function(indexwill) { // 总的切换项目数,每次切换的项目数 var elewillrelative = elerelatives.slice(indexwill, indexwill + numswitch); var eleselected = null, elewillselect = null, elerelative = null; // 如果是toggle切换 if (params.toggle == false) { // 在多对1模式下,我们关心的是触发按钮的临界状态 (disabled)等 // 而不是选中与不选中的样式切换状态 if (ismoretoone == true) { // 偏移元素就是 elewillrelative if (params.container) { // 获取相对父元素的偏移 var position = elewillrelative.position(); // 定位 params.container = $(params.container); // 位置存储(动画终点位置) params.container.data("position", position); // 容器动画 $.powerswitch.animation(null, null, params); // 按钮状态 params.classdisabled && funstateprevnext(indexwill); } else { // 容器动画 $.powerswitch.animation(elerelatives.eq(indexselected, indexselected + numswitch), elewillrelative, params); } // 回调 params.onswitch.call(this, elewillrelative); } else if (isonetomore == true) { // 1对多模式 // 也存在按钮的临界状态 // 只能显示,不能收起 // 对应元素的显隐控制 $.powerswitch.animation(null, elewillrelative, params); // 回调 params.onswitch.call(this, elewillrelative); } else { // 1 vs 1 或者 1 vs many情况下 // 关心按钮选中与不选中的样子 // console.log([indexwill, indexselected].join()); elewillselect = self.eq(indexwill); if (indexselected >= 0) { eleselected = self.eq(indexselected); elerelative = elerelatives.eq(indexselected, indexselected + numswitch); } else { eleselected = $(); elerelative = $(); } // 触发元素的类名状态改变 elewillselect.addclass(params.classadd).removeclass(params.classremove); // 已选元素的改变 if (indexselected !== indexwill) eleselected.addclass(params.classremove).removeclass(params.classadd); // 对应元素的显隐控制 $.powerswitch.animation(elerelative, elewillrelative, params); // 回调 params.onswitch.call(this, elewillrelative, eleselected, elerelative); } indexselected = indexwill; } else { // 如果多选 // 如果只能展开 // 能伸能屈 if ((params.animation == "visibility" && elewillrelative.css("visibility") == "hidden") || (params.animation != "visibility" && elewillrelative.css("display") == "none")) { // 显示 $.powerswitch.animation(null, elewillrelative, params); display = true; } else { $.powerswitch.animation(elewillrelative, null, params); display = false; } // 回调 params.onswitch.call(this, elewillrelative, display); } }; // 遍历 loop var anchorsplit = location.href.split("#")[1]; self.each(function(index, element) { // 存储索引 // 存储title以及index $(element).data("index", index); if (ismoretoone == true) { $(element).bind("click", function() { var indexwill, elewill; if (params.classdisabled) { if ($(this).attr("disabled")) return false; if (index == 0) { indexwill = indexselected - numswitch; indexwill = math.max(0, indexwill); } else if (index == 1) { indexwill = indexselected + numswitch; indexwill = math.min(indexwill, lenrelatives - 1); } funswitchable.call(this, indexwill); } else if (params.container && lenrelatives > numswitch) { // 无限滚动 if (index == 0) { indexwill = indexselected - numswitch; if (indexwill < 0) { // 瞬间无感重定位 elewill = elerelatives.eq(indexselected + lenrelatives); $(params.container).data("position", elewill.position()); $.powerswitch.animation(null, null, $.extend({}, params, { animation: "none" })); indexwill = indexselected + lenrelatives - numswitch; } } else if (index == 1) { indexwill = indexselected + numswitch; if (indexwill > lenrelatives * 2 - numswitch) { // 末位数量不够了 elewill = elerelatives.eq(indexselected - lenrelatives); $(params.container).data("position", elewill.position()); $.powerswitch.animation(null, null, $.extend({}, params, { animation: "none" })); // 新的索引位置 indexwill = indexselected - lenrelatives + numswitch; } } funswitchable.call(this, indexwill); eleprevornext = $(this); } else { index? funplaynext(this): funplayprev(this); eleprevornext = $(this); } return false; }); } else if (isonetomore == true) { $(element).bind("click", function() { var indexwill; if (params.number == "auto") { numswitch = lenrelatives; } if (!$(this).attr("disabled")) { if (indexselected == -1) { indexwill = 0; } else { indexwill = indexselected + numswitch; } funswitchable.call(this, indexwill); if (indexwill >= lenrelatives - 1) { $(this).addclass(params.classdisabled).attr("disabled", "disabled").removeattr("title"); } } return false; }); } else if (params.eventtype == "click") { $(element).bind("click", function() { // 设置标志量,根据位置判断方向 params.prevornext = $(this); // 点击事件 click events funswitchable.call(this, index); return false; }); if (anchorsplit && element.href && anchorsplit == element.href.split("#")[1]) { $(element).trigger("click"); } } else if (/^hover|mouseover$/.test(params.eventtype)) { $(element).hover(function() { params.prevornext = $(this); // 鼠标经过 hover events cleartimeout(hovertimer); hovertimer = settimeout(function() { funswitchable.call(element, index); }, parseint(params.hoverdelay) || 0); }, function() { // 鼠标移开 cleartimeout(hovertimer); }); } }); elerelatives.each(function(index, element) { $(element).data("index", index); }); // 自动播放 var funplaynext = function(trigger) { var indexwill = indexselected + 1; if (indexwill >= lenrelatives) { indexwill = 0; } funswitchable.call(trigger || self.get(indexwill), indexwill); }, funplayprev = function(trigger) { var indexwill = indexselected - 1; if (indexwill < 0) { indexwill = lenrelatives -1; } funswitchable.call(trigger || self.get(indexwill), indexwill); }, funplayprevornext = function() { eleprevornext.trigger("click"); }, funautoplay = function() { cleartimeout(autoplaytimer); if (funautoplay.flagautoplay == true) { autoplaytimer = settimeout(function() { ismoretoone == false? funplaynext(): funplayprevornext(); funautoplay(); }, params.autotime); } }; // 单对单模式,或者无限切换的多对一模式支持自动播放 if ((isonetomore == false && params.toggle == false && ismoretoone == false) || (ismoretoone == true && !params.classdisabled)) { // 创建前进、后退、以及暂停按钮 if (params.container && ismoretoone == false) { var htmltempoperate = ''; self.length && $.each(["prev", "pause", "next"], function(index, key) { if (params.autotime == 0 && key == "pause") return; // 自动播放模式时候需要 htmltempoperate = htmltempoperate + ''; }); params.container.append(htmltempoperate).delegate("a", "click", function() { var type = $(this).attr("data-type"), classtype = params["class" + type.slice(0, 1).touppercase() + type.slice(1)], indexwill = indexselected; switch (type) { case "prev": { params.prevornext = $(this); funplayprev(); break } case "play": { funautoplay.flagautoplay = true; $(this).attr("data-type", "pause").removeclass(classtype).addclass(params.classpause); funautoplay(); break } case "pause": { funautoplay.flagautoplay = false; $(this).attr("data-type", "play").removeclass(classtype).addclass(params.classplay); funautoplay(); break } case "next": { params.prevornext = $(this); funplaynext(); break } } return false; }); } if (params.autotime) { // 定时播放相关事件绑定 // 自定义按钮容器,选项卡,以及切换面板鼠标经过停止自动播放 // 如果容器存在,且是包含关系 // 只要绑定容器就可以 var arrhoverplay = [self, elerelatives, params.container]; if (ismoretoone == true || (document.body.contains && params.container && params.container.get(0).contains(elerelatives.get(0)))) { arrhoverplay = [self, params.container]; } $.each(arrhoverplay, function(index, hovertarget) { if (hovertarget) hovertarget.hover(function(event) { if (event.pagex !== undefined || params.eventtype == "click") cleartimeout(autoplaytimer); }, function(event) { if (event.pagex !== undefined || params.eventtype == "click") funautoplay(); }); }); funautoplay.flagautoplay = true; funautoplay(); } } return self; }; })(window, jquery);