優化以前寫過的一篇文章:https://www.cnblogs.com/beileixinqing/p/11283315.html
目前常見移動端適配方案
1、媒體查詢
- 通過寫媒體查詢,在不同的分辨率下寫對應不同的樣式,這樣帶來以下幾點缺點:
- 開發上的繁瑣,需要針對不同設備下寫對應適配的樣式;
- 在不同設備或者不同分辨率切換下,效果變化時的視覺沖擊,帶來不好的用戶體驗;
2、通過 rem 單位來實現適配
通過設置根元素的font-size,其他單位使用rem,但是需要引入一段js,動態判斷當前設備的dpr,從而改變根元素的font-size,也是一種很好的方案,例如 著名的flexible.js,非常好用,唯一不足時多引入了一個js文件,而且對安卓的適配性不是很好。
3、通過視口單位( Viewport units )
在業界,極為推崇的一種理論是 Peter-Paul Koch (江湖人稱“PPK大神”)提出的關於視口的解釋——在桌面端,視口指的是在桌面端,指的是瀏覽器的可視區域;而在移動端較為復雜,它涉及到三個視口:分別是 Layout Viewport(布局視口)、 Visual Viewport(視覺視口)、Ideal Viewport。
而視口單位中的“視口”,在桌面端,毫無疑問指的就是瀏覽器的可視區域;但是在移動端,它指的則是三個 Viewport 中的 Layout Viewport 。
根據CSS3規范,視口單位主要包括以下4個:
- vw : 1vw 等於視口寬度的1%
- vh : 1vh 等於視口高度的1%
- vmin : 選取 vw 和 vh 中最小的那個
- vmax : 選取 vw 和 vh 中最大的那個
視口單位區別於%單位,視口單位是依賴於視口的尺寸,根據視口尺寸的百分比來定義的;而%單位則是依賴於元素的祖先元素。
用視口單位度量,視口寬度為100vw,高度為100vh(左側為豎屏情況,右側為橫屏情況)
例如,在桌面端瀏覽器視口尺寸為650px,那么 1vw = 650 * 1% = 6.5px(這是理論推算的出,如果瀏覽器不支持0.5px,那么實際渲染結果可能是7px)。
兼容性
其兼容性如下圖所示,可以知道:在移動端 iOS 8 以上以及 Android 4.4 以上獲得支持,並且在微信 x5 內核中也得到完美的全面支持。
截圖來自Can I Use
截圖來自X5內核-Can I Use
視口單位特點是寬度或者高度不同,對應的尺寸也會發生變化,從而達到適配即自適應,然而也有缺點,不能設置最大最小寬度限制,當比如橫屏展示的時候,寬度很寬的情況下,必須設置最大font-size限制,防止字體顯示過大;
4、視口單位+rem布局
- 給根元素大小設置隨着視口變化而變化的 vw 單位,這樣就可以實現動態改變其大小。
- 限制根元素字體大小的最大最小值,配合 body 加上最大寬度和最小寬度
自己常用的兩種方案
方案一:使用lib-flexible包
參考鏈接:https://www.w3cplus.com/mobile/lib-flexible-for-html5-layout.html
使用flexible包方式,安裝 lib-flexible 包和 px2rem-loader包
npm install --save-dev lib-flexible px2rem-loader |
在需要的js文件中頭部引入,如果是vue項目就引入到main.js中:
import 'lib-flexible'
webpack配置loader,注意順序很重要,順序不對會出錯
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader', {
loader: 'px2rem-loader',
options: {
remUni: 75,
remPrecision: 8,
}
},
{
loader: 'postcss-loader',
options: {
plugins: [require("autoprefixer")("last 100 versions")]
}
}]
},
{
test: /\.less$/,
use: [MiniCssExtractPlugin.loader, 'css-loader?importLoaders=1', {
loader: 'px2rem-loader',
options: {
remUni: 75,
remPrecision: 8,
}
},
{
loader: 'postcss-loader',
options: {
plugins: [require("autoprefixer")("last 100 versions")]
}
},
'less-loader', ]
},
|
這里有個問題,在安卓下flexible.js源碼是全部按dpr=1來適配的,那自然是不行的,我們修改一下源碼,改為按devicePixelRatio顯示
if (isIPhone) { // iOS下,對於2和3的屏,用2倍的方案,其余的用1倍方案
if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) {
dpr = 3;
} else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)) {
dpr = 2;
} else {
dpr = 1;
}
} else { // 其他設備下,仍舊使用1倍的方案
dpr = devicePixelRatio; //這里將原來=1改為devicePixelRatio
} |
然后寫針對不同dpr下字體大小的適配,這里用less實現:
.font-dpr(@font-size) {
font-size: @font-size;
[data-dpr="1"] & { font-size: @font-size;
} [data-dpr="2"] & {
font-size: @font-size * 2;
} //for mx3 [data-dpr="2.5"] & {
font-size: @font-size * 2;
} //for 小米note,for 小米mix [data-dpr="2.75"] & {
font-size: @font-size * 2.2;
} [data-dpr="3"] & {
font-size: @font-size * 2.2;
} //for 三星note4 ,三星s6 [data-dpr="4"] & {
font-size: @font-size * 2;
} } |
使用的時候直接.font-dpr(20) 就可以了。
方案二:使用less或者sass等CSS 預處理語言寫適配方案
參考鏈接:https://juejin.im/post/5caaa230e51d452b672f9703#heading-7
基准按照設計圖尺寸,進行vw,vh換算,設置html元素字體大小。
這里貼出我的mixin.less
@baseDesign: 375; // 設計圖尺寸默認按寬為375
@baseSize: @baseDesign/10; // 設計圖尺寸默認按寬為375 / 10
.font-size(@px) {
font-size: (@px/@baseSize/2)*1rem;
}
.margin(@px) {
margin: (@px/@baseSize/2)*1rem;
}
.margin-all(@a,@b,@c,@d) {
margin: (@a/@baseSize/2)*1rem (@b/@baseSize/2)*1rem (@c/@baseSize/2)*1rem (@d/@baseSize/2)*1rem;
}
.padding(@px) {
padding: (@px/@baseSize/2)*1rem;
}
.padding-all(@a,@b,@c,@d) {
padding: (@a/@baseSize/2)*1rem (@b/@baseSize/2)*1rem (@c/@baseSize/2)*1rem (@d/@baseSize/2)*1rem;
}
.width(@px) {
width: (@px/@baseSize/2)*1rem;
}
.height(@px) {
height: (@px/@baseSize/2)*1rem;
}
.min-width(@px) {
min-width: (@px/@baseSize/2)*1rem;
}
.max-width(@px) {
max-width: (@px/@baseSize/2)*1rem;
}
.min-height(@px) {
min-height: (@px/@baseSize/2)*1rem;
}
.max-height(@px) {
max-height: (@px/@baseSize/2)*1rem;
}
.line-height(@px) {
line-height: (@px/@baseSize/2)*1rem;
}
.margin-right(@px) {
margin-right: (@px/@baseSize/2)*1rem;
}
.padding-right(@px) {
padding-right: (@px/@baseSize/2)*1rem;
}
.margin-left(@px) {
margin-left: (@px/@baseSize/2)*1rem;
}
.padding-left(@px) {
padding-left: (@px/@baseSize/2)*1rem;
}
.margin-top(@px) {
margin-top: (@px/@baseSize/2)*1rem;
}
.padding-top(@px) {
padding-top: (@px/@baseSize/2)*1rem;
}
.margin-bottom(@px) {
margin-bottom: (@px/@baseSize/2)*1rem;
}
.padding-bottom(@px) {
padding-bottom: (@px/@baseSize/2)*1rem;
}
.border(@px,@color) {
border: (@px/@baseSize/2)*1rem solid @color;
}
.border-top(@px,@color) {
border-top: (@px/@baseSize/2)*1rem solid @color;
}
.border-bottom(@px,@color) {
border-bottom: (@px/@baseSize/2)*1rem solid @color;
}
.border-right(@px,@color) {
border-right: (@px/@baseSize/2)*1rem solid @color;
}
.border-left(@px,@color) {
border-left: (@px/@baseSize/2)*1rem solid @color;
}
// border 不可這樣用,暫時廢棄~
/*.border(@px,@color) {
position: relative;
&::after {
content: "";
position: absolute;
left: 0;
top: 0;
width: 200%;
height: 200%;
border: @px solid @color;
color: @color;
-webkit-transform-origin: left top;
transform-origin: left top;
-webkit-transform: scale(0.5,0.5);
transform: scale(0.5,0.5);
pointer-events: none; !* 防止點擊觸發 *!
box-sizing: border-box;
}
}*/
.border-radius(@px) {
border-radius: (@px/@baseSize/2)*1rem;
}
.border-radius(@a,@b,@c,@d) {
border-radius: (@a/@baseSize/2)*1rem (@b/@baseSize/2)*1rem (@c/@baseSize/2)*1rem (@d/@baseSize/2)*1rem;
}
.border-bottom-right-radius(@px) {
border-bottom-right-radius: (@px/@baseSize/2)*1rem;
}
/*右上角圓角*/
.border-top-right-radius(@px) {
border-top-right-radius: (@px/@baseSize/2)*1rem;
}
/*左下角圓角*/
.border-bottom-left-radius(@px) {
border-bottom-left-radius: (@px/@baseSize/2)*1rem;
}
/*左上角圓角*/
.border-top-left-radius(@px) {
border-top-left-radius: (@px/@baseSize/2)*1rem;
}
.border-width(@a,@b,@c,@d) {
border-width: (@a/@baseSize/2)*1rem (@b/@baseSize/2)*1rem (@c/@baseSize/2)*1rem (@d/@baseSize/2)*1rem;
}
.top(@px) {
top: (@px/@baseSize/2)*1rem;
}
.left(@px) {
left: (@px/@baseSize/2)*1rem;
}
.right(@px) {
right: (@px/@baseSize/2)*1rem;
}
.bottom(@px) {
bottom: (@px/@baseSize/2)*1rem;
}
.flex-basis(@px) {
flex-basis: (@px/@baseSize/2)*1rem;
}
// 根元素大小使用 vw 單位
html {
font-size: (@baseSize/(@baseDesign / 2)) * 100vw;
@media screen and (orientation: landscape) {
font-size: (@baseSize/(@baseDesign / 2)) * 100vh;
}
// 同時,通過Media Queries 限制根元素最大最小值
@media screen and (orientation: portrait) and (max-width: 320px) {
font-size: 64px;
}
@media screen and (orientation: portrait) and (min-width: 540px) {
font-size: 108px;
}
/*@media screen and (orientation: landscape) and (min-width: 1023px) and (max-width:1024px) {
font-size: (@baseSize/(@baseDesign / 2)) * 100vw;
}*/
}
@imgPath: "../../assets/images/";
|
目前前端模版大部分采用了此方案,由於頁面使用less寫的樣式,不是scss,所以不能直接通過函數傳入參數轉為對應的rem值,最終的解決辦法是把常用的可能會用到的尺寸屬性全部用rem的形式表示出來,寫一個mixin.less,其他頁面直接使用對應的方法,傳入設計圖尺寸,單位默認為px。
此方法實現的適配,可以很好的做平板的適配,只需要限制最大font-size就可以,對於橫屏下顯示的字體大小設置,個人感覺一個很方便的方法,直接在橫屏下所有的vw改為vh即可,唯獨在頁面是橫屏但寬高差別不大的時候,需要用媒體查詢做個適配,用vw更合理。




