1. 程式人生 > >微信小程式中WebView中原生元件限制問題解析

微信小程式中WebView中原生元件限制問題解析

背景

在微信的文件中有一個章節說明了『 原生元件的使用限制 』有這麼一段話

『由於原生元件脫離在 WebView 渲染流程外,因此在使用時有以下限制:

原生元件的層級是最高的,所以頁面中的其他元件無論設定 z-index 為多少,都無法蓋在原生元件上。
後插入的原生元件可以覆蓋之前的原生元件。
原生元件還無法在 scroll-view、swiper、picker-view、movable-view 中使用。
部分CSS樣式無法應用於原生元件,例如:
無法對原生元件設定 CSS 動畫
無法定義原生元件為 position: fixed
不能在父級節點使用 overflow: hidden 來裁剪原生元件的顯示區域
原生元件的事件監聽不能使用 bind:eventname 的寫法,只支援 bindeventname。原生元件也不支援 catch 和 capture 的事件繫結方式
在iOS下,原生元件暫時不支援觸控相關事件。
在工具上,原生元件是用web元件模擬的,因此很多情況並不能很好的還原真機的表現,建議開發者在使用到原生元件時儘量在真機上進行除錯。』

解析

所謂的原生元件,即非Web元件系統擴充套件Native元件。因為小程式在檢視渲染層面使用了WebView,而在Video,Map這類元件,使用WebView的WebCore渲染之後體驗不佳的詬病一直存在,而且標準不一。小程式上因使用原生的WebView進行渲染,而不是用修改的WebView核心(至少在iOS上沒有這麼幹),而無法對web原生標籤擴充套件。基於使用者體驗,和坑爹的技術限制,小程式提出了原生元件的概念,也就是在WebView上面使用原生元件填充佔位元素的方式修補這類元件使用者體驗問題。因為WebView和原生元件在應用層本身就不是一個渲染層級,於是出現Web上面的標籤無法浮於Video之上(直播應用的惡夢),在不修改技術思路的前提下,position: fixed, overflow: hidden這樣的屬性是不可能用於原生元件的樣式的。不過偽同層渲染也不是說不可能,即在渲染原生元件時候根據層級鏤空面積。

特別在Map上使用WebView作為渲染之後體驗不佳的詬病一直存在,特別是地圖上marker標記過多的重度場景下,筆者所在的公司的在使用高德地圖Web端提供出來的C端具備反人類的體驗,地圖拖拉龜速,點選響應緩慢,載入loading地圖區域等待時間過長。而Video則支援的格式有限,列出部分瀏覽器的支援的如下:

Firefox:支援 Ogg Vorbis和WAV 
Opera :支援Ogg Vorbis和WAV 
Safari :支援MP3,AAC格式 ,和MP4 
Chrome :支援Ogg Vorbis,MP3,WAV,AAC和MP4 
Internet Explorer 9+ :支援MP3,AAC格式 ,和MP4 
IOS :支援MP3,AAC格式 ,和MP4 
Android :支援AAC和MP3 

上述,可以知道視訊支援有限(限於版權)。而就我們關注的移動端iOS和Andoroid,實現一個視訊播放,我們可能都會有以下幾點的需求:1、全屏處理;2、覆蓋層效果;3、自動播放;4、播放控制;5、隱藏播放控制元件;在iOS上如果使用WebView,你無法修改全屏下的工具這一點體驗已經足夠讓所有的產品經理抓狂,更不用說Android的這麼多的機型。覆蓋層效果在微信上不得不使用微信提供原生元件cover-view實現,而限於原生實現限制,cover-view的支援有限。

設計方案

1、元件層於WebView層之上

這也應該是微信小程式團隊現階段使用的方案,通過特殊的佔位標籤,使用getBoundingClientRect獲取元件位置,而原生元件跟隨Webview滾動。Talking is cheap. Show me your code,那麼用程式碼實現的效果的如下。now_lowest_gif.gif從圖中可見,覆蓋層確實位於原生元件之下。

2、元件層於WebView層之下

此方式略微複雜。需要通過與Webview scroll聯動的置於Webview之下的Component Layer實現,而Webview背景設定為透明。至於事件,通過Webview的事件透傳,傳遞到Component Layer,需要通過快取webview中元素再計算是否被點中通過重寫hitTest方法實現。通過此技術方案實現的好處也是明顯的,因為原生元件層很多時候都是置於最底層,而Web上的元件可以輕鬆覆蓋於Native之上,無需使用cover-view之類的hack方法。效果如下所示圖片發自簡書App

Tips

在iOS上還特別需要注意一點UIWebview的坑。在使用-webkit-overflow-scrolling 使用,你會發現momentum scroll階段並不會觸發scroll事件,而且 scrollTop 屬性不會變化,當然getBoundingClientRect也同樣失效。如果考慮使用touchmove 這樣事件你也僅僅在手指還在螢幕上的時候觸發,檢測滾動區域內部元素的getBoundingClientRect 同樣無效。

當然幸運的是,這麼大一個坑只是發生在UIWebview,對WKWebview並沒有影響。