Heim >Backend-Entwicklung >PHP-Tutorial >Detaillierte Erläuterung des Front-End-Frameworks Heng.js
Framework-Beschreibung: Basierend auf objektorientierten Programmierideen und in der nativen JS-Sprache implementiert, ist es nicht auf eine JS-Bibliothek von Drittanbietern angewiesen und verweist nicht auf externe Codefragmente! Berücksichtigen Sie verschiedene Anwendungsszenarien vollständig, um die Flexibilität und Robustheit des Frameworks sicherzustellen.
* Implementierungsfunktion: Sie implementiert die Architektur und die internen Details, die vollständig mit jQuery übereinstimmen. Die interne Schleife des Einstellungsvorgangs kann alle übereinstimmenden Knoten festlegen und die Methode kann in einer Kette aufgerufen werden.
* Methodenklassifizierung: Statische Methoden werden innerhalb des Frameworks für Datenlogik, DOM-Vorverarbeitung und andere globale Nicht-DOM-Instanzmethoden verwendet, die entsprechende UI-Komponenten aufrufen.
* Parameterkonfiguration: Konfigurieren Sie verschiedene Parameter entsprechend den tatsächlichen Anforderungen und rufen Sie sie flexibel auf. Wenn keine Parameter vorhanden sind, verwenden Sie die Standardkonfiguration.
* Selektorunterstützung: ID, Klasse, Label, Nachkommenselektor, Dom-Knoten, instanziiertes Objekt H („CSS-Selektor“).
* Kompatibilität: Alle Methoden sind vollständig browserübergreifend kompatibel und mit den meisten aktuellen Browsern (IE6/7/8/9/10/11, Chrome, Firefox, Safari usw.) kompatibel.
* Variablensicherheit: In einer geschlossenen Umgebung ist das Framework selbst garantiert sicher und variabel, und Variablenkonflikte werden vermieden.
* Framework-Leistung: Optimieren Sie den Code, um die Leistung zu verbessern und Browser-Neuanordnungen und -Neuzeichnungen zu minimieren. Geben Sie Speicher frei, indem Sie einen Nullwert zuweisen. Vermeiden Sie die Inselkommunikation zwischen DOM, BOM und ECMAScript. Führen Sie DOM-Operationen basierend auf Dokumentfragmenten durch, um Sammlungsereignisse zu verarbeiten * Statische Methode: H.Method(value)
* Instanzmethode: H("css selector").Method(value), H("css selector").Method({key:value})
* H(sel).Method({Eigenschaftsereigniskonfiguration}), H(sel).Method(Komponentenmethode, Methodenparameter)
* Kettenaufruf: H("css selector").Method({key :value}) .Method()
* Ausführung nach dem Laden des DOM-Baums: H(fn) entspricht $(fn)===$(document).ready(fn)
* Interne Schleife: Die Instanzmethode wird ausgeführt Eine interne Schleife für die Menge der mit H übereinstimmenden Knoten („CSS-Selektor“) und kann alle übereinstimmenden Knoten gleichzeitig bedienen
* Attributmethode: opts legt das Komponentenattribut fest ) ;
* Gestenberührung: Kapselt Berührungsgestenereignisse (Tippen, Kneifen, Halten, Wischen) für das mobile Endgerät und meldet den Status der entsprechenden Geste über e.data. Wie Pinch-Zoom-Koeffizient, Schieberichtung, Schiebeabstand usw.
*
* Autor: wheng
* Datum: 25.07.2015
*/
* Framework-Quellcode http://whwheng.gitee .io/csdn
* Implementieren Sie die Funktion „CSS-Selektor“ im Konstruktor, um DOM-Knoten zu erhalten und die Knoten im Knotenattribut zu speichern, wobei Sie den Regeln der „öffentlichen Datenattribution“ folgen.
(function(){ ……框架主体……; window.H=window.Heng=H;//向外部引用基础类 })(); H(sel).Method()或Heng.(sel).Method();//外部实例化 *内部已做了Heng基础类实例化处理new Heng(sel);实际使用时H(sel)即为创建对象,若sel为函数内嵌代码在形成完整DOM树时立即执行。 H(fn)实现了jqyuey库中的$(fn)===$(document).ready(fn)形成完整DOM树即可执行。 var H=function (sel){ if(typeof sel=="function"){ H.ready(sel) } else{ return new Heng(sel); } }; H(function(){ H(sel).Method();});//DOM树加载完毕H(fn)等价于jqyuey库中的$(fn)===$(document).ready(fn)
*Statische Methoden erfordern keine Instanziierung und werden für die Datenlogik und DOM-Vorverarbeitung verwendet, oder andere globale Nicht-DOM-Instanzmethoden werden mit DOM-Knoten kombiniert, um die Anforderungen zu erfüllen SEL-Matching-Komponenteneintrag ist eine Sammlung, dann können alle Knoten die Logik und Funktionen der Methode implementieren.
function Heng(sel) { //【原选择器理】:选择器拆分数组,从document开始上级get后代作为下级祖先 this.sel = sel; this.nodes = []; //选择器匹配的节点数组 if (typeof sel == 'string') { if (sel.indexOf(' ') != -1) { var nodes = sel.split(' '); var childElements = []; var node = []; //实时祖先节点数组 for (var i = 0; i < nodes.length; i++) { if (node.length == 0) node.push(document); switch (nodes[i].charAt(0)) { case '#': childElements = []; //清除上一组值再更新 childElements.push(this.getId(nodes[i].substring(1))); node = childElements; break; case '.': childElements = []; for (var j = 0; j < node.length; j++) { var temps = this.getClass(nodes[i].substring(1), node[j]); for (var k = 0; k < temps.length; k++) { childElements.push(temps[k]); } } node = childElements; break; default: childElements = []; for (var j = 0; j < node.length; j++) { var temps = this.getTagName(nodes[i], node[j]); for (var k = 0; k < temps.length; k++) { childElements.push(temps[k]); } } node = childElements; } } this.nodes = childElements; } else { //sel为无空格选择器字符串 switch (sel.charAt(0)) { case '#': this.nodes.push(this.getId(sel.substring(1))); break; case '.': this.nodes = this.getClass(sel.substring(1)); break; default: this.nodes = this.getTagName(sel); } } } else if (typeof sel == 'object') { //sel为dom节点 if (sel != undefined) { this.nodes[0] = sel; } } }
*Objektorientierte Programmierung führt zwangsläufig zur Verwirrung dieses Zeigers. Deklarieren Sie bei allen ähnlichen Problemen var This=this außerhalb der Funktion, indem Sie diesen Zeiger korrigieren.
//静态方法 H.Method() = function (arg) { …code… }; //实例方法 Heng.prototype.Method = function (opts) { …… var nodes = this.nodes[i]; //sel匹配的的节点数组 for (var i = 0; i < this.nodes.length; i++) {//内部循环 var op = this.nodes[i]; var aUl = op.getElementsByTagName('ul'); …… } }
*
Heng.prototype.slide = function (opts) { …… var This = this; node = function (opts) { //node为dom节点 //原型上的方法getClass只能通过指向实例的this去引用,此时的this为node节点 This.getClass(classString, parrent); /*this.getClass(classString,parrent)错误*/ } } *每一个实例方法都可以通过修改参数对象otps实现不同的需求,未指定的数据会使用默认值。 //模态框 Heng.prototype.dialog = function (opts) { //opts={"animate":是否开启动画,"enterDir":进入方向,"maskopa":遮罩透明度,"warncount":警告闪烁次数,"content":弹框内容} var def = { "animate" : false, "enterDir" : "top", "maskbg" : "#000000", "maskopa" : 0.5, "warncount" : 5, "content" : "<h1>Hello World</h1>" };//def为默认值 opts = H.extend(def, opts);//数据合并 …… }*[Instanzmethodenfall] H("form").formCheck(); eine Codezeile schließt die gesamte Formularüberprüfung auf der Seite ab , und die Funktionen sind wie folgt: * Alle Arten von Formularelementen in allen Formularen, die mit H („Form“) übereinstimmen, werden überprüft. *Jedes Formularelement wird unabhängig validiert, wenn es den Fokus verliert. * Beim Absenden des Formulars werden alle internen Elemente einheitlich überprüft. Dies ist unterteilt in „Einzelne Überprüfung (Unterbrechung nachfolgender Inspektionen, wenn auf Elemente gestoßen wird, die die Überprüfung nicht bestehen)“ und „Einmalige Überprüfung ( alle Elemente auf einmal prüfen)" Zwei Modi, wechseln Sie zwischen den beiden Modi, indem Sie opts["submitCheck"]=true/false konfigurieren. * Unabhängig davon, ob die Überprüfung bestanden wurde oder nicht, wird eine Eingabeaufforderungsmeldung angezeigt, die den Erfolg oder Misserfolg des entsprechenden Überprüfungstyps anzeigt. Sie können den Parameter opts konfigurieren, um den Inhalt und die Stilinformationen der Eingabeaufforderung anzupassen . *Eingebaute Überprüfung gängiger Datentypen. Der Überprüfungstyp kann auch durch opts["customType"] und opts["customReg"] erweitert werden, was die Flexibilität der Formularüberprüfung erheblich verbessert.
*
Heng.prototype.formCheck = function (opts) { //opts={"customType":"无formCheck的class值","customReg":"必须的自定义类型的正则文本","customTip":"必须的自定义类型的错误提示"} //自定义校验类型用"formCheck-"+opts["customType"]作为class值 var def = { "user" : "*请输入3-16位字母数字", "password" : "*请输入5-17位以字母开头的字符数字组合", "email" : "*请输入正确邮箱地址", "Mobilephone" : "*请输入正确手机号", "radioBox" : "请选择", "ch" : "请输入中文字符", "wrongStyle" : "font-size:12px; color:#F00;", "passContent" : "成功", "passStyle" : "font-size:12px; color:#0C0;", "submitCheck" : false //提交时逐条验证还是一次验证显示所有错误信息。默认一次校验 }; opts = H.extend(def, opts); //dataType绑定到具体表单元素class值 var dataType = ["formCheck-user", "formCheck-password", "formCheck-email", "formCheck-mobilePhone", "formCheck-ch", "formCheck-radioBox"]; var Reg = { "user" : "^[a-zA-Z0-9_]{3,16}$", "password" : "^[a-zA-Z]+\w{5,17}$", "email" : "^([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[_|\_|\.]?)*[a-zA-Z0-9]+\.[a-zA-Z]{2,3}$", "mobilePhone" : "^(13+\d{9})|(159+\d{8})|(153+\d{8})$", "ch" : "[\u4e00-\u9fa5]" }; if (opts["customType"]) { Reg[opts["customType"]] = opts["customReg"]; opts[opts["customType"]] = opts["customTip"]; //需要放在formCheck-radioBox之前 dataType.splice((dataType.length - 2), 1, ("formCheck-" + opts["customType"])); } var This = this; for (var i = 0; i < this.nodes.length; i++) { var form = this.nodes[i]; form.nodes = []; //dataType匹配的表单元素集合的超集,保存在每个form下 for (var j = 0; j < dataType.length; j++) { //表单元素超集_二维数组; var resultArr = this.getClass(dataType[j], form); if (resultArr.length != 0) { resultArr.dataClass = dataType[j]; //将对应class值绑定在子数组上 form.nodes.push(resultArr); } } for (var k = 0; k < form.nodes.length; k++) { //绑定blur事件 (function () { if (form.nodes[k].dataClass != "formCheck-radioBox") { var regoptsKEY = form.nodes[k].dataClass.slice(form.nodes[k].dataClass.indexOf("-") + 1); var regTest = new RegExp(Reg[regoptsKEY]); for (var l = 0; l < form.nodes[k].length; l++) { form.nodes[k][l].onblur = function () { var wrongSpan = This.getClass("formCheck-wrong", this.parentNode)[0]; if (!regTest.test(this.value)) { wrongSpan.innerHTML = opts[regoptsKEY]; wrongSpan.style.cssText = def["wrongStyle"]; } else { wrongSpan.innerHTML = def["passContent"]; wrongSpan.style.cssText = def["passStyle"]; } } } } else { if (form.nodes[k].dataClass == "formCheck-radioBox") { for (var m = 0; m < form.nodes[k].length; m++) { var RBA = form.nodes[k][m]; (function (RBA) { //每组radio或checkBox的input集合,form.nodes[k][m].radioBoxArr三维数组 form.nodes[k][m].radioBoxArr = form.nodes[k][m].parentNode.getElementsByTagName("input"); for (var n = 0; n < form.nodes[k][m].radioBoxArr.length; n++) { if (form.nodes[k][m].radioBoxArr[n].checked) { var statePre = true; break; } else { var statePre = false; } form.nodes[k][m].state = statePre; form.nodes[k][m].radioBoxArr[n].onclick = function () { for (var n = 0; n < RBA.radioBoxArr.length; n++) { if (RBA.radioBoxArr[n].checked) { var statePre = true; break; } else { var statePre = false; } } RBA.state = statePre; var wrongSpan = This.getClass("formCheck-wrong", this.parentNode)[0]; if (RBA.state) { wrongSpan.innerHTML = opts["passContent"]; wrongSpan.style.cssText = opts["passStyle"]; } else { wrongSpan.innerHTML = opts["radioBox"]; wrongSpan.style.cssText = opts["wrongStyle"]; } } } })(RBA) } } } })() } (function (form) { form.onsubmit = function (e) { var e = e || window.event; for (var k = 0; k < form.nodes.length; k++) { var regoptsKEY = form.nodes[k].dataClass.slice(form.nodes[k].dataClass.indexOf("-") + 1); var regTest = new RegExp(Reg[regoptsKEY]); for (var l = 0; l < form.nodes[k].length; l++) { if (def["submitCheck"]) { if (form.nodes[k].dataClass != "formCheck-radioBox") { var wrongSpan = This.getClass("formCheck-wrong", form.nodes[k][l].parentNode)[0]; if (!regTest.test(form.nodes[k][l].value)) { wrongSpan.innerHTML = opts[regoptsKEY]; wrongSpan.style.cssText = def["wrongStyle"]; return false; //停止执行中断循环,阻止默认 } else { wrongSpan.innerHTML = def["passContent"]; wrongSpan.style.cssText = def["passStyle"]; } } else if (form.nodes[k].dataClass == "formCheck-radioBox") { var wrongSpan = This.getClass("formCheck-wrong", form.nodes[k][l].parentNode)[0]; if (form.nodes[k][l].state) { wrongSpan.innerHTML = opts["passContent"]; wrongSpan.style.cssText = opts["passStyle"]; } else { wrongSpan.innerHTML = opts["radioBox"]; wrongSpan.style.cssText = opts["wrongStyle"]; return false; } } } else { if (form.nodes[k].dataClass != "formCheck-radioBox") { var wrongSpan = This.getClass("formCheck-wrong", form.nodes[k][l].parentNode)[0]; if (!regTest.test(form.nodes[k][l].value)) { wrongSpan.innerHTML = opts[regoptsKEY]; wrongSpan.style.cssText = opts["wrongStyle"]; e.preventDefault(); e.returnValue = false; } else { wrongSpan.innerHTML = opts["passContent"]; wrongSpan.style.cssText = opts["passStyle"]; } } else if (form.nodes[k].dataClass == "formCheck-radioBox") { var wrongSpan = This.getClass("formCheck-wrong", form.nodes[k][l].parentNode)[0]; if (form.nodes[k][l].state) { wrongSpan.innerHTML = opts["passContent"]; wrongSpan.style.cssText = opts["passStyle"]; } else { wrongSpan.innerHTML = opts["radioBox"]; wrongSpan.style.cssText = opts["wrongStyle"]; e.preventDefault(); e.returnValue = false; } } } } } } })(form) } }H(".wrap").page(opts); der Seitencontainer kann die Paging-Steuerung implementieren, indem er den erforderlichen Parameter page übergibt und ihn instanziiert: *Gesamtzahl der Seiten pageSize = Gesamtzahl der Datenelemente in totalData ÷ Anzahl der Datenelemente auf jeder Seite von pageItems (der Quotient wird aufgerundet). Notwendige Konfigurationsdaten werden vom Server während der Initialisierung zurückgegeben. *pageSize *6≤pageSize *pageSize≥10 Die Seitenzahl wird basierend auf nowPage generiert. Unter Berücksichtigung, ob es obere und untere Seiten gibt, befindet sich nowPage am Anfang, Ende und in der Mitte von 2*3=6 Situationen.
*页码序数基于等差数列通项公式【An=a1+(n-1)*d】推导并输出在按钮data-page上,a1由pageBtns、pageSize确定。
*上下页、首尾页、当前页、禁用页、省略页按钮根据不同情况重写data-page和内容,并判断启用或者禁用,对比nowPage报告当前页等。
*监听容器点击操作修改nowPage并更新所有页码,回调函数opts["cb"]接收nowPage用于数据请求,需过滤无效页码按钮避免无效更新浪费性能。
*本控件充分利用事件委托,文档碎片createDocumentFragment()原理优化批量节点的事件处理和DOM操作,节约性能损耗。
Heng.prototype.page = function (opts) { /*opts={"totalData":总数据条数(后台取),"pageItems":每页数据条数(后台取),"pageSize":总页数","pageBtns":所有按钮总数(含省略号上下页), "preNext":有无上下页,"nowPage":当前页码,"active":激活样式class,"disable",禁用样式class,"headText":"首页","endText":"尾页","cb":回调函数} "preText":上一页按钮文本,"nextText":"下一页按钮文本"*/ /*总页数=总数据条数÷每页数据条数并向上取整 && pageSize>=6基于nowPage生成页码时有上下页pageBtns>=10最佳 && 无上下页pageBtns>= 6最佳 && pageSize<6时基于pageSize生成页码强制pageSize==pageBtns*/ var def = { "pageSize" : Math.ceil(opts["totalData"] / opts["pageItems"]), "preNext" : true, "preText" : "上一页", "nextText" : "下一页", "nowPage" : 1, "headText" : "1", "endText" : opts["pageSize"], "pageBtns" : 10 }; opts = H.extend(def, opts); var isHead, isEnd, isHeadPN, isEndPN, frag = document.createDocumentFragment(); for (var i = 0; i < this.nodes.length; i++) { var wrap = this.nodes[i]; int(); wrap.onclick = function (e) { //点击更新页码,确定当前页,执行回调函数 var e = e || window.event; var target = e.target || e.srcElement; if (target.getAttribute("data-page") && target.getAttribute("data-page") != opts["nowPage"]) { //全部页码基于nowPage及其所在位置生成,更新nowPage才能更新页码 opts["nowPage"] = parseInt(target.getAttribute("data-page")); int(); } else if (target.getAttribute("data-page") && target.getAttribute("data-page") == opts["nowPage"]) { opts["cb"] && opts["cb"](opts["nowPage"]); } else if (!target.getAttribute("data-page")) { return false } } wrap.onselectstart = function () { return false } } function int() { wrap.innerHTML && (wrap.innerHTML = null); if (opts["pageSize"] < 10 && opts["pageSize"] >= 6) { opts["preNext"] = false; opts["pageBtns"] = 6; } else if (opts["pageSize"] < 6) { static(); opts["pageSize"]; } if (opts["preNext"]) { //循环外判断一次即可 if (opts["pageBtns"] % 2 == 1) { isHeadPN = opts["nowPage"] <= (opts["pageBtns"] + 1) / 2; isEndPN = opts["nowPage"] >= opts["pageSize"] + 1 - (opts["pageBtns"] - 1) / 2; } if (opts["pageBtns"] % 2 == 0) { isHeadPN = opts["nowPage"] <= opts["pageBtns"] / 2; isEndPN = opts["nowPage"] >= [opts["pageSize"] + 1 - (opts["pageBtns"] / 2)] } } else { if (opts["pageBtns"] % 2 == 1) { isHead = opts["nowPage"] <= (opts["pageBtns"] + 1) / 2; isEnd = opts["nowPage"] >= [opts["pageSize"] - (opts["pageBtns"] - 1) / 2]; } if (opts["pageBtns"] % 2 == 0) { isHead = opts["nowPage"] <= (opts["pageBtns"] / 2); isEnd = opts["nowPage"] >= [opts["pageSize"] - opts["pageBtns"] / 2] } } if (opts["pageSize"] >= 6) { for (var j = 1; j <= opts["pageBtns"]; j++) { var oSp = document.createElement("span"); if (opts["preNext"] && (opts["pageSize"] >= 10)) { if (opts["pageBtns"] < 10) { opts["pageBtns"] = 10; }; if (isHeadPN) { //nowPage靠近头部 oSp.innerHTML = j - 1; oSp.setAttribute("data-page", j - 1); if (j == opts["pageBtns"] - 2) { oSp.innerHTML = "…"; //重写倒数第三项 oSp.removeAttribute("data-page"); } } else if (isEndPN) { //nowPage靠近尾部 oSp.innerHTML = opts["pageSize"] - opts["pageBtns"] + j + 1; oSp.setAttribute("data-page", opts["pageSize"] - opts["pageBtns"] + j + 1) if (j == 3) { oSp.innerHTML = "…"; //重写第三项 oSp.removeAttribute("data-page"); } } else { //nowPage中间,页码基于nowPage生成 middle(); if (j == opts["pageBtns"] - 2 || j == 3) { oSp.innerHTML = "…"; //重写第三项和倒数第三项 oSp.removeAttribute("data-page"); } } //重写所有的第一项和倒数第一项 if (j == 1) { oSp.innerHTML = opts["preText"]; if (opts["nowPage"] == 1) { oSp.setAttribute("class", opts["disable"]); oSp.removeAttribute("data-page"); } else { oSp.setAttribute("data-page", opts["nowPage"] - 1); } } else if (j == opts["pageBtns"]) { oSp.innerHTML = opts["nextText"]; if (opts["nowPage"] == opts["pageSize"]) { oSp.setAttribute("class", opts["disable"]); oSp.removeAttribute("data-page"); } else { oSp.setAttribute("data-page", opts["nowPage"] + 1); } } //重写所有的第二项和倒数第二项 if (j == 2) { oSp.innerHTML = opts["headText"]; oSp.setAttribute("data-page", 1) } else if (j == opts["pageBtns"] - 1) { oSp.innerHTML = opts["endText"]; oSp.setAttribute("data-page", opts["pageSize"]) } } else if (!opts["preNext"] && (opts["pageSize"] >= 6)) { if (opts["pageBtns"] < 6) { opts["pageBtns"] = 6 }; if (isHead) { oSp.innerHTML = j; oSp.setAttribute("data-page", j); if (j == opts["pageBtns"] - 1) { oSp.innerHTML = "…"; oSp.removeAttribute("data-page"); } } else if (isEnd) { oSp.innerHTML = opts["pageSize"] - opts["pageBtns"] + j; oSp.setAttribute("data-page", opts["pageSize"] - opts["pageBtns"] + j) if (j == 2) { oSp.innerHTML = "…"; oSp.removeAttribute("data-page"); } } else { middle(); if (j == opts["pageBtns"] - 1 || j == 2) { oSp.innerHTML = "…"; oSp.removeAttribute("data-page"); } } //重写第一项和倒数第一项 if (j == 1) { oSp.innerHTML = opts["headText"]; oSp.setAttribute("data-page", 1) } else if (j == opts["pageBtns"]) { oSp.innerHTML = opts["endText"]; oSp.setAttribute("data-page", opts["pageSize"]) } } if (oSp.getAttribute("data-page") === opts["nowPage"].toString()) { oSp.setAttribute("class", opts["active"]); } oSp.style.cursor = "pointer"; (!oSp.getAttribute("data-page")) && (oSp.style.cursor = "auto"); frag.appendChild(oSp); } //for wrap.appendChild(frag); } function middle() { if (opts["pageBtns"] % 2 == 1) { oSp.innerHTML = opts["nowPage"] - [(opts["pageBtns"] + 1) / 2] + j; oSp.setAttribute("data-page", opts["nowPage"] - [(opts["pageBtns"] + 1) / 2] + j) } else if (opts["pageBtns"] % 2 == 0) { oSp.innerHTML = opts["nowPage"] - (opts["pageBtns"] / 2) + j; oSp.setAttribute("data-page", opts["nowPage"] - (opts["pageBtns"] / 2) + j) } } function static() { for (var k = 1; k <= opts["pageSize"]; k++) { var oSp = document.createElement("span"); oSp.innerHTML = k; oSp.setAttribute("data-page", k); frag.appendChild(oSp); if (oSp.getAttribute("data-page") === opts["nowPage"].toString()) { oSp.setAttribute("class", opts["active"]); } } wrap.appendChild(frag); } opts["cb"] && opts["cb"](opts["nowPage"]) //报告当前页发送请求用 } //int }
*
H("p").on ("Tap",fn/{"Tap":fn,"Hold":fn});自定义事件处理程序实现触控手势操作:
*触控手势不属于系统事件,需一套在数据层管理事件及函数添加、销毁、遍历执行的机制,模拟系统原生add/removeEventListener方法的功能。
*触控手势事件的触发依赖系统touchstart/touchmove/touchend,触点ID号identifier跟踪同一手指。通过e.data报告手势状态。
*Tap:屏幕停留时间小于250ms,因不可避免手指抖动、力度不均影响触点状态,手指离屏时坐标偏移需小于阀值(水平:10px,垂直:5px)。
*Pinch:手指移动中水平或垂直方向坐标相对进屏时偏移大于10px时可触发,两根手指水平垂直偏移量4个值中的最大者作为缩放系数e.data.k。
*Hold:手指在屏幕停留时间大于500ms,如果提前离开或者手指偏移量大于阀值(水平:10px,垂直:5px)则停止定时器不触发。
*swipe:手指需在1000ms内连续(中途不反向)移动至少30px,对比进出屏触点坐标水平垂直偏移量,取较大者来确定e.data报告滑动方向和距离。
/*自定义事件处理程序*/ H.addEvent = function (type, handler) { this.handlers = {}; //{type1:[fn],type2:[fn]} if (typeof this.handlers[type] == "undefined") { this.handlers[type] = []; } this.handlers[type].push(handler); }; H.fireEvent = function (type, data) {//调用对应类型函数触发事件 if (this.handlers[type]instanceof Array) { var arrayEvent = this.handlers[type]; for (var i = 0, len = arrayEvent.length; i < len; i++) { if (typeof arrayEvent[i] === "function") { arrayEvent[i](data); } } } }; H.removeEvent = function (type, handler) { if (this.handlers[type]instanceof Array) { var arrayEvent = this.handlers[type]; for (var i = 0, len = arrayEvent.length; i < len; i++) { if (arrayEvent[i] === handler) { arrayEvent.splice(i, 1); break; } } } } /*触控手势实现和事件绑定*/ Heng.prototype.on = function (handle, fn) { //调用方式(type,fn) 或 ({type1:fn1,type2:fn2}) for (var i = 0; i < this.nodes.length; i++) { var node = this.nodes[i]; var iStouch = false; var hasTap = false, hasPinch = false, hasHold = false, hasSwipeLeft = false, hasSwipeRight = false; left = true, right = true, up = true, down = true; var inTime, outTime, touchID, touchID1, inX, inY, inX1, inY1, outX, outY, moveX, moveY, moveX1, moveY1, disX, disY, disX1, disY1, t; if (arguments.length == 1) { for (var k in handle) { if ((k === "Tap") || (k === "Pinch") || (k === "Hold") || (k === "Swipe")) { iStouch = true; //触控和鼠标事件不和共用 this.iStouch = iStouch; checkType(k); } } } else { iStouch = (handle === "Tap") || (handle === "Pinch") || (handle === "Hold") || (handle === "Swipe"); this.iStouch = iStouch; checkType(handle); } if (iStouch) { H.bind(node, "touchstart", tsFn); H.bind(node, "touchend", teFn); if (!hasTap) { H.bind(node, "touchmove", tmFn); } if (arguments.length == 1) { for (var j in handle) { H.addEvent(j, handle[j]); } } else { H.addEvent(handle, fn); } } else { if (arguments.length == 1) { for (var j in handle) { H.bind(node, j, handle[j]); } } else { H.bind(node, handle, fn) } } } function checkType(x) { switch (x) { case "Tap": hasTap = true; break; case "Pinch": hasPinch = true; break; case "Hold": hasHold = true; break; case "swipeLeft": hasSwipe = true; break; } } function tsFn(e) { touchID = e.changedTouches[0].identifier; inTime = new Date().getTime(); inX = e.changedTouches[0].clientX; inY = e.changedTouches[0].clientY; if (e.changedTouches[1]) { touchID1 = e.changedTouches[1].identifier; inX1 = e.changedTouches[1].clientX; inY1 = e.changedTouches[1].clientY; } if (hasHold) { if (e.targetTouches.length === 1 && e.changedTouches[0].identifier === touchID) { t = window.setTimeout(function () { H.fireEvent("Hold", e); }, 500) } } } function tmFn(e) { if (hasHold) { if ([Math.abs(moveY - inY) >= 5] && [Math.abs(moveX - inX) >= 10]) { window.clearTimeout(t); } } else if (hasPinch && e.targetTouches.length === 2 && e.changedTouches[1].identifier === touchID1 && e.changedTouches[0].identifier === touchID) { e.preventDefault(); disX = Math.abs(inX1 - inX); disY = Math.abs(inY1 - inY); disX1 = Math.abs(moveX1 - moveX); disY1 = Math.abs(moveY1 - moveY); if ((Math.abs(disX - disX1) >= 10) || (Math.abs(disY - disY1) >= 10)) { e.data.k = (Math.abs(disX - disX1) > Math.abs(disY - disY1)) ? (disX1 / disX) : (disY1 / disY); //缩放因子 H.fireEvent("Pinch", e); } } else if (hasSwipe && e.targetTouches.length === 2) { if (e.changedTouches[0].clientX >= moveX) { left = true } //对比相邻两次的移动判断是否中途反向 else { right = false } if (e.changedTouches[0].clientY >= moveY) { up = true } else { down = false } e.preventDefault(); } moveX = e.changedTouches[0].clientX; moveY = e.changedTouches[0].clientY; if (e.changedTouches[1]) { moveX1 = e.changedTouches[1].clientX; moveY1 = e.changedTouches[1].clientY; } } function teFn(e) { outTime = new Date().getTime(); outX = e.changedTouches[0].clientX; outY = e.changedTouches[0].clientY; if (hasTap && e.targetTouches.length === 1 && e.changedTouches[0].identifier === touchID) { if ([(outTime - inTime) <= 250] && [Math.abs(outY - inY) <= 5] && [Math.abs(outX - inX) <= 10]) { H.fireEvent("Tap", e); } } else if (hasHold && (outTime - inTime) <= 500) { window.clearTimeout(t); } else if (hasSwipe && e.targetTouches.length === 1 && e.changedTouches[0].identifier === touchID) { if (Math.abs(outX - inX) >= Math.abs(outY - inY)) { e.data.dis = Math.abs(outX - inX); if (outX >= inX) { e.data.direction = "right" } else { e.data.direction = "left" } } else { e.data.dis = Math.abs(outY - inY); if (outY >= inY) { e.data.direction = "down" } else { e.data.direction = "up" } } if ((outTime - inTime) <= 1000 && (Math.abs(outY - inY) >= 30 || Math.abs(outX - inX) >= 30)) { if ((left && right) || (up && down)) { //保证中途连续不反向 H.fireEvent("swipe", e); } } } } return this; };
*
*【静态方法案例】
H.ajaxJsonp(opts);数据请求包含ajax和jsonp两大模块:
*jsonp:基于src跨域特性动态插入script标签发送get请求远程调用本地同名函数opts["jsonpCBname"],数据以函数传参为载体传回本地。
*发送数据opts['data']自动序列化(支持Array、Object、String等类型);通过success获取ajax或jsonp请求结果并自动解析。
*post必须定义Content-Type类型,默认setRequestHeader("Content-Type","application/x-www-form-urlencoded)。
*区分同步异步请求过程,并处理了success、error、progress、complete等四个状态事件。
*若opts['dataType']=jsonp将切换为jsonp 请求,其它值为ajax,opts['data']自动拼接到get请求地址或推入send()后post数据。
*jsonp回调函数名默认随机生成并自动发送,执行回调后立即清除本次生成的script标签防止无限插入,同时清空回调释放内存。
/*ajax and jsonp*/ H.ajaxJsonp = function (opts) { /*opts={"method":"GET/POST","url":地址,"async":布尔,"data":数组对象(内部序列化)或已序列化的字符串,"cache":是否禁用缓存, "dataType":期望返回数据类型(如果取值jsonp将进行JSONP请求),"jsonp":后台获取回调函数名的键名默认callback, "jsonpCBname":回调函数名默认随机生成,"time":超时,"contentType":发送文件类型, "success":fn(response)与jsonp请求共用回调函数,"error":fn(status, statusText),"progress":fn,"complete":fn}*/ //返回结果已内部解析 var def = { "method" : "POST", "async" : true, "data" : null, "cache" : true, "time" : 800, "dataType" : "text", "contentType" : "application/x-www-form-urlencoded", "jsonp" : "callback" }; for (var p in opts) { def[p] = opts[p]; }; opts = def; var dataStr = ""; if (opts['data']) { dataType = Object.prototype.toString.call(opts['data']).toLowerCase(); if (dataType == "[object array]") { for (var i = 0; i < opts['data'].length; i++) { opts['data'][i] = "" + i + "=" + encodeURIComponent(opts['data'][i]); } dataStr = opts['data'].join("&"); } else if (dataType == "[object object]") { for (x in opts['data']) { dataStr += "&" + x + "=" + encodeURIComponent(opts['data'][x]); } } else if (dataType == "[object string]") { /*需为序列化字符串*/ dataStr = opts['data']; } } if (opts['dataType'] == 'jsonp') { //jsonp请求 var a = Math.floor(Math.random() * 26) + "a".charCodeAt(0); var b = Math.floor(Math.random() * 26) + "a".charCodeAt(0); var c = Math.floor(Math.random() * 26) + "a".charCodeAt(0); var d = Math.floor(Math.random() * 26) + "a".charCodeAt(0); opts["jsonpCBname"] = opts["jsonpCBname"] || String.fromCharCode(a, b, c, d); if (opts['url'].indexOf('?') === -1) { opts['url'] += '?' + opts['jsonp'] + '=' + opts['jsonpCBname'] + '&' + dataStr; } else { opts['url'] += '&' + opts['jsonp'] + '=' + opts['jsonpCBname'] + '&' + dataStr; } if (opts['cache']) { opts['url'] += '&cache=' + Date.now(); } var script = document.createElement('script'); script.id = "" + opts["jsonpCBname"]; script.src = opts['url']; document.getElementsByTagName('head')[0].appendChild(script); window[opts["jsonpCBname"]] = function (response) { //服务器调用的函数 opts['success'] && opts['success'](response); document.getElementById(opts["jsonpCBname"]).parentNode.removeChild(document.getElementById(opts["jsonpCBname"])); window[opts["jsonpCBname"]] = null; } } else { //Ajax请求 var xhr = new XMLHttpRequest(); if (opts.method === 'get') { opts.url += opts.url.indexOf('?') == -1 ? '?' + dataStr : '&' + dataStr; } if (opts.async === true) { xhr.onreadystatechange = function () { if (xhr.readyState == 0) { opts.progress(); } else if (xhr.readyState == 4) { callback(); opts.complete(); } }; } xhr.open(opts.method, opts.url, opts.async); if (opts["cache"]) { xhr.setRequestHeader("Cache-Control", "no-cache"); xhr.setRequestHeader("If-Modified-Since", "0"); } xhr.responseType = opts["dataType"]; xhr.timeout && (xhr.timeout = opts["time"]) if (opts.method === 'post') { //实际发送文件类型POST必须 xhr.setRequestHeader("Content-Type", opts["contentType"]); xhr.send(dataStr); } else { xhr.send(null); } if (opts.async === false) { callback(); opts.complete(); } function callback() { var response; if (xhr.status == 200) { try { response = JSON.parse(xhr.responseText); } catch (er) { response = eval("(" + xhr.responseText + ")"); } opts.success(response); } else { opts.error(xhr.status, xhr.statusText); } } } //ajax End }
相关推荐:
Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung des Front-End-Frameworks Heng.js. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!