프레임워크 설명: 객체 지향 프로그래밍 아이디어를 기반으로 하고 기본 js 언어로 구현되어 타사 js 라이브러리에 의존하지 않으며 외부 코드 조각을 참조하지 않습니다! 프레임워크의 유연성과 견고성을 보장하기 위해 다양한 애플리케이션 시나리오를 완전히 고려하십시오.
* 구현 기능: jQuery와 완전히 일치하는 아키텍처 및 내부 세부 사항을 구현합니다. 설정 작업의 내부 루프는 일치하는 모든 노드를 설정할 수 있으며 메서드는 체인에서 호출될 수 있습니다.
* 메소드 분류: 정적 메소드는 데이터 로직, DOM 전처리 및 기타 전역 비 DOM 작업을 위해 프레임워크 내에서 사용되며 해당 UI 구성요소를 호출합니다.
* 매개변수 구성: 실제 필요에 따라 다양한 매개변수를 구성하고 유연하게 호출합니다. 매개변수가 없는 경우 기본 구성을 사용합니다.
* 선택기 지원: ID, 클래스, 라벨, 하위 선택기, dom 노드, 인스턴스화된 객체 H("css 선택기").
* 호환성: 모든 방법은 브라우저 간 완벽하게 호환되며 대부분의 최신 브라우저(IE6/7/8/9/10/11, Chrome, Firefox, Safari 등)와 호환됩니다.
* 변수 안전성: 클로저 환경에서 프레임워크 자체 변수의 안전성이 보장되고 변수 충돌이 방지됩니다.
* 프레임워크 성능: 코드를 최적화하여 성능을 향상시키고 브라우저 리플로우 및 다시 그리기를 최소화합니다. null 값을 할당하여 메모리를 해제합니다. DOM, BOM 및 ECMAScript 간의 아일랜드 통신을 방지합니다. 문서 조각을 기반으로 DOM 작업을 수행하여 컬렉션 이벤트를 처리합니다...
*
* [호출]
* 정적 메서드: H. Method(value)
* 인스턴스 메소드: H("css selector").Method(value), H("css selector").Method({key:value})
* H(sel).Method({ 속성 이벤트 구성}), H(sel).Method(컴포넌트 메소드, 메소드 매개변수)
* 체인 호출: H("css selector").Method({key:value}).Method()
* DOM 트리 로드 후 실행: H(fn)은 jQuery 라이브러리의 $(fn)===$(document).ready(fn)
과 동일합니다. * 내부 루프: 인스턴스 메소드는 내부적으로 H("css selector")와 일치합니다. 노드 컬렉션은 내부 루프를 수행합니다. 일치하는 모든 노드를 동시에 작동할 수 있습니다
* 속성 메소드: opts는 컴포넌트 속성 이벤트를 설정합니다. 컴포넌트 메소드 메소드(컴포넌트 메소드, 매개변수)
* 제스처 터치: 모바일 단말기에 대한 터치 제스처 이벤트(Tap)가 캡슐화됩니다. , Hold, Swipe)을 수행하고 e.data를 통해 해당 제스처의 상태를 보고합니다. 핀치 줌 계수, 슬라이딩 방향, 슬라이딩 거리 등
*
* 작성자: wheng
* 날짜: 2015-07-25
*/
* 프레임워크 소스코드 http://whwheng.gitee.io/csdn
*
*[구조적 간략한 설명]
* 프레임워크의 본체는 폐쇄 환경에서 자체 안전을 보장하고 다양한 충돌을 방지하며 H 또는 Heng을 통해 기본 클래스를 외부로 참조합니다.
(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)
*생성자에서는 "공용 데이터 귀속" 규칙에 따라 DOM 노드의 기능을 얻고 노드 속성에 노드를 저장하기 위해 "css 선택기"가 구현됩니다.
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; } } }
*정적 메서드는 인스턴스화가 필요하지 않으며 프레임워크 내에서 데이터 논리 및 DOM 전처리 또는 기타 전역 비DOM 작업에 사용됩니다. sel 일치 구성 요소 항목이 있는 경우 인스턴스 메서드는 DOM 노드와 결합됩니다. 설정하면 모든 노드가 메소드의 논리와 기능을 실현할 수 있습니다.
//静态方法 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'); …… } }
*객체 지향 프로그래밍은 필연적으로 이 포인터에 대한 혼란을 야기합니다. 모든 유사한 문제의 경우 클로저를 통해 함수 외부에 var This=this를 선언하세요.
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 id="Hello-nbsp-World">Hello World</h1>" };//def为默认值 opts = H.extend(def, opts);//数据合并 …… }
*
*[인스턴스 메소드 사례]
H("form").formCheck(); 한 줄의 코드로 페이지의 모든 폼 검증을 완료하며, 기능은 다음과 같습니다.
*H(" form") 일치 양식 내의 모든 유형의 모든 양식 요소가 확인됩니다.
*각 양식 요소는 포커스를 잃으면 독립적으로 유효성이 검사됩니다.
*양식 제출 시 내부의 모든 요소를 균일하게 검증하며, '일대일 검증(검증에 실패한 요소 발견 시 후속 점검 중단)'과 '일회성 검증(모든 요소 확인)' 두 가지 모드로 구분됩니다. 즉시)". opts["submitCheck"]=true/false를 구성하여 두 모드 사이를 전환합니다.
*인증 통과 여부에 관계없이 해당 인증 유형의 성공 또는 실패를 나타내는 프롬프트 메시지가 표시됩니다. opts 매개변수를 구성하여 프롬프트의 내용과 스타일 정보를 맞춤 설정할 수 있습니다.
*일반 데이터 유형에 대한 검증 기능이 내장되어 있으며 opts["customType"] 및 opts["customReg"]를 통해 검증 유형을 확장할 수도 있어 양식 검증의 유연성이 크게 향상됩니다.
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); 페이지 컨테이너는 페이징 제어를 인스턴스화하기 위해 필수 매개변수 페이지를 전달합니다.
*총 페이지 수 pageSize=totalDataTotal 데이터 항목 수 ¶pageItems per 페이지 데이터 항목 수입니다(몫은 반올림됨). 필요한 구성 데이터는 초기화 중에 서버에서 반환됩니다.
*pageSize
*6≤pageSize
*pageSize≥10 nowPage를 기준으로 페이지 번호가 생성되며, 상위 페이지와 하위 페이지가 있는지 고려하면 nowPage는 시작, 끝, 중간 2*3=6 상황에 있습니다.
*页码序数基于等差数列通项公式【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 }
相关推荐:
위 내용은 프론트엔드 프레임워크 Heng.js에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

PHP 유형은 코드 품질과 가독성을 향상시키기위한 프롬프트입니다. 1) 스칼라 유형 팁 : PHP7.0이므로 int, float 등과 같은 기능 매개 변수에 기본 데이터 유형을 지정할 수 있습니다. 2) 반환 유형 프롬프트 : 기능 반환 값 유형의 일관성을 확인하십시오. 3) Union 유형 프롬프트 : PHP8.0이므로 기능 매개 변수 또는 반환 값에 여러 유형을 지정할 수 있습니다. 4) Nullable 유형 프롬프트 : NULL 값을 포함하고 널 값을 반환 할 수있는 기능을 포함 할 수 있습니다.

PHP에서는 클론 키워드를 사용하여 객체 사본을 만들고 \ _ \ _ Clone Magic 메소드를 통해 클로닝 동작을 사용자 정의하십시오. 1. 복제 키워드를 사용하여 얕은 사본을 만들어 객체의 속성을 복제하지만 객체의 속성은 아닙니다. 2. \ _ \ _ 클론 방법은 얕은 복사 문제를 피하기 위해 중첩 된 물체를 깊이 복사 할 수 있습니다. 3. 복제의 순환 참조 및 성능 문제를 피하고 클로닝 작업을 최적화하여 효율성을 향상시키기 위해주의를 기울이십시오.

PHP는 웹 개발 및 컨텐츠 관리 시스템에 적합하며 Python은 데이터 과학, 기계 학습 및 자동화 스크립트에 적합합니다. 1.PHP는 빠르고 확장 가능한 웹 사이트 및 응용 프로그램을 구축하는 데 잘 작동하며 WordPress와 같은 CMS에서 일반적으로 사용됩니다. 2. Python은 Numpy 및 Tensorflow와 같은 풍부한 라이브러리를 통해 데이터 과학 및 기계 학습 분야에서 뛰어난 공연을했습니다.

HTTP 캐시 헤더의 주요 플레이어에는 캐시 제어, ETAG 및 최종 수정이 포함됩니다. 1. 캐시 제어는 캐싱 정책을 제어하는 데 사용됩니다. 예 : 캐시 제어 : Max-AGE = 3600, 공개. 2. ETAG는 고유 식별자를 통해 리소스 변경을 확인합니다. 예 : ETAG : "686897696A7C876B7E". 3. Last-modified는 리소스의 마지막 수정 시간을 나타냅니다. 예 : 마지막으로 변형 : Wed, 21oct201507 : 28 : 00GMT.

PHP에서 Password_hash 및 Password_Verify 기능을 사용하여 보안 비밀번호 해싱을 구현해야하며 MD5 또는 SHA1을 사용해서는 안됩니다. 1) Password_hash는 보안을 향상시키기 위해 소금 값이 포함 된 해시를 생성합니다. 2) Password_verify 암호를 확인하고 해시 값을 비교하여 보안을 보장합니다. 3) MD5 및 SHA1은 취약하고 소금 값이 부족하며 현대 암호 보안에는 적합하지 않습니다.

PHP는 동적 웹 개발 및 서버 측 응용 프로그램에 사용되는 서버 측 스크립팅 언어입니다. 1.PHP는 편집이 필요하지 않으며 빠른 발전에 적합한 해석 된 언어입니다. 2. PHP 코드는 HTML에 포함되어 웹 페이지를 쉽게 개발할 수 있습니다. 3. PHP는 서버 측 로직을 처리하고 HTML 출력을 생성하며 사용자 상호 작용 및 데이터 처리를 지원합니다. 4. PHP는 데이터베이스와 상호 작용하고 프로세스 양식 제출 및 서버 측 작업을 실행할 수 있습니다.

PHP는 지난 수십 년 동안 네트워크를 형성했으며 웹 개발에서 계속 중요한 역할을 할 것입니다. 1) PHP는 1994 년에 시작되었으며 MySQL과의 원활한 통합으로 인해 개발자에게 최초의 선택이되었습니다. 2) 핵심 기능에는 동적 컨텐츠 생성 및 데이터베이스와의 통합이 포함되며 웹 사이트를 실시간으로 업데이트하고 맞춤형 방식으로 표시 할 수 있습니다. 3) PHP의 광범위한 응용 및 생태계는 장기적인 영향을 미쳤지 만 버전 업데이트 및 보안 문제에 직면 해 있습니다. 4) PHP7의 출시와 같은 최근 몇 년간의 성능 향상을 통해 현대 언어와 경쟁 할 수 있습니다. 5) 앞으로 PHP는 컨테이너화 및 마이크로 서비스와 같은 새로운 도전을 다루어야하지만 유연성과 활발한 커뮤니티로 인해 적응력이 있습니다.

PHP의 핵심 이점에는 학습 용이성, 강력한 웹 개발 지원, 풍부한 라이브러리 및 프레임 워크, 고성능 및 확장 성, 크로스 플랫폼 호환성 및 비용 효율성이 포함됩니다. 1) 배우고 사용하기 쉽고 초보자에게 적합합니다. 2) 웹 서버와 우수한 통합 및 여러 데이터베이스를 지원합니다. 3) Laravel과 같은 강력한 프레임 워크가 있습니다. 4) 최적화를 통해 고성능을 달성 할 수 있습니다. 5) 여러 운영 체제 지원; 6) 개발 비용을 줄이기위한 오픈 소스.


핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

WebStorm Mac 버전
유용한 JavaScript 개발 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

에디트플러스 중국어 크랙 버전
작은 크기, 구문 강조, 코드 프롬프트 기능을 지원하지 않음

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

VSCode Windows 64비트 다운로드
Microsoft에서 출시한 강력한 무료 IDE 편집기
