從微信小程序開發者工具源碼看實現原理(四)- - 自適應布局


從前面從微信小程序開發者工具源碼看實現原理(一)- - 小程序架構設計可以知道,小程序大部分是通過web技術進行渲染的,也就是最終通過瀏覽器的dom tree + cssom來生成渲染樹;既然最終是通過css來繪制ui布局,我們知道小程序提供的自適應css單位rpx在瀏覽器環境根本不被識別,所以小程序最終還是將rpx單位轉化為瀏覽器識別的css長度單位,到底是怎么轉化的呢,本節就來探討一下轉化機制。

小程序樣式轉換

從微信小程序開發者工具源碼看實現原理(二)- - 小程序技術實現中可以知道,小程序中的wxss樣式文件進行的主要轉換轉換rpx單位,視圖層模板注入轉換后的wxss代碼如下圖:

上面的內容就是注入到視圖層pageframe模板中的css代碼,其內容包括:

  • 提供rpx單位到px單位的轉換
  • 提供動態插入轉換后樣式內容到dom中的js方法
  • 每個頁面引入公共樣式,即app.wxss轉換后的css內容

上面提到的這些轉換操作都是內置到小程序的wcsc可執行程序中,通過調用可執行程序來完成具體轉換工作。最終注入到頁面中的css內容如下圖所示:

小程序自適應單位rpx轉換

小程序的自適應布局采用的內部實現的rpx來完成,但是其不被web識別,所以rpx單位轉換是指:

是將小程序的css單位rpx轉換為web識別的css單位px

那么小程序怎么來進行rpx與px之間的轉換呢?先來看一下官網有關rpx的描述:

rpx(responsive pixel): 可以根據屏幕寬度進行自適應。規定屏幕寬為750rpx。如在 iPhone6 上,屏幕寬度為375px,共有750個物理像素,則750rpx = 375px = 750物理像素,1rpx = 0.5px = 1物理像素。

由此可以看出,小程序在實現rpx轉換時,不論是什么屏幕的手機,都是將屏幕寬度固定設為750rpx,然后根據實際屏幕的設備像素比dpr(dpr = 設備像素 / css像素)來進行轉換的。具體對應關系如下:

1rpx = (number/ 750) * 設備寬度 px

下面通過小程序開發者工具簡單分析小程序wcsc可執行命令程序生成的有關rpx轉換的js代碼

首先獲取小程序的設備寬度

小程序開發者工具在初始渲染一個頁面時會首先獲取設備寬度deviceWidth和dpr,然后會通過checkDeviceWidth方法(wcsc可執行命令注入的代碼)檢查修正二者的值,因為屏幕orientation方向可能變化,在上面代碼有這么一段:

if (window.screen.orientation && /^landscape/.test(window.screen.orientation.type || "")) {
   newDeviceWidth = newDeviceHeight;
}

該代碼利用window.screen.orientation來判斷手機的橫豎方向,若處於橫屏的時候,webview的寬度與高度值會互換,即高度值就是屏幕的真實寬度;需要注意的是小程序開發者工具的webview這一點與移動端手機表現不太一致。

另外,需要補充兩點:

  • 利用window.screen.orientation這個判斷手機方向的特性大部分瀏覽器支持情況比較差,具體可以看這里。但是小程序開發者工具使用基於chrome的webview,這個是支持的。
  • 代碼的window.__checkDeviceWidth__在小程序的一些基礎庫(如2.3.2)中是沒有定義的;但是新的版本(2.7.7)是有該方法定義的,但是從什么版本開始支持的不得而知。

rpx單位轉換

正如官網所描述的,小程序將屏幕固定750rpx,然后根據當前屏幕寬度以及設置的rpx值,最終推算出rpx對應的px值。
補充一點,在設置的rpx值轉換為px值大於0小於1時,不論設置的rpx值是多少,最終在dpr不是1的ios情況下會始終返回0.5px,其他情況始終返回1px;例如下面代碼:

.text {
  height: 1rpx;
  background: #333;
}

最終在開發者工具中轉換的px值為0.5,如下圖:

小程序屏幕旋轉自適應轉換過程

通過上面轉換rpx值,一旦轉換完成后轉換值就固定了;但是對於支持屏幕旋轉的情況,這顯然不是我們希望的結果,期望根據屏幕旋轉的方向來重新轉換對應的rpx值。
小程序從2.4.0基礎版本開始通過配置"pageOrientation": "auto"開始支持屏幕旋轉,這就需要知道屏幕發生變化的時機來做對應的處理。具體分兩個方面轉換:wxss樣式文件轉換style內聯樣式轉換

wxss樣式文件自適應轉換

首先,在視圖層,wxss樣式文件經rpx初始轉換后並將樣式注入到頁面過程中,會向window.__rpxRecalculatingFuncs__數組中收集窗口變化時的回調;先看wcsc可執行程序輸出的處理rpx轉換相關的setCssToHead函數實現,其最終返回rewritor函數,對應代碼如下圖:

可以看出在轉換后的樣式嵌入到document.head中后,依然保存有創建的style元素的句柄,在頁面窗口變更時執行對應的回調來修正rpx轉換后的px值。

然后,在小程序基礎庫WAWebview內部初始時會使用wx.onWindowResize(fn)來注冊窗口變更的事件回調,注冊事件內部會執行window.__rpxRecalculatingFuncs__中的回調,具體代碼如下圖:

這樣,視圖窗口變更時就會通知樣式文件進行重新rpx轉換,最后將最新轉換的樣式內容更新到頁面中。

那么,小程序如何把握屏幕切換的觸發時機呢?

這個觸發時機在微信環境是由native提供感知能力,開發環境則是小程序開發工具本身提供支持。拿開微信開發者工具來說明具體的整個過程:

  • 視圖層與業務邏輯層分別注冊onViewDidResize事件回調
  • 開發者工具感知到窗口變化會通過websocket方式向視圖層和業務邏輯層同時發送執行onViewDidResize回調的消息
  • onViewDidResize會分別執行通過wx.onWindowResize(fn)注冊的回調

內聯樣式自適應轉換

內聯樣式轉換在底層基礎庫是采用transformRpx方法來轉換rpx值的,思路與上面介紹的一樣,唯一不同點就是是否對0進行修正,具體代碼如下:

var $ = function(e) { // e為要轉換的rpx值,V為設備寬度
   return 0 === e && function(e) {
      var t = window.__wcc_version_info__;
      if (t) return t[e];
    }("fixZeroRpx") ? 0 : (e = e / 750 * V, 0 === (e = Math.floor(e + 1e-4)) ? 1 !== dpr && isIPhone ? .5 : 1 : e)
 }

通過獲取window.__wcc_version_info__.fixZeroRpx的值來判斷rpx為0時如何轉換;而window.__wcc_version_info__的定義賦值是在wcc可執行命令轉換wxml文件生成的js腳本中完成的,下面是wcc生成有關賦值代碼:

具體樣式文件自適應轉換過程如下:

  • 視圖層在生成virtual dom過程中會收集每個元素的屬性,其中包括style屬性
  • 在生成dom過程中,針對元素的style屬性使用transformRpx進行轉換,轉換后內容應用到具體dom元素
  • 為含有rpx單位內聯樣式dom元素綁定窗口變化回調,窗口變化時style中的rpx進行重新轉換並應用到dom元素上


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM