摘要:從本人這兩個(gè)月移動(dòng)實(shí)踐的經(jīng)驗(yàn)來(lái)看,微信的里面和的滑動(dòng)效果無(wú)論是在安卓還是下的體驗(yàn)都很一般,有明顯的卡頓現(xiàn)象,在安卓下面還會(huì)出現(xiàn)滑動(dòng)過(guò)快的時(shí)候在頁(yè)面停下來(lái)之后滾動(dòng)條才閃到相應(yīng)位置的現(xiàn)象。
前言
在微信里面瀏覽頁(yè)面的時(shí)候,有一個(gè)很管用的方法可以區(qū)分這個(gè)頁(yè)面是原生的還是H5形式的。隨便打開(kāi)一個(gè)頁(yè)面,用力往下扯的時(shí)候,如果頁(yè)面上方出現(xiàn)了“黑底”,黑底上有一行諸如網(wǎng)頁(yè)由game.weixin.qq.com提供的文字,就表明這個(gè)頁(yè)面是H5形式的。這帶來(lái)的問(wèn)題是,如果一個(gè)頁(yè)面可滾動(dòng)區(qū)域很小,隨便一拉,頁(yè)面下方出現(xiàn)了黑底,然后你又輕輕往上一拉,上面的黑底又出來(lái)了,個(gè)人表示非常難受啊!
于是乎,折騰了一番,寫(xiě)了一個(gè)簡(jiǎn)單的組件來(lái)實(shí)現(xiàn)禁止這種拉動(dòng)頁(yè)面出現(xiàn)黑底的特性。
首先需要說(shuō)明的是,由于Android和IOS的webview存在差異,這個(gè)組件對(duì)于IOS是比較友好的,安卓下并不能做到完美避免,下面一一分析。
簡(jiǎn)述touch事件智能手機(jī)和平板電腦一類(lèi)的移動(dòng)設(shè)備通常會(huì)有一個(gè)電容式觸摸屏(capacitive touch-sensitive screen),以捕捉用戶的手指所做的交互。有三種在規(guī)范中列出并獲得跨移動(dòng)設(shè)備廣泛實(shí)現(xiàn)的基本觸摸事件:
touchstart :手指放在一個(gè)DOM元素上
touchmove :手指拖曳一個(gè)DOM元素
touchend :手指從一個(gè)DOM元素上移開(kāi)
其中每一個(gè)觸摸事件都會(huì)包含三個(gè)觸摸列表:
touches :當(dāng)前位于屏幕上的所有手指的一個(gè)列表。
targetTouches :位于當(dāng)前DOM元素上的手指的一個(gè)列表。
changedTouches :涉及當(dāng)前事件的手指的一個(gè)列表。
這些列表由包含了觸摸信息的對(duì)象組成:
identifier :一個(gè)數(shù)值,唯一標(biāo)識(shí)觸摸會(huì)話(touch session)中的當(dāng)前手指。
target :DOM元素,是動(dòng)作所針對(duì)的目標(biāo)。
客戶/頁(yè)面/屏幕坐標(biāo) :動(dòng)作在屏幕上發(fā)生的位置。
半徑坐標(biāo)和 rotationAngle :畫(huà)出大約相當(dāng)于手指形狀的橢圓形。
在jsfiddle里面寫(xiě)一個(gè)簡(jiǎn)單的小demo就一目了然了:
http://jsfiddle.net/yuanzm/ws9j4v1v/2/
在這個(gè)組件中,我們只需要用到e.touches[0].clientY屬性就夠了:在開(kāi)始觸摸的時(shí)候,記錄觸摸點(diǎn)的起始位置,在手指移動(dòng)過(guò)程中,不斷獲取最新的clientY,與起始位置的clientY比較,就能獲知拉動(dòng)頁(yè)面的方向。
與height相關(guān)的幾個(gè)屬性scrollHeight: 是計(jì)量元素內(nèi)容高度的只讀屬性,包括overflow樣式屬性導(dǎo)致的視圖中不可見(jiàn)內(nèi)容。沒(méi)有垂直滾動(dòng)條的情況下,scrollHeight值與元素視圖填充所有內(nèi)容所需要的最小值clientHeight相同。包括元素的padding,但不包括元素的margin.
offsetHeight:是一個(gè)只讀屬性,它返回該元素的像素高度,高度包含該元素的垂直內(nèi)邊距和邊框,且是一個(gè)整數(shù)。
scrollTop:設(shè)置獲取讀取元素向上滾動(dòng)了多少像素。對(duì)于可滾動(dòng)的元素,這個(gè)值是可見(jiàn)區(qū)域頂部和不可見(jiàn)區(qū)域頂部的距離。如果元素不能滾動(dòng),這個(gè)值默認(rèn)為0。
這三個(gè)屬性是用來(lái)計(jì)算元素處于頁(yè)面的哪個(gè)位置的,考慮下面兩種情況:
元素的offsetHeight大于等于scrollHeight,無(wú)縱向滾動(dòng)條出現(xiàn),這個(gè)元素是不能滾動(dòng)的。如果一個(gè)元素不能滾動(dòng)了,就會(huì)嘗試外層的元素能不能滾動(dòng),一層一層往外冒泡。在webview里面,最外面一層就是這個(gè)webview容器了,按照微信的設(shè)置,這一層的“滾動(dòng)”就是露出下面的黑底。所以為了避免露出黑底,我們要在當(dāng)前元素不能滾動(dòng)的時(shí)候及時(shí)禁止掉冒泡,這樣就不會(huì)觸發(fā)到上一層的滾動(dòng)。
如果一個(gè)元素設(shè)置了高度,并且設(shè)置了overflow: scroll,當(dāng)元素內(nèi)的內(nèi)容可滾動(dòng)的時(shí)候,scrollHeight的值就會(huì)明顯大于offsetheight,那我們?cè)趺磁袛嘣貎?nèi)的內(nèi)容下拉到底部了呢?這就需要綜合offsetHeight和scrollTop的值了,如果offsetHeight的值加上srcollTop的值大于等于scrollHeight的值,就表明內(nèi)容已經(jīng)滑動(dòng)底部了。和第一點(diǎn)一樣,當(dāng)我們知道了臨界條件后,及時(shí)阻止掉冒泡就ok了。
結(jié)合touch和height屬性通過(guò)上面兩點(diǎn),我們已經(jīng)知道要達(dá)到禁止出現(xiàn)黑底的效果,努力的方向是在知道滑動(dòng)方向的條件下,在與height相關(guān)的屬性達(dá)到臨界值的時(shí)候及時(shí)阻止事件冒泡。只有三種簡(jiǎn)單的情況:
(內(nèi)容)向下拉到底部,不能往下拉,但是可以往上拉
(內(nèi)容)向上拉到頂部,不能往上拉,但是可以往下拉
(內(nèi)容)既不能往下拉也不能往下拉
總結(jié)起來(lái)如下表(1為允許,0為禁止,高位表示向上方向,低位表示向下方向)
可以拉的方向(height) | 拉的方向(touch) | 能否繼續(xù)拉 |
---|---|---|
00 | 10 | 0 |
00 | 01 | 0 |
01 | 10 | 0 |
01 | 01 | 1 |
10 | 10 | 1 |
10 | 01 | 0 |
從表中我們可以得出一個(gè)結(jié)論是,能否在該方向上繼續(xù)拉其實(shí)就是對(duì)兩種條件做一個(gè)&運(yùn)算!話不多說(shuō),上核心源碼
// 防止過(guò)分拉動(dòng) preventMove: function(e) { // 高位表示向上滾動(dòng), 底位表示向下滾動(dòng): 1容許 0禁止 var status = "11", e = e || window.event, // 使用 || 運(yùn)算取得event對(duì)象 ele = this, currentY = e.touches[0].clientY, startY = startMoveYmap[ele.id], scrollTop = ele.scrollTop, offsetHeight = ele.offsetHeight, scrollHeight = ele.scrollHeight; if (scrollTop === 0) { // 如果內(nèi)容小于容器則同時(shí)禁止上下滾動(dòng) status = offsetHeight >= scrollHeight ? "00" : "01"; } else if (scrollTop + offsetHeight >= scrollHeight) { // 已經(jīng)滾到底部了只能向上滾動(dòng) status = "10"; } if (status != "11") { // 判斷當(dāng)前的滾動(dòng)方向 var direction = currentY - startY > 0 ? "10" : "01"; // console.log(direction); // 操作方向和當(dāng)前允許狀態(tài)求與運(yùn)算,運(yùn)算結(jié)果為0,就說(shuō)明不允許該方向滾動(dòng),則禁止默認(rèn)事件,阻止?jié)L動(dòng) if (!(parseInt(status, 2) & parseInt(direction, 2))) { e.preventDefault(); e.stopPropagation(); return; } } },與UI共用的線程
開(kāi)始的時(shí)候,我以為上面的代碼就萬(wàn)事大吉了,經(jīng)過(guò)實(shí)踐和摸索,結(jié)論是:簡(jiǎn)直是天真。
異步的概念之所以首先在Web2.0中火起來(lái),是因?yàn)樵跒g覽器中JavaScript在單線程上執(zhí)行,而且它還與UI渲染共用一個(gè)UI線程。這意味著JavaScript在執(zhí)行的時(shí)候UI渲染和響應(yīng)是處于停滯狀態(tài)的。 ----《深入淺出nodejs》
這意味這什么呢?當(dāng)我們的UI線程在進(jìn)行渲染的時(shí)候,JavaScript代碼也是處于停滯狀態(tài)的!不信的話可以在一個(gè)可以滑動(dòng)的頁(yè)面上引入下面這段代碼:
var count = 0; setInterval(functiong() { console.log(++count); }, 100);
刷新頁(yè)面的時(shí)候,控制臺(tái)會(huì)一直打印不斷變大的數(shù)字,但是只要你用手指開(kāi)始拖動(dòng)頁(yè)面,打印終止,等你把手放開(kāi)的時(shí)候,打印繼續(xù),而且數(shù)字會(huì)承接打印停止前那個(gè)數(shù)字。也就是UI在渲染的時(shí)候,js保存了狀態(tài),在UI渲染停止的時(shí)候,js又可以繼續(xù)運(yùn)行。
這對(duì)我們的組件帶來(lái)的影響是什么呢?幾乎是毀滅性的,場(chǎng)景如下:
如果頁(yè)面內(nèi)容不足一屏,按照組件的設(shè)定,既不能上拉也不能下拉,這種情況不會(huì)受影響。
如果頁(yè)面內(nèi)容多于一屏,按照組件的設(shè)定,這時(shí)候可以往下拉不能往上拉,在嘗試上拉的時(shí)候,組件會(huì)阻止冒泡。但如果先下拉一點(diǎn)然后使勁往上拉,本來(lái)拉到頂之后組件會(huì)阻止事件冒泡,但是一旦下拉之后,線程就歸屬于UI了,上拉的過(guò)程中組件的判斷完全插不進(jìn)手,還是無(wú)情漏出了黑底!GG!
可愛(ài)的IOS5新特性在尋求最終的解決方案之前,我們先來(lái)討論一下overflow這個(gè)屬性。
傳統(tǒng) pc 端中,子容器高度超出父容器高度,通常使用 overflow:auto 可出現(xiàn)滾動(dòng)條拖動(dòng)顯示溢出的內(nèi)容,而移動(dòng)web開(kāi)發(fā)中,由于瀏覽器廠商的系統(tǒng)不同、版本不同,導(dǎo)致有部分機(jī)型不支持對(duì)彈性滾動(dòng),從而在開(kāi)發(fā)中制造了所謂的 BUG。
從本人這兩個(gè)月移動(dòng)Web實(shí)踐的經(jīng)驗(yàn)來(lái)看,微信的webview里面overflow: scroll和overflow: auto的滑動(dòng)效果無(wú)論是在安卓還是IOS下的體驗(yàn)都很一般,有明顯的卡頓現(xiàn)象,在安卓下面還會(huì)出現(xiàn)滑動(dòng)過(guò)快的時(shí)候在頁(yè)面停下來(lái)之后滾動(dòng)條才閃到相應(yīng)位置的現(xiàn)象。
在IOS5之后,出現(xiàn)了一個(gè)新的屬性: -webkit-overflow-scrolling,用來(lái)控制元素在移動(dòng)設(shè)備上是否使用滾動(dòng)回彈效果。它的取值有兩個(gè):
auto:使用普通滾動(dòng), 當(dāng)手指從觸摸屏上移開(kāi),滾動(dòng)會(huì)立即停止。
touch:使用具有回彈效果的滾動(dòng), 當(dāng)手指從觸摸屏上移開(kāi),內(nèi)容會(huì)繼續(xù)保持一段時(shí)間的滾動(dòng)效果。繼續(xù)滾動(dòng)的速度和持續(xù)的時(shí)間和滾動(dòng)手勢(shì)的強(qiáng)烈程度成正比。同時(shí)也會(huì)創(chuàng)建一個(gè)新的堆棧上下文。
實(shí)驗(yàn)表明,在IOS下,對(duì)一個(gè)元素設(shè)置了overflow:scroll的基礎(chǔ)上再添加-webkit-overflow-scrolling: touch;會(huì)讓滑動(dòng)又如絲般順滑。
這個(gè)屬性和我們解決之前的問(wèn)題有什么聯(lián)系呢?秘密就在這彈性滾動(dòng)效果。
頁(yè)面中body元素的內(nèi)容超過(guò)一屏,頁(yè)面可以往下滑動(dòng)(手指往上拉)。按照我們組件的設(shè)定,手指開(kāi)始的時(shí)候是不能往下拉的,但是如果手指的方向是先往上拉一小段,在手指不離開(kāi)屏幕的基礎(chǔ)上再往下拉,當(dāng)頁(yè)面拉到頂部的時(shí)候,會(huì)相繼出現(xiàn)黑底,因?yàn)閁I在渲染,js沒(méi)法去阻止事件冒泡。
現(xiàn)在我們把組件的作用元素設(shè)定為body內(nèi)最外圍的div元素,并且給這個(gè)元素添加兩個(gè)CSS屬性overflow:scroll和-webkit-overflow-scrolling: touch;,那么上面的場(chǎng)景就會(huì)變成:
頁(yè)面中body內(nèi)最外圍的div標(biāo)簽內(nèi)容超過(guò)一屏,其內(nèi)容可以往下滑動(dòng)(手指往上拉)。按照我們組件的設(shè)定,手指開(kāi)始的時(shí)候是不能往下拉的。和之前一樣,手指先往上拉一小段,在手指不離開(kāi)該元素的基礎(chǔ)上再往下拉,當(dāng)元素內(nèi)容到頂之后,因?yàn)閁I在渲染,js本插不上手,但是該元素內(nèi)部的內(nèi)容設(shè)置了彈性滾動(dòng),要實(shí)現(xiàn)彈性滾動(dòng),基本要求就是這個(gè)div容器是不動(dòng)的,可以理解成因?yàn)閺椥詽L動(dòng),自動(dòng)就禁止掉了事件冒泡,也就不會(huì)出現(xiàn)黑底了。
肯定有人要問(wèn)了,既然自動(dòng)禁止了事件冒泡,那還要這個(gè)組件何用?當(dāng)然有用,會(huì)禁止掉事件冒泡的前提是內(nèi)容在滾動(dòng)。依照上面的場(chǎng)景,如果一開(kāi)始手指直接往下拉,沒(méi)有組件的限制,還是會(huì)露出黑底,因而,要實(shí)現(xiàn)比較好的效果,是需要這兩個(gè)屬性和組件配合的。
至于安卓嘛,因?yàn)闆](méi)有這個(gè)屬性,暫時(shí)只能一邊涼快去吧。
多說(shuō)無(wú)用,看源碼吧:
https://github.com/yuanzm/preventoverscrolljs
touch事件標(biāo)準(zhǔn)文檔
http://www.phpied.com/rendering-repaint-reflowrelayout-restyle/
https://stackoverflow.com/questions/10966710/choppy-laggy-scroll-event-on-chrome-and-ie/11039468#11039468?newreg=ded03509915248adaed26d1facce2c6c
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/86025.html
在項(xiàng)目開(kāi)發(fā)中,會(huì)要求在小程序有時(shí)使用下拉框選項(xiàng)。在通常思路就是用 picker 組件實(shí)現(xiàn)。pick 組件使用 mode 來(lái)區(qū)分類(lèi)別,默認(rèn)使用普通選擇器就行?! ∵€有另一個(gè)方法就是可以通過(guò)自定義組件實(shí)現(xiàn),代碼如下: //index.js Component({ /** *組件的屬性列表 */ properties:{ propArray:{ type:Array, ...
文章主要是詳細(xì)介紹了pythonGUI多做輸入文本Text的控制方式,具有非常好的實(shí)用價(jià)值,希望能幫助到大家。如有誤或者未考慮到真正的地區(qū),望鼎力相助 Text的屬性wrap fromtkinterimport* root=Tk() root.geometry('200x300') te=Text(root,height=20,width=15) #將多做輸...
前言 世界之大無(wú)奇不有,當(dāng)客戶對(duì)于導(dǎo)航欄不滿時(shí)候,就需要我們自己去開(kāi)發(fā)了。當(dāng)然uniapp也有很多自定義導(dǎo)航插件,但現(xiàn)在我們要自己寫(xiě)?! ∈紫?,我們要知道有那幾部分。 可以看到在我們的,這個(gè)時(shí)候我們自定義的導(dǎo)航欄,需要做到標(biāo)題于膠囊水平對(duì)齊,那其實(shí)這個(gè)時(shí)候整個(gè)頭部其實(shí)主要又:。 上圖中,微信小程序中,頭部導(dǎo)航欄其實(shí)受到右上角膠囊的限制比較大,也就需要我們做改進(jìn),我們可以看到:狀態(tài)欄的高度+...
微信小程序項(xiàng)目中,要實(shí)現(xiàn)無(wú)滑動(dòng)效果,可以用tab點(diǎn)擊切換,看看具體代碼供參考: <!--pages/dingdan/dingdan.wxml--> <viewclass="body"> <viewclass="swiper-tab"> <viewwx:for="{{tabList}}"...
這篇文章主要是闡述了selenium可視化數(shù)據(jù)抓取有效的方法實(shí)現(xiàn),文中根據(jù)舉例編碼講到的十分詳盡,對(duì)大家學(xué)習(xí)培訓(xùn)還是工作具有很強(qiáng)的參照學(xué)習(xí)培訓(xùn)使用價(jià)值,需求的小伙伴們下邊伴隨著小編就來(lái)互相學(xué)習(xí)了解一下吧。 Selenium是一個(gè)自動(dòng)化技術(shù)檢測(cè)工具,運(yùn)用它能夠推動(dòng)電腦瀏覽器實(shí)行特殊動(dòng)作,如鼠標(biāo)點(diǎn)擊、下拉框等操作,同時(shí)也可以獲取電腦瀏覽器現(xiàn)階段獲得界面的程序代碼,保證由此可見(jiàn)即可獲得。對(duì)于有些J...
閱讀 3089·2021-10-12 10:12
閱讀 1600·2021-09-09 11:39
閱讀 1931·2019-08-30 15:44
閱讀 2370·2019-08-29 15:23
閱讀 2920·2019-08-29 15:18
閱讀 2992·2019-08-29 13:02
閱讀 2731·2019-08-26 18:36
閱讀 767·2019-08-26 12:08