(function (win, doc) { // ### 1、通用方法 var is = { str: function (a) { return typeof a === 'string'; }, und: function (a) { return a === void 0; }, bol: function (a) { return typeof a === 'boolean'; }, true: function (a) { return a === true; }, num: function (a) { return typeof a === 'number'; }, null: function (a) { return a === null; }, fun: function (a) { return typeof a === 'function'; }, arr: function (a) { return array.isarray(a); }, obj: function (a) { return (a !== null && typeof a === 'object' && 'constructor' in a && a.constructor === object); }, emptyobj: function (a) { return this.obj(a) && a.length === 0; }, emptyarr: function (a) { return this.arr(a) && a.length === 0; }, } // 生成唯一标识符 function uuid(len, radix) { var chars = '0123456789abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz'.split(''); var uuid = [], i; radix = radix || chars.length; if (len) { for (i = 0; i < len; i++) uuid[i] = chars[0 | math.random() * radix]; } else { var r; uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-'; uuid[14] = '4'; for (i = 0; i < 36; i++) { if (!uuid[i]) { r = 0 | math.random() * 16; uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r]; } } } return uuid.join(''); } // 判断数据类型 function typeof(d) { return object.prototype.tostring.call(d).slice(8, -1); } // 转成数组 function toarray(el) { return [].slice.call(el); } // 获取htmlelement元素的类名选择器 function getclassselector(el) { var arr = this.toarray(el.classlist); return is.emptyarr(arr) ? '' : '.' + arr.join('.'); } // 向上遍历(查找指定父级)closest function closest(ele, tar) { var _this = this; if (!element.prototype.isprototypeof(ele)) { throw new typeerror(ele + 'is not a element!'); } var elarr = (function () { if (tar instanceof htmlelement) return [tar]; try { tar = doc.queryselectorall(tar); } catch (err) {} var tartype = _this.typeof(tar), tartypeoptions = [ 'nodelist', 'htmlcollection', 'array', ]; if (tartypeoptions.indexof(tartype) > -1) return _this.toarray(tar); })(); do { if (elarr.indexof(ele) > -1) return ele; ele = ele.parentelement; } while (ele !== null); return null; } // 扁平化数组:即array.prototype.flat function flat(arr, d) { var _this = this; d = d || 1; return d > 0 ? arr.reduce(function (prev, now) { return prev.concat(array.isarray(now) ? _this.flat(now, d - 1) : now); }, []) : arr.slice(); } // 对数值数组:即object.values function values(obj) { if (obj !== object(obj)) throw new typeerror(obj + 'is a non-object'); return object.keys(obj).map(function (e) { return obj[e]; }); } // transitionend兼容支持 function gettransitionend() { var el = doc.createelement('div'); var transitions = { 'transition': 'transitionend', 'webkittransition': 'webkittransitionend', 'otransition': 'otransitionend', 'moztransition': 'transitionend', } for (var attr in transitions) { if (!is.und(el.style[attr])) return transitions[attr]; } } // 即array.prototype.find function find(arr, callback) { for (var i = 0, len = arr.length; i < len; i++) { var curitem = arr[i]; if (callback(curitem, i, arr)) return curitem; } } function getpagevisibility() { var hiddenarr = [ 'hidden', 'webkithidden', 'mozhidden', ], visibilitystatearr = [ 'visibilitystate', 'webkitvisibilitystate', 'mozvisibilitystate', ]; var values = [hiddenarr, visibilitystatearr].map(function (e) { return find(e, function (e) { return e in doc; }); }); return { hidden: values[0], visibilitystate: values[1], } } // 获取元素的计算属性,参数:el元素、css属性、pseudoel伪元素 function computedcss(el, css, pseudoel) { pseudoel = pseudoel || null; return win.getcomputedstyle(el, pseudoel)[css]; } // 获取元素translate值 function gettranslate(el) { if (!el instanceof element) { throw new typeerror(el + 'is not a element'); } var transformarr = this.computedcss(el, 'transform').replace(/\(|\)/g, '').split(','), isthreed = transformarr[0].indexof('3d') > -1, translatearr = isthreed ? transformarr.slice(12, 15) : transformarr.slice(4); var result = { x: 0, y: 0, z: 0, }; object.keys(result).foreach(function (key, i) { // 对于transform为none未设置和translatey没有的情况,自动补充为0 result[key] = parsefloat(translatearr[i]) || 0; }); return result; } function extend(tarobj, initobj) { is.und(tarobj) && (tarobj = {}); is.und(initobj) && (initobj = {}); var _this = this; object.keys(initobj).foreach(function (key) { if (is.und(tarobj[key])) { tarobj[key] = initobj[key]; } else if (is.obj(initobj[key]) && is.obj(tarobj[key]) && object.keys(initobj[key]).length > 0) { _this.extend(tarobj[key], initobj[key]); } }); } var _ = { uuid: uuid, flat: flat, find: find, values: values, typeof: typeof, extend: extend, closest: closest, toarray: toarray, computedcss: computedcss, gettranslate: gettranslate, getclassselector: getclassselector, pagevisibility: getpagevisibility(), transitionend: gettransitionend(), } // ### 2、参数中css样式对象的解析方法和类 // 2.1 处理css样式对象工具类 var cssutils = { // 将小驼峰形式转成-连接符形式 toconnectorform: function (prop) { var rgep = /[a-z]/g, res = null, matchedchar = '', replacedchar = function () { return '-' + matchedchar.tolowercase(); }; while ((res = rgep.exec(prop)) !== null) { matchedchar = res[0]; prop = prop.replace(matchedchar, replacedchar()); }; return prop; }, // 将css对象的所有属性转成-连接符形式 normalizecssobj: function (cssobj) { var _this = this; object.keys(cssobj).foreach(function (key) { var newkey = _this.toconnectorform(key); if (newkey !== key) { cssobj[newkey] = cssobj[key]; delete cssobj[key]; } }); }, // 删除css对象的无用属性 removeuselesskey: function (cssobj) { object.keys(cssobj).foreach(function (key) { is.und(cssobj[key]) && (delete cssobj[key]); }); }, // 页面中添加css样式新规则,参数:[csstext,csstext,...]数组 insertcsstext: function (csstextarr) { var getstylesheets = function (sheets) { return [].slice.call(sheets).filter(function (e) { return !e.disabled === true && e.ownernode.tagname.tolowercase() === 'style'; }); }, sheets = getstylesheets(doc.stylesheets), lastsheet = sheets[sheets.length - 1]; if (is.und(lastsheet)) { var s = doc.createelement('style'); doc.head.appendchild(s); sheets = getstylesheets(doc.stylesheets); lastsheet = sheets[sheets.length - 1]; } csstextarr.foreach(function (e) { try { lastsheet.insertrule(e, lastsheet.cssrules.length); } catch (err) { console.log(err) } }); }, // 获取csstext样式规则 csstext: function (cssobj, key) { var value = object.keys(cssobj).map(function (key) { return key + ':' + cssobj[key] + '!important;'; }).join(''); return key + '{' + value + '}'; }, } // 2.2 cssparser解析类 function cssparser(cssparams) { var _this = this; this.cssparams = cssparams; this.cssparamstype = _.typeof(this.cssparams); // 将要执行的cssparams函数形式的属性或其本身 this.willexecssfn = this.cssparamstype === 'function' && this.cssparams; // 获取计算后的css对象 this.computedcssobj = (function () { var typeoptions = ['function', 'object'], computedmethodfn = [_this.execssfnself, _this.generatecssobj], typeindex = typeoptions.indexof(_this.cssparamstype); return function (coord, abscoord, index) { return computedmethodfn[typeindex].apply(_this, arguments); } })(); this.init(); } // 创建cssparser原型对象,并将自定义的css工具方法添加至其原型 var cssparserproto = object.create(cssutils); // 初始化 cssparserproto.init = function () { if (is.obj(this.cssparamstype)) { this.initcssparams(); } }; // 执行css对象中的函数或其本身 cssparserproto.execssfnself = function (coord, abscoord, index) { var cssobj = this.willexecssfn(coord, abscoord, index); this.normalizecssobj(cssobj); // 再次标准化该css对象 return cssobj; }; // 初始化cssparams对象 cssparserproto.initcssparams = function () { var type = this.cssparamstype, cssparams = this.cssparams; // 移除cssparams对象的无用属性 this.removeuselesskey(cssparams); // 将cssparams对象属性名转成'-'连接符形式 this.normalizecssobj(cssparams); }; // 生成计算后的css对象 cssparserproto.generatecssobj = function (coord, abscoord, index) { var _this = this, cssobj = {}, cssparams = this.cssparams; object.keys(cssparams).foreach(function (key) { var item = cssparams[key]; // 如果是函数,就获取其返回结果 if (is.fun(item)) { _this.willexecssfn = item; cssobj[key] = _this.willexecssfn(coord, abscoord, index); return; } cssobj[key] = item; }); return cssobj; }; // 将cssparserproto绑定为cssparser的原型 cssparser.prototype = cssparserproto; // ### 3、轮播图主体代码部分 // 初始化每张卡片要填入的css stylesheet的样式规则 function initstylerules() { var empile = this, params = empile.params, slides = empile.slides, slideslen = slides.length, mediant = slides.mediant, dataattrarr = slides.dataattrarr, uidkey = slides.dataslideid; // 卡片的'data-slide-id'属性名 // 新建一个css规则解析类 var cssparser = new cssparser(params.css), csstextarr = []; // 插入需要计算的cssparams样式 for (var index = 0; index < slideslen; index++) { var coord = index - mediant, // 坐标系索引 abscoord = math.abs(coord); // 坐标系索引绝对值 var uidval = dataattrarr[index].dataslideid, // 卡片'data-slide-id'属性值 selector = '[' + uidkey + '="' + uidval + '"]'; // css规则选择器 var cssobj = cssparser.computedcssobj(coord, abscoord, index), // 获取计算后的cssparams csstext = cssparser.csstext(cssobj, selector); // 生成csstext内容 csstextarr.push(csstext); } cssparser.insertcsstext(csstextarr); // 统一将csstext插入stylesheets中 } // 初始化slides配置信息 function initslidesinfo() { var empile = this, wrapper = empile.wrapper, isclickslide = empile.params.isclickslide, list = _.toarray(wrapper.children), length = list.length, // 如11和12张卡片,中间卡片的左边均显示5张,即mediant中间索引为5 mediant = math.floor((length - 1) / 2), dataslideid = 'data-slide-id', dataslideindex = 'data-slide-index', clickable = isclickslide, dataattrarr = list.map(function (item, index) { return { dataslideid: _.uuid(14, 16), dataslideindex: index, } }); return { // 继续前面的例子。如果要让第1张在中间显示,即它在新数组中的索引时5 list: list.splice(length - mediant).concat(list), length: length, mediant: mediant, dataslideid: dataslideid, dataslideindex: dataslideindex, clickable: clickable, dataattrarr: dataattrarr, } } // slideto function slidetoprev() { var empile = this, dataattrarr = empile.slides.dataattrarr; dataattrarr.push(dataattrarr.shift()); empile.updateslidedataattr(); } function slidetonext() { var empile = this, dataattrarr = empile.slides.dataattrarr; dataattrarr.unshift(dataattrarr.pop()); empile.updateslidedataattr(); } function slidetoslide(target) { var empile = this, slides = empile.slides, mediant = slides.mediant, dataattrarr = slides.dataattrarr; var index = +target.getattribute(slides.dataslideindex), diffi = mediant - index; slides.dataattrarr = dataattrarr.splice(diffi).concat(dataattrarr); empile.updateslidedataattr(); } var slideto = { slidetoprev: slidetoprev, slidetonext: slidetonext, slidetoslide: slidetoslide, } function ontransitionend() { var empile = this, waitfortransition = empile.params.waitfortransition, firstslideslist = empile.slides.list[0]; var transitionend = _.transitionend; firstslideslist.addeventlistener(transitionend, function () { waitfortransition && (empile.allowtransition = true); empile.autoplay.init(); }); } var fade = { ontransitionend: ontransitionend, } // update function updateslidedataattr() { var empile = this, slides = empile.slides, dataattrarr = slides.dataattrarr; slides.list.foreach(function (el, index) { el.setattribute(slides.dataslideid, dataattrarr[index].dataslideid); el.setattribute(slides.dataslideindex, dataattrarr[index].dataslideindex); }); } var update = { updateslidedataattr: updateslidedataattr, } var prototypes = { slideto: slideto, update: update, fade: fade, } // autoplay var autoplay = { init: function () { var empile = this, autoplay = empile.params.autoplay, delay = 0; // 过滤筛选autoplay if (is.bol(autoplay)) { is.true(autoplay) && (delay = 4000); } else if (is.obj(autoplay)) { if (is.und(autoplay.delay)) throw new error('autoplay.delay is not defined!') autoplay.delay = delay = parsefloat(autoplay.delay); } empile.params.autoplay = { delay: delay } // 如果delay时间是有效的,就准许自动轮播 if (is.num(delay) && delay !== 0) { settimeout(function () { empile.autoplay.onvisibilitychange(); }, 0); return function () { empile.autoplay.run(); } } return function () {} }, run: function () { var empile = this, delay = empile.params.autoplay.delay; empile.autoplay.stop(); empile.autoplay.timer = setinterval(function () { empile.slidetonext(); }, delay); }, stop: function () { var empile = this, delay = empile.params.autoplay.delay; if (delay) { return function () { clearinterval(empile.autoplay.timer); empile.autoplay.timer = void 0; } } return function () {}; }, onvisibilitychange: function () { var empile = this, autoplay = empile.params.autoplay, dochiddenoff = autoplay.dochiddenoff, pagehiddenattr = _.pagevisibility.hidden, delay = 0; // 过滤筛选dochiddenoff if (is.bol(dochiddenoff)) { if (is.true(dochiddenoff)) delay = 2000; else return; } else if (is.obj(dochiddenoff)) { if (is.und(dochiddenoff.delay)) throw new error('dochiddenoff.delay is not defined!'); dochiddenoff.delay = delay = parsefloat(dochiddenoff.delay); } autoplay.dochiddenoff = { delay: delay } return function () { var autoplay = empile.autoplay, visiabletimer = null, ispagehidden = false, hiddenfn = delay === 0 ? function () { autoplay.stop() } : function () { ispagehidden = true; visiabletimer = settimeout(function () { autoplay.stop(); }, delay); }, visiablefn = delay === 0 ? function () { cleartimeout(visiabletimer); autoplay.init(); } : function () { if (ispagehidden) { ispagehidden = false; cleartimeout(visiabletimer); autoplay.init(); } }; doc.addeventlistener('visibilitychange', function () { var pagehidden = doc[pagehiddenattr]; if (pagehidden) hiddenfn(); else visiablefn(); }); } }, } // click var click = { init: function () { var empile = this; empile.click.getcanclickeles(); if (is.emptyarr(empile.click.canclickeles)) return; empile.click.run(); }, run: function () { var empile = this, wrapper = empile.wrapper, canclickeles = empile.click.canclickeles, slides = empile.slides, mediant = slides.mediant, dataslideindex = slides.dataslideindex, params = empile.params, waitfortransition = params.waitfortransition, navigation = params.navigation, transitionduration = parsefloat(_.computedcss(slides.list[0], 'transition-duration')); // 过渡时长 wrapper.parentelement.addeventlistener('click', function (ev) { var willtar = _.find(canclickeles, function (e) { return e === _.closest(ev.target, e); }); // 如果点击的不是目标元素或者目标元素是中间那张卡片,就不执行切换效果。 if (is.und(willtar) || willtar.getattribute(dataslideindex) == mediant) return; // 如果需要等待过渡完成并且过渡时间不为0 if (waitfortransition && !!transitionduration) { if (!empile.allowtransition) return empile.allowtransition = false; } empile.autoplay.stop(); if (willtar === navigation.prevel) { empile.slidetoprev(); } else if (willtar === navigation.nextel) { empile.slidetonext(); } else { empile.slidetoslide(willtar); } }); }, getcanclickeles: function () { var empile = this, navigation = empile.params.navigation, slides = empile.slides, canclickeles = empile.click.canclickeles; object.keys(navigation).foreach(function (e) { !is.null(navigation[e]) && canclickeles.push(navigation[e]); }); is.true(slides.clickable) && canclickeles.push(slides.list); empile.click.canclickeles = _.flat(canclickeles, infinity); }, } var empile = function (wrapper, params) { this.wrapper = wrapper; var defaultparams = { isclickslide: false, waitfortransition: false, autoplay: false, navigation: { nextel: null, prevel: null, }, css: {}, } _.extend(params, defaultparams); // 存储修正后的params this.params = params; if(this.params.waitfortransition) this.allowtransition = true; _.extend(this, { slides: initslidesinfo.call(this), // 存储slide卡片信息 autoplay: { onvisibilitychange: autoplay.onvisibilitychange.call(this), init: autoplay.init.call(this), run: autoplay.run.bind(this), stop: autoplay.stop.call(this), }, click: { canclickeles: [], init: click.init.bind(this), run: click.run.bind(this), getcanclickeles: click.getcanclickeles.bind(this), }, }); object.keys(prototypes).foreach(function (group) { object.keys(prototypes[group]).foreach(function (method) { !empile.prototype[method] && (empile.prototype[method] = prototypes[group][method]); }); }); this.init(); } empile.prototype.init = function () { var empile = this; initstylerules.call(empile); empile.updateslidedataattr(); // 给卡片设置所需的data-属性 empile.autoplay.init(); // 初始化定时器 empile.click.init(); // 初始化点击事件 empile.ontransitionend(); // 初始化transitionend监听 } win.cssparser = cssparser; win.empile = empile; })(window, document);