jsLibrary.js
以前看犀牛书收藏和组合别人的库。
; (function () { 'use strict'; if (!Date.now) Date.now = function () { return new Date().getTime(); }; var vendors = ['webkit', 'moz']; for (var i = 0; i < vendors.length && !window.requestAnimationFrame; ++i) { var vp = vendors[i]; window.requestAnimationFrame = window[vp + 'RequestAnimationFrame']; window.cancelAnimationFrame = (window[vp + 'CancelAnimationFrame'] || window[vp + 'CancelRequestAnimationFrame']); } if (/iP(ad|hone|od).*OS 6/.test(window.navigator.userAgent) // iOS6 is buggy || !window.requestAnimationFrame || !window.cancelAnimationFrame) { var lastTime = 0; window.requestAnimationFrame = function (callback) { var now = Date.now(); var nextTime = Math.max(lastTime + 16, now); return setTimeout(function () { callback(lastTime = nextTime); }, nextTime - now); }; window.cancelAnimationFrame = clearTimeout; } }()); ; (function(win, doc) { function JSLibrary(arg) { //用来保存选中的元素, elements是一个真正的数组, 不是HTMLCollection对象 this.elements = []; switch (typeof arg) { case 'function': _methodSets.ready(arg); break; case 'string': switch (arg.charAt(0)) { case '#': //ID var obj = doc.querySelector(arg); this.elements.push(obj); break; case '.': //class this.elements = _methodSets.getByClass(doc, arg.substring(1)); break; default: //tagName this.elements = _methodSets.convertToArray(doc.getElementsByTagName(arg)); } break; case 'object': this.elements.push(arg); } }; // 用来存放内部调用的方法 JSLibrary.methodSets = { //文档加载完毕执行js脚本 ready: (function() { var funcs = []; //当获得事件时,要运行的函数 var bReady = false; //当触发事件处理程序时,切换到true //当文档准备就绪时,调用事件处理程序 function handler(e) { //如果已经运行过一次, 只需要返回 if (bReady) return; //如果发生readystatechange事件,但是其状态不是'complete'的话, 那么文档尚未准备好 if (e.type === 'readystatechange' && doc.readyState !== 'complete') return; //运行所有注册函数, 注意每次都要计算funcs.length, 以防止这些函数的调用可能会导致注册更多的函数 for (var i = 0; i < funcs.length; i++) { funcs[i].call(doc); } //现在设置ready标识为true, 并移除所有函数 bReady = true; funcs = null; } //为接收到任何事件注册处理程序 if (doc.addEventListener) { doc.addEventListener('DomContentLoaded', handler, false); doc.addEventListener('readystatechange', handler, false); win.addEventListener('load', handler, false); } else if (doc.attachEvent) { doc.attachEvent('onreadystatechange', handler); win.attachEvent('onload', handler); } //返回whenReady()函数 return function whenReady(f) { if (bReady) f.call(doc); //若准备完毕, 只需要运行它 else funcs.push(f); //否则, 加入列队等候 } })(), //把NodeList数组对象转换成数组 convertToArray: function(nodes) { var arrayOfNodes = null; try { arrayOfNodes = Array.prototype.slice.call(nodes, 0); } catch (ex) { //兼容ie8及以前的版本 arrayOfNodes = new Array(); for (var i = 0; i < nodes.length; i++) { arrayOfNodes.push(nodes[i]); } }; return arrayOfNodes; }, myAddEvent: function(obj, sEv, fn) { if (obj.attachEvent) { obj.attachEvent('on' + sEv, function() { if (false == fn.call(obj)) { event.cancelBubble = true; return false; } }); } else { obj.addEventListener(sEv, function(ev) { if (false == fn.call(obj)) { ev.cancelBubble = true; ev.preventDefault(); } }, false); } }, getByClass: function(oParent, sClass) { var aEle = oParent.getElementsByTagName('*'); var aResult = []; var i = 0; for (i = 0; i < aEle.length; i++) { if (aEle[i].className == sClass) { aResult.push(aEle[i]); } } return aResult; }, getStyle: function(obj, attr) { if (obj.currentStyle) { return obj.currentStyle[attr]; } else { return getComputedStyle(obj, false)[attr]; } } }; var _methodSets = JSLibrary.methodSets; /********************* 以下JSLibrary 原型方法 开始 ************************/ JSLibrary.prototype = { constructor: JSLibrary, /** * 这个点击事件是针对传统pc的 * 手机端使用tap事件 */ click: function(callback) { this.elements.forEach(function(item) { _methodSets.myAddEvent(item, 'click', callback); }); return this; }, _setEvent: function(evtName, callback) { var oFinger = null, currentDom = null; for (var i = 0, len = this.elements.length; i < len; i += 1) { currentDom = this.elements[i]; if (currentDom.oFinger) { currentDom.oFinger.on(evtName, callback); } else { oFinger = new jsLib.Finger(currentDom, {}); oFinger.on(evtName, callback); currentDom.oFinger = oFinger; }; }; }, _removeEvent: function(evtName, callback) { var currentDom = null; for (var i = 0, len = this.elements.length; i < len; i += 1) { currentDom = this.elements[i]; if (currentDom.oFinger) { currentDom.oFinger.off(evtName, callback); }; }; }, /** * 移动端事件 * @use * var aLi = jsLib('#ul1').find('li').swipe(swipe); function swipe(ev) { console.log(ev.direction); } aLi.tap(tap); function tap(ev) { console.log('tap'); jsLib(ev.target).removeSwipe(swipe); } aLi.doubleTap(doubleTap); function doubleTap(ev) { console.log('doubleTap'); jsLib(ev.target).removeTap(tap); }; aLi.longTap(longTap); function longTap(ev) { console.log('longTap'); jsLib(ev.target).removeDTap(doubleTap); }; aLi.destroy(); //销毁dom上的一切事件 */ tap: function(callback) { this._setEvent('tap', callback); return this; }, removeTap: function(callback) { this._removeEvent('tap', callback); return this; }, swipe: function(callback) { // console.log(ev.direction); this._setEvent('swipe', callback); return this; }, removeSwipe: function(callback) { this._removeEvent('swipe', callback); return this; }, longTap: function(callback) { this._setEvent('longTap', callback); return this; }, removeLTap: function(callback) { this._removeEvent('longTap', callback); return this; }, doubleTap: function(callback) { this._setEvent('doubleTap', callback); return this; }, removeDTap: function(callback) { this._removeEvent('doubleTap', callback); return this; }, destroy: function() { var currentDom = null; for (var i = 0, len = this.elements.length; i < len; i += 1) { currentDom = this.elements[i]; if (currentDom.oFinger) { currentDom.oFinger.destroy(); }; }; return this; }, transform: function() { for (var i = 0, len = this.elements.length; i < len; i += 1) { jsLib.Transform(this.elements[i]); }; return this.toDom(); }, /* * 据父节点查找其子孩子 * @param { String } 可以是标签名或.calss名 */ find: function(str) { var i = 0; var aResult = []; for (i = 0; i < this.elements.length; i++) { switch (str.charAt(0)) { case '.': //class var aEle = _methodSets.getByClass(this.elements[i], str.substring(1)); aResult = aResult.concat(aEle); break; default: //标签 var aEle = this.elements[i].getElementsByTagName(str); aResult = _methodSets.convertToArray(aEle); } } var newJSLib = jsLib(); newJSLib.elements = aResult; return newJSLib; }, /* * 获取点击时它在其兄弟节点的索引位置 */ index: function() { var _this = this; return (function(_this) { var obj = _this.elements[0]; var aBrother = obj.parentNode.children; var i = 0; for (i = 0; i < aBrother.length; i++) { if (aBrother[i] == obj) { return i; } } }(_this)); }, /* * 获取第几个dom对象(含原型方法) * @param { Number } 从0开始 */ eq: function(n) { return jsLib(this.elements[n]); }, /* * 返回元素的length长度 */ length: function() { return this.elements.length; }, /* * 单纯地获取dom对象(不含原型方法) * @param { Number } 从0开始 */ toDom: function() { if (this.elements.length === 1) { return this.elements[0]; } else { return this.elements; } }, show: function() { this.elements.forEach(function(item) { item.style.display = 'block'; }); }, hide: function() { this.elements.forEach(function(item) { item.style.display = 'none'; }); }, /* * 给节点元素添加样式(多个或者单个) * @param { Object } {width: '100px', height: '100px', background: '#ccc', opacity: 30} * @use: setStyle([oDiv,oDiv2], {width: '100px', height: '100px', background: '#ccc', opacity: 30}); * @use: setStyle(oDiv, {width: 100, height: 100, background: '#ccc', opacity: 30}); */ setStyle: function(json) { (function setDomStyle(obj, json) { if (obj.length) { //对象数组 // for (var i = 0; i < obj.length; i++) arguments.callee(obj[i], json); for (var i = 0; i < obj.length; i++) setDomStyle(obj[i], json); } else { if (arguments.length == 2) { // for (var attr in json) arguments.callee(obj, attr, json[attr]); for (var attr in json) setDomStyle(obj, attr, json[attr]); } else { switch (arguments[1].toLowerCase()) { case 'opacity': obj.style.filter = 'alpha(opacity:' + arguments[2] + ')'; obj.style.opacity = arguments[2] / 100; break; default: if (typeof arguments[2] == 'number') { obj.style[arguments[1]] = arguments[2] + 'px'; } else { obj.style[arguments[1]] = arguments[2]; } break; } } } })(this.elements, json); }, /* * 获取/设置dom样式 * @param { String } 样式名 * @param { String } 样式值 */ css: function(attr, value) { if (arguments.length == 2) { //设置样式 var i = 0; for (i = 0; i < this.elements.length; i++) { this.elements[i].style[attr] = value; } } else { //获取样式 if (typeof attr == 'string') { return _methodSets.getStyle(this.elements[0], attr); } else { for (i = 0; i < this.elements.length; i++) { var k = ''; for (k in attr) { this.elements[i].style[k] = attr[k]; } } } } return this; }, /* * 获取/设置dom属性 * @param { String } dom属性名 * @param { String } dom属性值 */ attr: function(attr, value) { if (arguments.length == 2) { var i = 0; for (i = 0; i < this.elements.length; i++) { this.elements[i][attr] = value; } } else { return this.elements[0][attr]; } return this; }, removeAttr: function(name) { this.elements.forEach(function(item) { item.removeAttribute(name); }); return this; }, /* * 给元素节点设置Css3样式 * @param { String } name 属性名 * @param { String } value 属性值 */ setStyle3: function(name, value) { this.elements.forEach(function(item) { item.style['Webkit' + name.charAt(0).toUpperCase() + name.substring(1)] = value; item.style['Moz' + name.charAt(0).toUpperCase() + name.substring(1)] = value; item.style['ms' + name.charAt(0).toUpperCase() + name.substring(1)] = value; item.style['O' + name.charAt(0).toUpperCase() + name.substring(1)] = value; item.style[name] = value; }); }, /* * 给元素节点添加class * @param { String } sClass class名 */ addClass: function(sClass) { var re = new RegExp('\\b' + sClass + '\\b'); this.elements.forEach(function(item) { if (re.test(item.className)) return; item.className = (item.className + ' ' + sClass).match(/\S+/g).join(' '); }); return this; }, /* * 移除某元素节点的class * @param { String } sClass class名 */ removeClass: function(sClass) { var re = new RegExp('\\b' + sClass + '\\b', 'g'); this.elements.forEach(function(item) { item.className = item.className.replace(re, '').replace(/^\s+|\s+$/g, '').replace(/\s+/g, ' '); }); return this; }, /* * 设置dom文本 */ html: function(str) { this.elements.forEach(function(item) { item.innerHTML = str; }); }, /* * 扩张方法,把一些好的方法扩张到JSLibrary原型上 * @param { String } 方法名 * @param { Function } 函数 */ extend: function(name, fn) { JSLibrary.prototype[name] = fn; }, /** * [功能: 返回元素e的第几层祖先元素, 如果不存在此类祖先或祖先不是Element, * 则返回NUll] * @param {Object Dom} e 指定的元素 * @param {Number} n 第n层祖先元素 * @return {Object Dom} 返回其祖父元素 */ parent: function(n) { if (this.elements.length > 1) return; //多个元素直接返回 var e = this.elements[0]; if (n === undefined) n = 1; while(n -- && e) e = e.parentNode; if (!e || e.nodeType !== 1) return null; // return e; return jsLib(e); }, /** * [功能: 返回元素e的第几个兄弟元素, n为正,返回后续的第n个兄弟元素, * n为负,返回前面的第n个兄弟元素, n为0, 返回e本身] * @param {Object Dom} e 指定的元素 * @param {Number} n 第几个兄弟节点 * @return {Object Dom} 返回第几个兄弟节点 */ sibling: function(n) { if (this.elements.length > 1) return; //多个元素直接返回 var e = this.elements[0]; while(e && n !== 0) { //如果e未定义, 立即返回它 if (n > 0) { //查找后续的兄弟元素 if (e.nextElementSibling) { e = e.nextElementSibling; } else { for (e = e.nextSibling; e && e.nodeType !== 1; e = e.nextSibling) /* 空循环 */; }; n --; } else { //查找前面的兄弟元素 if (e.previousElementSibing) { e = e.previousElementSibing; } else { for (e = e.previousSibling; e && e.nodeType !== 1; e = e.previousSibling) /* 空循环 */; }; n ++; } } // return e; return jsLib(e); }, /** * [功能: 返回元素e的第n代子元素,如果不存在则为NUll, * 负值n代表从后往前计数. 0表示第一个子元素, -1代表最后一个, -2代表倒数第二个,以此类推.(和children功能一样, 从0开始)] * @param {Object Dom} e 指定的元素 * @param {Number} n 第几个 * @return {Object Dom} 返回第n代子元素 */ child: function(n) { if (this.elements.length > 1) return; //多个元素直接返回 var e = this.elements[0]; if (!e) return; if (e.children) { // 如果children数组存在 if (n < 0) // 转换负的n为数组索引 n += e.children.length; if (n < 0) // 如果它仍然为负, 说明没有子元素 return null; // return e.children[n]; //返回指定的子元素 return jsLib(e.children[n]); //返回指定的子元素 } //如果e没有children数组, 找到第一个子元素并向前数, 或找到最后一个子元素并往回数 if (n >= 0) { //找到元素的第一个子元素 if (e.firstElementChild) { e = e.firstElementChild; } else { for (e = e.firstChild; e && e.nodeType !== 1; e = e.nextSibling) /* 空循环 */; } // return this.sibling(e, n); //返回第一个子元素的第n个兄弟元素 return jsLib(this.sibling(e, n)); //返回第一个子元素的第n个兄弟元素 } else { // n为负数 if (e.lastElementChild) { e.lastElementChild; } else { for (e = e.lastChild; e && e.nodeType !== 1; e = e.previousSibling) /* 空循环 */; } // return this.sibling(e, n+1); return jsLib(this.sibling(e, n+1)); } } }; /********************* 以上JSLibrary 原型方法 结束 ************************/ /************** 以下是一些常用的方法挂在到jsLib(类方法) 开始 **************/ /** * 一些实用工具方法挂载到jsLib.utils */ jsLib.utils = {}; /** * 检测一个值是否是NaN */ jsLib.utils.isReallyNaN = function(x) { return x !== x; }; /** * 检测一个对象是否为空 */ jsLib.utils.isEmptyObj = function(obj){ for(var i in obj){ if(obj.hasOwnProperty(i)){ return false; } } return true; }; /** * 检测一个对象是否为数组 */ jsLib.utils.isArray = function (arr) { if (Array.isArray) { return Array.isArray(arr); } else { return Object.prototype.toString.call(arg) === '[object Array]'; } }; /** * * @param parent 要拷贝的对象 * @param child 返回浅拷贝的对象 * * 如果parent对象中属性也是对象或者数组,那么浅拷贝的对象是引用parent这个对象 */ jsLib.utils.extend = function(parent, child) { var i; child = child || {}; for (i in parent) { if (parent.hasOwnProperty(i)) { child[i] = parent[i]; }; }; return child; }; /** * * @param parent * @param child * 深拷贝以后,parent对象和child对象就不相等了,都是独立的了 */ jsLib.utils.extendDeep = function(parent, child) { var i, toStr = Object.prototype.toString, astr = "[object Array]"; child = child || {}; for (i in parent) { if (parent.hasOwnProperty(i)) { if (typeof parent[i] === 'object') { child[i] = (toStr.call(parent[i]) === astr) ? [] : {}; this.extendDeep(parent[i], child[i]); } else { child[i] = parent[i]; }; }; }; return child; }; /** * 从数组中随机取几个不重复的元素 */ jsLib.utils.getArrayItems = function(arr, num) { var temp_array = new Array(); for (var index in arr) { temp_array.push(arr[index]); } var return_array = new Array(); for (var i = 0; i<num; i++) { if (temp_array.length>0) { var arrIndex = Math.floor(Math.random()*temp_array.length); return_array[i] = temp_array[arrIndex]; temp_array.splice(arrIndex, 1); } else { break; }; }; return return_array; } /** * 判断点在多边形内 * eg: var pointCenter = {x: gV.box_w/2, y: gV.box_h/2}; * var polygon = [ * {x:aClientLeftPoint[0], y:aClientLeftPoint[1]}, * {x:aClientRightPoint[0], y:aClientRightPoint[1]}, * {x:aClientRightunderPoint[0], y:aClientRightunderPoint[1]}, * {x:aClientLeftunderPoint[0], y:aClientLeftunderPoint[1]}, * {x:aClientLeftPoint[0], y:aClientLeftPoint[1]} * ]; * jsLib.utils.pointInPolygon(pointCenter, polygon); */ jsLib.utils.pointInPolygon = function(curPoint, points) { var counter = 0; for (var i = 0, p1, p2; i < points.length; i++) { p1 = points[i]; p2 = points[(i + 1) % points.length]; if (p1.y == p2.y) { continue; } if (curPoint.y <= Math.min(p1.y, p2.y)) { continue; } if (curPoint.y >= Math.max(p1.y, p2.y)) { continue; } var x = (curPoint.y - p1.y) * (p2.x - p1.x) / (p2.y - p1.y) + p1.x; if (x > curPoint.x) counter++; }; if (counter % 2 == 0) { return false; } else { return true; }; }; /** * [功能: 作为一个对象的w和h属性返回视口的尺寸(视口坐标)] * @param {Object} w 指定的窗口 * @return {Object} { x: 屏幕的宽度, y: 屏幕的高度 } */ jsLib.utils.getViewportSize = function(w) { //使用指定的窗口, 如果不带参数则使用当前窗口 var w = w || window; //出了ie8及更早的版本以外, 其他浏览器都能用 if (w.innerWidth != null) return { w: w.innerWidth, h: w.innerHeight }; //对标准下的ie (或任何浏览器) var d = w.document; if (document.compatMode == 'CSS1Compat') return { w: d.documentElement.clientWidth, h: d.documentElement.clientHeight }; //对怪异模式下的浏览器 return { w: d.body.clientWidth, h: d.body.clientHeight }; }; /** * [功能: 查询窗口滚动条的位置] * @param {Object} w 指定的窗口 * @return {Object} { x: 滚动条的x, y: 滚动条的y } */ jsLib.utils.getScrollOffset = function(w) { //使用指定的窗口, 如果不带参数则使用当前窗口 var w = w || window; //除了ie8及更早的版本, 其他浏览器都能用 if (w.pageXOffset != null) return { x: w.pageXOffset, y: w.pageYOffset } //对于标准下的ie(或任意浏览器) var d = w.document; if (document.compatMode == 'CSS1Compat') return { x: d.documentElement.scrollLeft, y: d.documentElement.scrollTop } //对于怪异模式下的浏览器 return { x: d.body.scrollLeft, y: d.body.scrollTop } }; /** * [功能: 文档如果有滚动条的话,就不行了(文档坐标,含滚动条)] * @param {Object Dom} e dom节点 * @return {Object} 返回节点坐标(含滚动条) */ jsLib.utils.getElementPosition = function(e) { var x = 0, y = 0; while(e != null) { x += e.offsetLeft; y += e.offsetTop; e = e.offsetParent; } return { x: x, y: y }; }; /** * [功能: 增强版(视口坐标) 与 dom.getBoundingClientRect()对象中left和top相等,并且getBoundingClientRect方法效率高] * @param {Object Dom} elt dom节点 * @return {Object} 返回节点坐标(不含滚动条) */ jsLib.utils.getElementPos = function(elt) { var x = 0, y = 0; //循环以累加偏移量 for (var e = elt; e != null; e = e.offsetParent) { x += e.offsetLeft; y += e.offsetTop; } //在此循环所有的祖先元素,减去滚动的偏移量 //这也减去了主滚动条, 并转化为视口坐标 for (var e = elt.parentNode; e != null && e.nodeType == 1; e = e.parentNode) { x -= e.scrollLeft; y -= e.scrollTop; } return { x: x, y: y }; }; //函数赋值 jsLib.utils.getByClass = _methodSets.getByClass; jsLib.utils.convertToArray = _methodSets.convertToArray; /* * 仅仅简单的dom Id选择器 */ jsLib.getEle = function(id) { return doc.querySelector(id); }; /* ajax 的封装, 含跨域 jsonp * * 参数 默认值 描述 可选值 * url “” 请求的链接 string * type get 请求的方法 get,post * data null 请求的数据 object,string * contentType “” 请求头 string * dataType “” 请求的类型 jsonp * async true 是否异步 blooean * timeOut undefined 超时时间 number * before function(){} 发送之前执行的函数 function * error function(){} 请求报错执行的函数 function * success function(){} 请求成功的回调函数 function * * @use * ajax({ * type:"post", * dataType: 'jsonp', * url:"http://wx.indoorun.com/wx/getUnitsOfFloor.html", //添加自己的接口链接 * data: {'regionId':'14428254382730015', 'floorId':'14428254382890016'}, * timeOut:5000, * before:function(){ * console.log("before"); * }, * success:function(str){ * console.log(str); * }, * error:function(){ * console.log("error"); * } * }); */ jsLib.ajax = function(options) { //编码数据 function setData() { var name, value; if (data) { if (typeof data === "string") { data = data.split("&"); for (var i = 0, len = data.length; i < len; i++) { name = data[i].split("=")[0]; value = data[i].split("=")[1]; data[i] = encodeURIComponent(name) + "=" + encodeURIComponent(value); } data = data.replace("/%20/g", "+"); } else if (typeof data === "object") { var arr = []; for (var name in data) { if (typeof data[name] !== 'undefined') { var value = data[name].toString(); name = encodeURIComponent(name); value = encodeURIComponent(value); arr.push(name + "=" + value); } } data = arr.join("&").replace("/%20/g", "+"); } //若是使用get方法或JSONP,则手动添加到URL中 if (type === "get" || dataType === "jsonp") { url += url.indexOf("?") > -1 ? (url.indexOf("=")>-1 ? "&"+data : data ): "?" + data; } } } // JSONP function createJsonp() { var script = document.createElement("script"), timeName = new Date().getTime() + Math.round(Math.random() * 1000), callback = "JSONP_" + timeName; window[callback] = function(data) { clearTimeout(timeout_flag); document.body.removeChild(script); success(data); } script.src = url + (url.indexOf("?") > -1 ? "&" : "?") + "callback=" + callback; script.type = "text/javascript"; document.body.appendChild(script); setTime(callback, script); } //设置请求超时 function setTime(callback, script) { if (timeOut !== undefined) { timeout_flag = setTimeout(function() { if (dataType === "jsonp") { // delete window[callback]; document.body.removeChild(script); } else { timeout_bool = true; xhr && xhr.abort(); } console.log("timeout"); }, timeOut); } } // XHR function createXHR() { //由于IE6的XMLHttpRequest对象是通过MSXML库中的一个ActiveX对象实现的。 //所以创建XHR对象,需要在这里做兼容处理。 function getXHR() { if (window.XMLHttpRequest) { return new XMLHttpRequest(); } else { //遍历IE中不同版本的ActiveX对象 var versions = ["Microsoft", "msxm3", "msxml2", "msxml1"]; for (var i = 0; i < versions.length; i++) { try { var version = versions[i] + ".XMLHTTP"; return new ActiveXObject(version); } catch (e) {} } } } //创建对象。 xhr = getXHR(); xhr.open(type, url, async); //设置请求头 if (type === "post" && !contentType) { //若是post提交,则设置content-Type 为application/x-www-four-urlencoded xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8"); } else if (contentType) { xhr.setRequestHeader("Content-Type", contentType); } //添加监听 xhr.onreadystatechange = function() { if (xhr.readyState === 4) { if (timeOut !== undefined) { //由于执行abort()方法后,有可能触发onreadystatechange事件, //所以设置一个timeout_bool标识,来忽略中止触发的事件。 if (timeout_bool) { return; } clearTimeout(timeout_flag); } if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) { success(xhr.responseText); } else { error(xhr.status, xhr.statusText); } } }; //发送请求 xhr.send(type === "get" ? null : data); setTime(); //请求超时 } var url = options.url || "", //请求的链接 type = (options.type || "get").toLowerCase(), //请求的方法,默认为get data = options.data || null, //请求的数据 contentType = options.contentType || "", //请求头 dataType = options.dataType || "", //请求的类型 async = options.async === undefined && true, //是否异步,默认为true. timeOut = options.timeOut, //超时时间。 before = options.before || function() {}, //发送之前执行的函数 error = options.error || function() {}, //错误执行的函数 success = options.success || function() {}; //请求成功的回调函数 var timeout_bool = false, //是否请求超时 timeout_flag = null, //超时标识 xhr = null; //xhr对角 setData(); before(); if (dataType === "jsonp") { createJsonp(); } else { createXHR(); } }; /* * 把url后面的参数放入到一个对象中去 * 返回这个对象 */ jsLib.getQueryString = function() { var str = location.search.length > 0 ? location.search.substring(1) : ""; var items = str.length ? str.split("&") : []; var args = {}, item = null, name = null, value = null; for (var i = 0, len = items.length; i < len; i++) { item = items[i].split("="); name = decodeURIComponent(item[0]); value = decodeURIComponent(item[1]); if (name.length) { args[name] = value; } }; return args; }; /* * 绑定和解绑事件的方法 */ jsLib.EventUtil = { //事件绑定 EventUtil.addHandler() addHandler: function(element, type, handler) { //要绑定的元素, 事件类型, 发生事件的函数 if (element.addEventListener) { element.addEventListener(type, handler, false); // false为事件冒泡 (w3c标准下) } else if (element.attachEvent) { element.attachEvent('on' + type, handler); // 只有事件冒泡 (ie下) } else { element['on' + type] = handler; } }, //事件移除 removeHandler: function(element, type, handler) { if (element.removeEventListener) { element.removeEventListener(type, handler, false); } else if (element.detachEvent) { element.detachEvent('on' + type, handler); } else { element['on' + type] = null; } }, //获取事件对象 getEvent: function(event) { return event ? event : win.event; }, //获取事件目标 getTarget: function(event) { var oEvent = jsLib.EventUtil.getEvent(event); return oEvent.target || oEvent.srcElement; //标准或ie下 }, //取消默认事件 preventDefault: function(event) { var oEvent = jsLib.EventUtil.getEvent(event); oEvent.preventDefault ? oEvent.preventDefault() : oEvent.returnValue = false; }, //阻止事件冒泡和事件捕获 stopPropagation: function(event) { var oEvent = jsLib.EventUtil.getEvent(event); oEvent.stopPropagation ? oEvent.stopPropagation() : oEvent.cancelBubble = true; } }; /************** 以上是一些常用的方法挂在到jsLib(类方法) 结束 **************/ /************** 以下一些方法(插件)扩展到JSLibrary原型上 开始 **************/ /* * 任意dom节点运动方法 * @param { Object } 运动的属性 * @param { Function } 回调函数 * @user jsLib('#id').animate({left: '200', top: '200', 'opacity': 30}); */ jsLib().extend('animate', function(json, fn, time) { var time = time || 30; this.elements.forEach(function(item, index, array) { startMove(item, json, fn); }); function getStyle(obj, attr) { if (obj.currentStyle) { return obj.currentStyle[attr]; } else { return getComputedStyle(obj, false)[attr]; } } //运动 function startMove(obj, json, fn) { clearInterval(obj.timer); obj.timer = setInterval(function() { var attr = ''; var iStop = true; //假设所有值都到达了,定时器里一轮的运动结束了 for (attr in json) { //1.计算当前值 var iCurr = 0; if (attr == 'opacity') { iCurr = parseInt(parseFloat(getStyle(obj, attr)) * 100); } else { iCurr = parseInt(getStyle(obj, attr)); } //2.计算速度 var speed = (json[attr] - iCurr) / 8; speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed); //3.检测停止 if (iCurr != json[attr]) { iStop = false; }; if (attr == 'opacity') { obj.style.opacity = (iCurr + speed) / 100; obj.style.filter = 'alpha(opacity:' + (iCurr + speed) + ')'; } else { obj.style[attr] = iCurr + speed + 'px'; }; }; if (iStop) { //所有属性都到达了目标,那就关闭定时器 clearInterval(obj.timer); fn && fn(); }; }, time); } }); /** * 任意运动, 传入回调函数, 分批执行 * @return {_stopMove} 可以停止运动 * 可以单独使用 * @user * var stop = new Move.elastic([0, 1], 800, function(v){ * console.log(v); // v是速度在 0,1之间以各种运动变化, 每变化一次就执行一次函数 * } * * stop(); //通过调用,可以立即停止 */ ; (function(exports) { var PI = Math.PI, sin = Math.sin, cos = Math.cos, pow = Math.pow, abs = Math.abs, sqrt = Math.sqrt; var request = window.requestAnimationFrame, stopRequest = window.cancelAnimationFrame; var _move, _stopMove; // 都是函数, 不支持requestAnimationFrame的浏览器就使用定时器 //初始化运动函数和停止函数 if (request) { _move = function(fn, timer) { // fn: 匿名函数, timer: 不同的空对象 var step = function() { if (!fn()) { // fn函数返回值为假值则调用requestAnimationFrame方法(true代表运动结束) timer.id = request(step); }; }; step(); // 函数调用 }; } else { _move = function(fn, timer) { timer.id = setInterval(fn, 16); // 采用定时器, 时间间隔不能低于16 }; }; if (stopRequest) { _stopMove = function(timer) { stopRequest(timer.id); // 停止动画调用 }; } else { _stopMove = function(timer) { clearInterval(timer.id); // 关闭定时器 }; }; var Move = function() { // Move构造函数 this.aCurve = []; // 曲线动画函数名集合 this.init(); }; var curve = Move.prototype = { // Move原型 // 初始化动画曲线 init: function() { this.extends({ //定义域和值域均为[0, 1], 传入自变量x返回对应值y //先加速后减速 ease: function(x) { // return -0.5*cos(PI * (2 - x)) + 0.5; if (x <= 0.5) return 2 * x * x; else if (x > 0.5) return -2 * x * x + 4 * x - 1; }, // 初速度为0 ,一直加速 easeIn: function(x) { return x * x; }, //先慢慢加速1/3, 然后突然大提速, 最后减速 ease2: function(x) { return x < 1 / 3 ? x * x : -2 * x * x + 4 * x - 1; }, //初速度较大, 一直减速, 缓冲动画 easeOut: function(x) { return pow(x, 0.8); }, //碰撞动画 collision: function(x) { var a, b; //a, b代表碰撞点的横坐标 for (var i = 1, m = 20; i < m; i++) { a = 1 - (4 / 3) * pow(0.5, i - 1); b = 1 - (4 / 3) * pow(0.5, i); if (x >= a && x <= b) { return pow(3 * (x - (a + b) / 2), 2) + 1 - pow(0.25, i - 1); } } }, //弹性动画 elastic: function(x) { return -pow(1 / 12, x) * cos(PI * 2.5 * x * x) + 1; }, //匀速动画 linear: function(x) { return x; }, //断断续续加速减速 wave: function(x) { return (1 / 12) * sin(5 * PI * x) + x; }, //先向反方向移动一小段距离, 然后正方向移动, 并超过终点一小段, 然后回到终点 opposite: function(x) { return (sqrt(2) / 2) * sin((3 * PI / 2) * (x - 0.5)) + 0.5; }, // 相反的三次贝塞尔 reverseEase: function (x) { return 1 - Math.sqrt(1 - x * x); } }); }, // 随机选择一个动画方法名 getRd: function () { var preItem = null; return function () { var arr = this.aCurve; var index = Math.floor(Math.random() * arr.length), item = arr[index], result; if (preItem != item) { preItem = item; result = item; } else { result = this.getRd(arr); }; return result; }; }(), // 扩张曲线动画 extends: function(obj) { for (var k in obj) { if (k in curve) { console.warn('扩张的方法名' + k + ': 已经存在, 换个方法名吧!' ); return; }; this.aCurve.push(k); curve[k] = (function(moveType) { // 给Move原型添加 动画曲线 方法 return function() { return _doMove.call(this, arguments, moveType); // 每个动画曲线方法实际调用_doMove函数 }; })(obj[k]); }; } }; /** * 开始动画函数 * arg: 用户要传的([0, 1000], 500, function(v){ ... }, fnEnd) * moveType: 曲线动画函数 */ function _doMove(arg, moveType) { var r, // r => 过渡范围, 例如[0, 1000] (必须传, 且传数组) d, // d => 过渡时间, ms, (可不传, 默认500) fn, // fn => 每一帧的回调函数, 传入当前过渡值v (必须传) fnEnd; // fnEnd => 动画结束时回调 (可不传) // 严格限制传入参数, 且传入的参数可以没有顺序 for (var i = 0; i < 4; i++) { if (typeof arg[i] === 'object' && !r) r = arg[i]; else if (typeof arg[i] === 'number' && !d) d = arg[i]; else if (typeof arg[i] === 'function' && !fn) fn = arg[i]; else if (typeof arg[i] === 'function' && !fnEnd) fnEnd = arg[i]; }; if (!r instanceof Array || !fn) return; // 如果r不是数组或者fn不是函数(真值)就return掉 d = d || 500; // 过渡时间默认500ms var from = +new Date, //起始时间 x = 0, y, a = r[0], // 过渡范围的起点 b = r[1]; // 过度范围的终点 var timer = 't' + Math.random(); // 随机数 var self = this; // 存一下Move的实例 //用于保存定时器ID的对象, requestAnimation递归调用必须传入对象(给实例添加timer属性值为{}) this[timer] = {}; // 优先使用requestAnimationFrame否则setInterval定时器 _move(function() { x = (+new Date - from) / d; if (x >= 1) { // 动画结束 fn(b); // 调用外部动画的回调函数且把过度范围的终点值作为参数传过去 if (fnEnd) fnEnd(); // 如果有动画结束回调函数就执行回调函数 return true; // 返回真值停止调用requestAnimationFrame方法 } else { // 动画进行中 y = moveType(x); // 调用动画曲线中的函数返回运动数字 fn(a + (b - a) * y); // 调用外部动画的回调函数传参为 a + (b - a) * y }; }, self[timer]); return function() { _stopMove(self[timer]); // 调用cancelAnimationFrame方法停止动画 return a + (b - a) * y; // 返回动画停止后的运动数字 }; }; // 抛出去 exports.Move = Move; // Move构造函数抛出去 })(jsLib); ; (function(exports) { // 一些要使用的内部工具函数 // 2点之间的距离 (主要用来算pinch的比例的, 两点之间的距离比值求pinch的scale) function getLen(v) { return Math.sqrt(v.x * v.x + v.y * v.y); }; // dot和getAngle函数用来算两次手势状态之间的夹角, cross函数用来算方向的, getRotateAngle函数算手势真正的角度的 function dot(v1, v2) { return v1.x * v2.x + v1.y * v2.y; }; // 求两次手势状态之间的夹角 function getAngle(v1, v2) { var mr = getLen(v1) * getLen(v2); if (mr === 0) return 0; var r = dot(v1, v2) / mr; if (r > 1) r = 1; return Math.acos(r); }; // 利用cross结果的正负来判断旋转的方向(大于0为逆时针, 小于0为顺时针) function cross(v1, v2) { return v1.x * v2.y - v2.x * v1.y; }; // 如果cross大于0那就是逆时针对于屏幕是正角,对于第一象限是负角,所以 角度 * -1, 然后角度单位换算 function getRotateAngle(v1, v2) { var angle = getAngle(v1, v2); if (cross(v1, v2) > 0) { angle *= -1; }; return angle * 180 / Math.PI; }; // HandlerAdmin构造函数 var HandlerAdmin = function(el) { this.handlers = []; // 手势函数集合 this.el = el; // dom元素 }; // HandlerAdmin原型方法 // 把fn添加到实例的 handlers数组中 HandlerAdmin.prototype.add = function(handler) { this.handlers.push(handler); }; // 删除 handlers数组中的函数 HandlerAdmin.prototype.del = function(handler) { if(!handler) this.handlers = []; // handler为假值,handlers则赋值为[](参数不传undefined,其实不管this.handlers有没有成员函数,都得置空) for(var i = this.handlers.length; i >= 0; i--) { if(this.handlers[i] === handler) { // 如果函数一样 this.handlers.splice(i, 1); // 从handler中移除该函数(改变了原数组) }; }; }; // 执行用户的回调函数 HandlerAdmin.prototype.dispatch = function() { for(var i=0, len=this.handlers.length; i<len; i++) { var handler = this.handlers[i]; if(typeof handler === 'function') handler.apply(this.el, arguments); // 执行回调this为dom元素, 把触发的事件对象作为参数传过去了 }; }; function wrapFunc(el, handler) { var handlerAdmin = new HandlerAdmin(el); // 实例化一个对象 handlerAdmin.add(handler); return handlerAdmin; }; // AlloyFinger构造函数 var AlloyFinger = function (el, option) { // el: dom元素/id, option: 各种手势的集合对象 this.element = typeof el == 'string' ? document.querySelector(el) : el; // 获取dom元素 // 绑定原型上start, move, end, cancel函数的this对象为 AlloyFinger实例 this.start = this.start.bind(this); this.move = this.move.bind(this); this.end = this.end.bind(this); this.cancel = this.cancel.bind(this); // 给dom元素 绑定原生的 touchstart, touchmove, touchend, touchcancel事件, 默认冒泡 this.element.addEventListener("touchstart", this.start, false); this.element.addEventListener("touchmove", this.move, false); this.element.addEventListener("touchend", this.end, false); this.element.addEventListener("touchcancel", this.cancel, false); this.preV = { x: null, y: null }; // 开始前的坐标 this.pinchStartLen = null; // start()方法开始时捏的长度 this.scale = 1; // 初始缩放比例为1 this.isDoubleTap = false; // 是否双击, 默认为false var noop = function () { }; // 空函数(把用户没有绑定手势函数默认赋值此函数) // 提供了14种手势函数. 根据option对象, 分别创建一个 HandlerAdmin实例 赋值给相应的this属性 this.rotate = wrapFunc(this.element, option.rotate || noop); this.touchStart = wrapFunc(this.element, option.touchStart || noop); this.multipointStart = wrapFunc(this.element, option.multipointStart || noop); this.multipointEnd = wrapFunc(this.element, option.multipointEnd || noop); this.pinch = wrapFunc(this.element, option.pinch || noop); this.swipe = wrapFunc(this.element, option.swipe || noop); this.tap = wrapFunc(this.element, option.tap || noop); this.doubleTap = wrapFunc(this.element, option.doubleTap || noop); this.longTap = wrapFunc(this.element, option.longTap || noop); this.singleTap = wrapFunc(this.element, option.singleTap || noop); this.pressMove = wrapFunc(this.element, option.pressMove || noop); this.touchMove = wrapFunc(this.element, option.touchMove || noop); this.touchEnd = wrapFunc(this.element, option.touchEnd || noop); this.touchCancel = wrapFunc(this.element, option.touchCancel || noop); this.delta = null; // 差值 变量增量 this.last = null; // 最后数值 this.now = null; // 开始时的时间戳 this.tapTimeout = null; // tap超时 this.singleTapTimeout = null; // singleTap超时 this.longTapTimeout = null; // longTap超时(定时器的返回值) this.swipeTimeout = null; // swipe超时 this.x1 = this.x2 = this.y1 = this.y2 = null; // start()时的坐标x1, y1, move()时的坐标x2, y2 (相对于页面的坐标) this.preTapPosition = { x: null, y: null }; // 用来保存start()方法时的手指坐标 }; // AlloyFinger原型对象 AlloyFinger.prototype = { start: function (evt) { if (!evt.touches) return; // 如果没有TouchList对象, 直接return掉 (touches: 位于屏幕上的所有手指的列表) this.now = Date.now(); // 开始时间戳 this.x1 = evt.touches[0].pageX; // 相对于页面的 x1, y1 坐标 this.y1 = evt.touches[0].pageY; this.delta = this.now - (this.last || this.now); // 时间戳差值 this.touchStart.dispatch(evt); // 调用HandlerAdmin实例this.touchStart上的dispatch方法(用户的touchStart回调就在此调用的) if (this.preTapPosition.x !== null) { // 开始前tap的x坐标不为空的话(一次没点的时候必然是null了) this.isDoubleTap = (this.delta > 0 && this.delta <= 250 && Math.abs(this.preTapPosition.x - this.x1) < 30 && Math.abs(this.preTapPosition.y - this.y1) < 30); }; this.preTapPosition.x = this.x1; // 把相对于页面的 x1, y1 坐标赋值给 this.preTapPosition this.preTapPosition.y = this.y1; this.last = this.now; // 把开始时间戳赋给 this.last var preV = this.preV, // 把开始前的坐标赋给 preV变量 len = evt.touches.length; // len: 手指的个数 if (len > 1) { // 一根手指以上 this._cancelLongTap(); // 取消长按定时器 this._cancelSingleTap(); // 取消SingleTap定时器 var v = { // 2个手指坐标的差值 x: evt.touches[1].pageX - this.x1, y: evt.touches[1].pageY - this.y1 }; preV.x = v.x; // 差值赋值给PreV对象 preV.y = v.y; this.pinchStartLen = getLen(preV); // start()方法中2点之间的距离 this.multipointStart.dispatch(evt); // (用户的multipointStart回调就在此调用的) }; this.longTapTimeout = setTimeout(function () { this.longTap.dispatch(evt); // (用户的longTap回调就在此调用的) }.bind(this), 750); }, move: function (evt) { if (!evt.touches) return; // 如果没有TouchList对象, 直接return掉 (touches: 位于屏幕上的所有手指的列表) var preV = this.preV, // 把start方法保存的2根手指坐标的差值xy赋给preV变量 len = evt.touches.length, // 手指个数 currentX = evt.touches[0].pageX, // 第一根手指的坐标(相对于页面的 x1, y1 坐标) currentY = evt.touches[0].pageY; this.isDoubleTap = false; // 移动过程中不能双击了 if (len > 1) { // 2根手指以上(处理捏pinch和旋转rotate手势) var v = { // 第二根手指和第一根手指坐标的差值 x: evt.touches[1].pageX - currentX, y: evt.touches[1].pageY - currentY }; if (preV.x !== null) { // start方法中保存的this.preV的x不为空的话 if (this.pinchStartLen > 0) { // 2点间的距离大于0 evt.scale = getLen(v) / this.pinchStartLen; // move中的2点之间的距离除以start中的2点的距离就是缩放比值 this.pinch.dispatch(evt); // scale挂在到evt对象上 (用户的pinch回调就在此调用的) }; evt.angle = getRotateAngle(v, preV); // 计算angle角度 this.rotate.dispatch(evt); // (用户的pinch回调就在此调用的) }; preV.x = v.x; // 把move中的2根手指中的差值赋值给preV, 当然也改变了this.preV preV.y = v.y; } else { // 一根手指(处理拖动pressMove手势) if (this.x2 !== null) { // 一根手指第一次必然为空,因为初始化赋值为null, 下面将会用x2, y2保存上一次的结果 evt.deltaX = currentX - this.x2; // 拖动过程中或者手指移动过程中的差值(当前坐标与上一次的坐标) evt.deltaY = currentY - this.y2; } else { evt.deltaX = 0; // 第一次嘛, 手指刚移动,哪来的差值啊,所以为0呗 evt.deltaY = 0; }; this.pressMove.dispatch(evt); // deltaXY挂载到evt对象上,抛给用户(用户的pressMove回调就在此调用的) }; this.touchMove.dispatch(evt); // evt对象因if语句而不同,挂载不同的属性抛出去给用户 (用户的touchMove回调就在此调用的) this._cancelLongTap(); // 取消长按定时器 this.x2 = currentX; // 存一下本次的pageXY坐标, 为了下次做差值 this.y2 = currentY; if (len > 1) { // 2个手指以上就阻止默认事件 evt.preventDefault(); }; }, end: function (evt) { if (!evt.changedTouches) return; // 位于该元素上的所有手指的列表, 没有TouchList也直接return掉 this._cancelLongTap(); // 取消长按定时器 var self = this; // 存个实例 if (evt.touches.length < 2) { // 手指数量小于2就触发 (用户的多点结束multipointEnd回调函数) this.multipointEnd.dispatch(evt); }; this.touchEnd.dispatch(evt); // 触发(用户的touchEnd回调函数) //swipe 滑动 if ((this.x2 && Math.abs(this.x1 - this.x2) > 30) || (this.y2 && Math.abs(this.preV.y - this.y2) > 30)) { evt.direction = this._swipeDirection(this.x1, this.x2, this.y1, this.y2); // 获取上下左右方向中的一个 this.swipeTimeout = setTimeout(function () { self.swipe.dispatch(evt); // 立即触发,加入异步队列(用户的swipe事件的回调函数) }, 0); } else { // 以下是tap, singleTap, doubleTap事件派遣 this.tapTimeout = setTimeout(function () { self.tap.dispatch(evt); // 触发(用户的tap事件的回调函数) // trigger double tap immediately if (self.isDoubleTap) { // 如果满足双击的话 self.doubleTap.dispatch(evt); // 触发(用户的doubleTap事件的回调函数) clearTimeout(self.singleTapTimeout); // 清除singleTapTimeout定时器 self.isDoubleTap = false; // 双击条件重置 } else { self.singleTapTimeout = setTimeout(function () { self.singleTap.dispatch(evt); // 触发(用户的singleTap事件的回调函数) }, 250); }; }, 0); // 加入异步队列,主线程完成立马执行 }; this.preV.x = 0; // this.preV, this.scale, this.pinchStartLen, this.x1 x2 y1 y2恢复初始值 this.preV.y = 0; this.scale = 1; this.pinchStartLen = null; this.x1 = this.x2 = this.y1 = this.y2 = null; }, cancel: function (evt) { //清除定时器 clearTimeout(this.singleTapTimeout); clearTimeout(this.tapTimeout); clearTimeout(this.longTapTimeout); clearTimeout(this.swipeTimeout); // 执行用户的touchCancel回调函数,没有就执行一次noop空函数 this.touchCancel.dispatch(evt); }, _cancelLongTap: function () { // 取消长按定时器 clearTimeout(this.longTapTimeout); }, _cancelSingleTap: function () { // 取消延时SingleTap定时器 clearTimeout(this.singleTapTimeout); }, // 2点间x与y之间的绝对值的差值作比较,x大的话即为左右滑动,y大即为上下滑动,x的差值大于0即为左滑动,小于0即为右滑动 _swipeDirection: function (x1, x2, y1, y2) { // 判断用户到底是从上到下,还是从下到上,或者从左到右、从右到左滑动 return Math.abs(x1 - x2) >= Math.abs(y1 - y2) ? (x1 - x2 > 0 ? 'Left' : 'Right') : (y1 - y2 > 0 ? 'Up' : 'Down'); }, // 给dom添加14种事件中的一种 on: function(evt, handler) { if(this[evt]) { // 看看有没有相应的事件名 this[evt].add(handler); // HandlerAdmin实例的add方法 }; }, // 移除手势事件对应函数 off: function(evt, handler) { if(this[evt]) { this[evt].del(handler); // 从数组中删除handler方法 }; }, destroy: function() { // 关闭所有定时器 if(this.singleTapTimeout) clearTimeout(this.singleTapTimeout); if(this.tapTimeout) clearTimeout(this.tapTimeout); if(this.longTapTimeout) clearTimeout(this.longTapTimeout); if(this.swipeTimeout) clearTimeout(this.swipeTimeout); // 取消dom上touchstart, touchmove, touchend, touchcancel事件 this.element.removeEventListener("touchstart", this.start); this.element.removeEventListener("touchmove", this.move); this.element.removeEventListener("touchend", this.end); this.element.removeEventListener("touchcancel", this.cancel); // 把14个HandlerAdmin实例的this.handlers置为空数组 this.rotate.del(); this.touchStart.del(); this.multipointStart.del(); this.multipointEnd.del(); this.pinch.del(); this.swipe.del(); this.tap.del(); this.doubleTap.del(); this.longTap.del(); this.singleTap.del(); this.pressMove.del(); this.touchMove.del(); this.touchEnd.del(); this.touchCancel.del(); // 实例成员的变量全部置为null this.preV = this.pinchStartLen = this.scale = this.isDoubleTap = this.delta = this.last = this.now = this.tapTimeout = this.singleTapTimeout = this.longTapTimeout = this.swipeTimeout = this.x1 = this.x2 = this.y1 = this.y2 = this.preTapPosition = this.rotate = this.touchStart = this.multipointStart = this.multipointEnd = this.pinch = this.swipe = this.tap = this.doubleTap = this.longTap = this.singleTap = this.pressMove = this.touchMove = this.touchEnd = this.touchCancel = null; return null; } }; // 抛出去 exports.Finger = AlloyFinger; })(jsLib); ; (function(exports) { var DEG_TO_RAD = 0.017453292519943295; // 三维矩阵 var Matrix3D = function(n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44) { this.elements = window.Float32Array ? new Float32Array(16) : []; var te = this.elements; te[0] = (n11 !== undefined) ? n11 : 1; te[1] = n21 || 0; te[2] = n31 || 0; te[3] = n41 || 0; te[4] = n12 || 0; te[5] = (n22 !== undefined) ? n22 : 1; te[6] = n32 || 0; te[7] = n42 || 0; te[8] = n13 || 0; te[9] = n23 || 0; te[10] = (n33 !== undefined) ? n33 : 1; te[11] = n43 || 0; te[12] = n14 || 0; te[13] = n24 || 0; te[14] = n34 || 0; te[15] = (n44 !== undefined) ? n44 : 1; }; Matrix3D.prototype = { constructor: Matrix3D, set: function(n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44) { var te = this.elements; te[0] = n11; te[4] = n12; te[8] = n13; te[12] = n14; te[1] = n21; te[5] = n22; te[9] = n23; te[13] = n24; te[2] = n31; te[6] = n32; te[10] = n33; te[14] = n34; te[3] = n41; te[7] = n42; te[11] = n43; te[15] = n44; return this; }, identity: function() { this.set( 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ); return this; }, appendTransform: function(x, y, z, scaleX, scaleY, scaleZ, rotateX, rotateY, rotateZ, skewX, skewY, originX, originY, originZ) { var rx = rotateX * DEG_TO_RAD; var cosx = this._rounded(Math.cos(rx)); var sinx = this._rounded(Math.sin(rx)); var ry = rotateY * DEG_TO_RAD; var cosy = this._rounded(Math.cos(ry)); var siny = this._rounded(Math.sin(ry)); var rz = rotateZ * DEG_TO_RAD; var cosz = this._rounded(Math.cos(rz * -1)); var sinz = this._rounded(Math.sin(rz * -1)); this.multiplyMatrices(this, this._arrayWrap([ 1, 0, 0, x, 0, cosx, sinx, y, 0, -sinx, cosx, z, 0, 0, 0, 1 ])); this.multiplyMatrices(this, this._arrayWrap([ cosy, 0, siny, 0, 0, 1, 0, 0, -siny, 0, cosy, 0, 0, 0, 0, 1 ])); this.multiplyMatrices(this, this._arrayWrap([ cosz * scaleX, sinz * scaleY, 0, 0, -sinz * scaleX, cosz * scaleY, 0, 0, 0, 0, 1 * scaleZ, 0, 0, 0, 0, 1 ])); if (skewX || skewY) { this.multiplyMatrices(this, this._arrayWrap([ this._rounded(Math.cos(skewX * DEG_TO_RAD)), this._rounded(Math.sin(skewX * DEG_TO_RAD)), 0, 0, -1 * this._rounded(Math.sin(skewY * DEG_TO_RAD)), this._rounded(Math.cos(skewY * DEG_TO_RAD)), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ])); }; if (originX || originY || originZ) { this.elements[12] -= originX * this.elements[0] + originY * this.elements[4] + originZ * this.elements[8]; this.elements[13] -= originX * this.elements[1] + originY * this.elements[5] + originZ * this.elements[9]; this.elements[14] -= originX * this.elements[2] + originY * this.elements[6] + originZ * this.elements[10]; }; return this; }, // 矩阵相乘 multiplyMatrices: function(a, be) { var ae = a.elements; var te = this.elements; var a11 = ae[0], a12 = ae[4], a13 = ae[8], a14 = ae[12]; var a21 = ae[1], a22 = ae[5], a23 = ae[9], a24 = ae[13]; var a31 = ae[2], a32 = ae[6], a33 = ae[10], a34 = ae[14]; var a41 = ae[3], a42 = ae[7], a43 = ae[11], a44 = ae[15]; var b11 = be[0], b12 = be[1], b13 = be[2], b14 = be[3]; var b21 = be[4], b22 = be[5], b23 = be[6], b24 = be[7]; var b31 = be[8], b32 = be[9], b33 = be[10], b34 = be[11]; var b41 = be[12], b42 = be[13], b43 = be[14], b44 = be[15]; te[0] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41; te[4] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42; te[8] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43; te[12] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44; te[1] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41; te[5] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42; te[9] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43; te[13] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44; te[2] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41; te[6] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42; te[10] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43; te[14] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44; te[3] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41; te[7] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42; te[11] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43; te[15] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44; return this; }, // 解决角度为90的整数倍导致Math.cos得到极小的数,其实是0。导致不渲染 _rounded: function(value, i) { i = Math.pow(10, i || 15); return Math.round(value * i) / i; }, _arrayWrap: function(arr) { return window.Float32Array ? new Float32Array(arr) : arr; } }; // 主入口函数 function Transform(obj, notPerspective) { var observeProps = ['translateX', 'translateY', 'translateZ', 'scaleX', 'scaleY', 'scaleZ', 'rotateX', 'rotateY', 'rotateZ', 'skewX', 'skewY', 'originX', 'originY', 'originZ'], objIsElement = isElement(obj); if (!notPerspective) { observeProps.push('perspective'); }; obj.matrix3d = new Matrix3D(); observe( obj, observeProps, function() { var mtx = obj.matrix3d.identity().appendTransform(obj.translateX, obj.translateY, obj.translateZ, obj.scaleX, obj.scaleY, obj.scaleZ, obj.rotateX, obj.rotateY, obj.rotateZ, obj.skewX, obj.skewY, obj.originX, obj.originY, obj.originZ); var transform = (notPerspective ? '' : 'perspective(' + obj.perspective + 'px) ') + 'matrix3d(' + Array.prototype.slice.call(mtx.elements).join(',') + ')'; if (objIsElement) { obj.style.transform = obj.style.msTransform = obj.style.OTransform = obj.style.MozTransform = obj.style.webkitTransform = transform; } else { obj.transform = transform; }; }); if (!notPerspective) { obj.perspective = 500; // 景深默认值 }; obj.scaleX = obj.scaleY = obj.scaleZ = 1; obj.translateX = obj.translateY = obj.translateZ = obj.rotateX = obj.rotateY = obj.rotateZ = obj.skewX = obj.skewY = obj.originX = obj.originY = obj.originZ = 0; }; // 工具函数 function isElement(obj) { return ( typeof HTMLElement === 'object' ? obj instanceof HTMLElement : //DOM2 obj && typeof obj === 'object' && obj !== null && obj.nodeType === 1 && typeof obj.nodeName === 'string' ); }; function observe(target, props, callback) { for (var i = 0, len = props.length; i < len; i += 1) { var prop = props[i]; watch(target, prop, callback); }; }; // 每一次改变那15个属性中的任意一个,都会执行回调 function watch(target, prop, callback) { Object.defineProperty(target, prop, { get: function() { return this['_' + prop]; }, set: function(value) { if (value !== this['_' + prop]) { this['_' + prop] = value; callback(); }; } }); }; // 抛出去 exports.Transform = Transform; })(jsLib); /************** 上面一些方法(插件)扩展到JSLibrary原型上 结束 **************/ // 把jsLib抛出去 function jsLib(arg) { return new JSLibrary(arg); }; if (typeof module !== 'undefined' && typeof exports === 'object') { module.exports = jsLib; } else { if (!win.jsLib) win.jsLib = jsLib; }; }(window, document));
开心的做一个无忧无虑的码农,争取每天进步一点。