《微信小程序七日談》系列文章:
- 第一天:人生若只如初見;
- 第二天:你可能要拋棄原來的響應式開發思維;
- 第三天:玩轉Page組件的生命周期;
- 第四天:頁面路徑最多五層?導航可以這么玩;
- 第五天:你可能要在登錄功能上花費大力氣;
- 第六天:小程序devtool隱藏的秘密;
- 第七天:不要撿了芝麻丟了西瓜
本系列的文章並非初學教程,而是筆者在具體開發過程中遇到的問題以及部分解決方案。
上篇文章 第一天:人生若只如初見簡單記錄了筆者初步上手開發微信小程序遇到的一些問題,其中提到了wxss的部分細節問題。這篇文章以筆者在開發小程序響應式UI當中遇到的一些問題為例,簡單記錄一下使用wxss為響應式開發帶來的一些模式和思維上的改變。
rem的重定義
前端工程師對rem
非常熟悉,rem是以html元素的font-size
為基准的尺寸計量單位。rem方便了開發者對響應式UI的尺寸進行統籌管理。
wxss中的rem
與css中的rem
的含義完全不同,下面是微信官方文檔中對rem的定義:
rem(root em): 規定屏幕寬度為20rem;1rem = (750/20)rpx
其中的750這個數值是wxss將設備屏幕的寬統一定義為750rpx,對此,下文會講解。
各位讀到這里是否腦海里浮現了一個想法:wxss的rem怎么聽起來有點像bootstrap的柵格系統呢?
wxss將屏幕寬分為20rem,bootstrap將設備屏幕寬度分為12列。初看起來確實有點類似。但其實wxss的rem和bootstrap的柵格系統並不相同。雖然wxss和bootstrap都是講屏幕尺寸分割為單元格,但rem和柵格的定位不同。
bootstrap的開發者使用指定的classname進行元素間的比例分配,這其實接近為css3中的flexbox;而wxss的rem是一個尺寸單位,你可以在合理的場景下將任何以px
為單位的屬性值替換為rem
。
所以,開發小程序UI時,需要拋棄思維中對rem的常規認知。截止目前,筆者還未遇到必須使用小程序rem的需求,希望大家踴躍探討。
rpx的奇妙之處
上文提到wxss將設備屏幕的寬統一定義為750rpx,其中的rpx是wxss帶來的新的尺寸單位。rpx的定義如下:
rpx(responsive pixel): 可以根據屏幕寬度進行自適應。規定屏幕寬為750rpx。如在 iPhone6 上,屏幕寬度為375px,共有750個物理像素,則750rpx = 375px = 750物理像素,1rpx = 0.5px = 1物理像素。
css中的px與設備的物理像素並非絕對的一比一關系。尤其是在移動設備上,px與物理像素的比例與設備的dpr(devicePixelRadio)有關,詳細的對應關系各位可自行查閱。
rpx稱為相對像素值,rpx與物理像素也並非絕對的一比一關系。wxss將設備寬定義為750rpx,是以iPhone6的分辨率(750x1334)為基准划分的。也就是說,在iPhone6上,1rpx=1物理像素=0.5px。官方文檔列出了幾種屏幕的rpx對應關系如下:
大家可以從中得到rpx和px的換算公式:
1rpx = 1px/dpr
其中iPhone6的dpr=2。
那么rpx帶給響應式UI什么改變呢?
目前大部分UI工程師在制作UI稿的時候是按照iPhone6的尺寸設計,然后前端工程師按照UI稿尺寸的一半進行UI的還原開發。這樣在iPhone6以及接近iPhone6尺寸的設備上是沒有任何問題的。但是移動設備的尺寸多種多樣,我們的產品不可能只應對iPhone6(況且iPhone7已經來了哈哈...),所以通常的做法是使用css的媒體查詢根據設備的尺寸再進行適配微調。
如果使用rpx是不是就可以解決這個問題呢?筆者在開發過程中嘗試使用rpx代替px,使用UI稿的原始尺寸還原UI,截止到目前體驗非常好。rpx本身代表的是相對像素,所以不論多大尺寸的屏幕,rpx的UI占據的屏幕比例是絕對固定的,是等比縮放的。
但是rpx並非萬能的,比如使用css sprites的圖標。請看下文。
sprites圖標的響應式處理
使用css sprites作為圖標背景時,每個圖標的尺寸是以px為單位固定的,比如:
.icon{
background-image: url('//image.daojia.com/icon.png');
display: inline-block;
vertical-align: middle;
}
.icon__circle{
background-position: 0 0;
width: 40px;
height: 40px;
}
如果圖標的尺寸不符合UI設計,則進行一定比例的縮放:
.icon__circle{
transform: scale(0.5);
}
也就是說,使用sprites圖標不可避免地會用到px,如果與rpx結合使用,是不能保證同rpx一樣等比縮放效果的。那么怎么去解決這個問題呢?
根據上文總結出的rpx與px的換算公式,如果想要將以px規定的UI達到同rpx一樣的響應式縮放效果,必須將px與設備的dpr進行計算。但是css作為一種標記語言,並不具備動態特性,無法動態地獲取設備dpr並計算。所以,單純使用wxss並不能解決上文提到的問題。
好消息是小程序提供了獲取設備信息的API,並且支持CommonJS模塊化方案。有了這些功能,我們可以在封裝組件時加入動態的邏輯配置。
還是以上文的代碼為例,sprites圖的icon__circle
尺寸為40px*40px,我們的目標是將其適配為20rpx,以下是筆者的開發方案。
比如項目中有一個user組件,包含了一些sprites圖標節點。user組件的文件目錄如下:
user.wxml
- 組件模板;user.wxss
- 組件樣式;user.js
- 組件邏輯。
首先給user.wxml
中icon對應的element設置動態的transform
:
<view class='icon icon__circle' style="transform: scale({{iconScale}})"></view>
其中iconScale
是引用user的外部組件index傳遞給user組件的:
<import src='user.wxml'/>
<template is='product-user' data="{{iconScale: userIconScale}}"/>
userIconScale是index組件的一個data,userIconScale的值並非index組件規定的,而是由index組件的js調用user.js
動態獲取的。以下代碼是user.js
暴露的API:
const ORIGIN_ICON_PX = 40;
const TARGET_ICON_RPX = 20;
module.exports = {
getIconScale() {
let result = 1;
wx.getSystemInfo({
success: function(res) {
let _dpr = res.pixelRatio;
result = TARGET_ICON_RPX/(ORIGIN_ICON_PX * _dpr);
}
});
return result;
}
}
然后在index組件的js中調用以上API:
let getIconScale = require('user.js').getIconScale;
Page({
data: {
userIconScale: 1
},
onLoad(){
this.setData({
userIconScale: getIconScale()
});
}
});
以上只是初步的方案,很多地方需要再仔細琢磨。不過以上方案基本上具備了一個組件的邏輯封裝,並且達到了我們對響應式的開發需求。
總結
第二天的開發經歷還是頗有收獲的,不僅僅是對小程序開發模式的熟悉,而且對一些綜合方案也有一定的深入。期待后續吧。