天天看點

web移動端各種适配方案(親測有效)

web移動端最強适配(親測有效)

    • 視口(viewport)适配
    • rem機關适配
        • flexible方案
    • 長屏短屏布局适配
    • ios安全區域(Safe Area)适配
    • 橫豎屏适配
        • CSS Media Queries
        • window.orientation
        • 軟鍵盤的彈出-影響橫豎屏判斷的情況
        • 遊戲接入h5頁面強制豎屏時遇到的問題
        • 橫豎屏旋轉canvas需要重新繪制
    • 常見問題
        • 1px問題
        • 圖檔模糊問題
        • 全屏背景

視口(viewport)适配

meta元素 中繼資料資訊。 告訴浏覽器如何解析頁面。

借助meta元素的viewport來幫助我們設定視口、縮放等,進而讓移動端得到更好的展示效果

web移動端各種适配方案(親測有效)

rem機關适配

flexible方案

核心代碼非常簡單:

// set 1rem = viewWidth / 100
    function setRemUnit() {
        var rem = docEl.clientWidth / 7.5
        docEl.style.fontSize = rem + 'px'
    }
    setRemUnit()
           

豎屏、橫屏、ipad、PC通用:應有盡有,項目不需要的都可以移除

(function (window, document) {
    const os = function() {
        var ua = navigator.userAgent,
            isAndroid = /(?:Android)/.test(ua),
            isIos = /iPad|iPhone|iPod/.test(navigator.platform) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1),
            isTablet = /(?:iPad|PlayBook)/.test(ua) || (isAndroid && !/(?:Mobile)/.test(ua)) || (isFireFox && /(?:Tablet)/.test(ua)),
            isPC = !(/Android|webOS|iPhone|iPod|BlackBerry|isZstar/i.test(ua));
        return {
            isIos: isIos,
            isAndroid: isAndroid,
            isTablet: isTablet,
            isPC: isPC,
        };
    }();
    function resize() {
        var docEl = document.documentElement;
        var clientWidth = docEl.clientWidth;
        var clientHeight = docEl.clientHeight;
        var k = 375;
        if (clientWidth <= k) { //豎屏
            docEl.style.fontSize = clientWidth / 7.5 + "px";
        } else { //橫屏
            docEl.style.fontSize = k / 7.5 + "px";
        }
        if(os.isTablet) { //ipad
            if(clientWidth >= 1366) {
                docEl.style.fontSize = clientWidth / 13.66 + "px";
            } else {
                docEl.style.fontSize = clientWidth / 10.24 + "px";
            }
        }
        if (os.isPC) {
        	if(docEl.clientWidth >= 1920) {
            	docEl.style.fontSize = 1920 / 19.20 + "px";
            } else {
            	docEl.style.fontSize = docEl.clientWidth / 19.20 + "px";
            }
        }
    }
    resize();

    // reset rem unit on page resize
    window.addEventListener("resize", function () {resize()});
    window.addEventListener('pageshow', function (e) {
        if (e.persisted) {
            resize()
        }
    });
}(window, document));
           

長屏短屏布局适配

rem(font size of the root element)是指相對于根元素的字型大小的機關。雖然不同手機的寬高尺寸不一樣,但是設定了root font-size之後,每個手機的寬度都是等量的rem,可以了解為每個手機的寬度都是一樣的,差別在于高度不一樣。

web移動端各種适配方案(親測有效)

【問題】:有些單屏頁面内容很少,在短屏上超出一屏、剛好,在長屏上隻占頁面頂上一小部分,布局會顯得不協調。

【方案】:css 适當擴充間距讓内容撐滿更多高度

1、1.垂直定位盡量用%,不要用固定機關;

ios安全區域(Safe Area)适配

iphone x\xr\xs\11 pro 取消了實體按鍵,改成底部小黑條,這一改動導緻網頁出現了比較尴尬的螢幕适配問題。

對于網頁而言,頂部(劉海部位)的适配問題浏覽器已經做了處理,是以我們隻需要關注底部與小黑條的适配問題即可(即常見的吸底導航、傳回頂部等各種相對底部 fixed 定位的元素)。如下圖:

web移動端各種适配方案(親測有效)

【安全區域】:一個可視視窗範圍,處于安全區域的内容不受圓角(corners)、齊劉海(sensor housing)、小黑條(Home Indicator)影響。

【viewport-fit】:iOS11 新增特性,蘋果公司為了适配 iPhoneX 對現有 viewport meta 标簽的一個擴充,用于設定網頁在可視視窗的布局方式,可設定 三個值:

auto \ contain: 可視視窗完全包含網頁内容(左圖)。頁面内容顯示在safe area内

cover:網頁内容完全覆寫可視視窗(右圖)。頁面内容充滿螢幕。

web移動端各種适配方案(親測有效)

【env() 和 constant()】:iOS11 新增特性,Webkit 的一個 CSS 函數,用于設定安全區域與邊界的距離,有四個預定義的變量:

safe-area-inset-left:安全區域距離左邊邊界距離

safe-area-inset-right:安全區域距離右邊邊界距離

safe-area-inset-top:安全區域距離頂部邊界距離

safe-area-inset-bottom:安全區域距離底部邊界距離

【注意】:

1)網頁預設不添加擴充的表現是 viewport-fit=contain,需要适配 iPhoneX 必須設定 viewport-fit=cover,不然 constant 函數是不起作用的,這是适配的必要條件

2)env() 是官方文檔中提到将來要替換 constant (),目前還不可用。

為此目前我們可以看作 constant:針對iOS < 11.2以下系統,env:針對于iOS >= 11.2的系統

使用的時候 env() 必須寫在 constant () 後面

【***最終的h5、小程式适配代碼】:

第一步:設定網頁在可視視窗的布局方式 (viewport-fit=cover")

第二步:頁面主體内容限定在安全區域内

body {
  padding-top: constant(safe-area-inset-top);  
   padding-top: env(safe-area-inset-top);  
  padding-bottom: constant(safe-area-inset-bottom);
  padding-bottom: env(safe-area-inset-bottom);
}
           

第三步:相對底部 fixed 定位的元素适配

1、fixed吸底 元素(bottom = 0)

<div class="footer ipx-padding-safe-area"></div>
// 或者
<div class="footer ipx-height-safe-area"></div>
// 或者
<div class="footer ipx-margin-safe-area"></div>
<div class="ipx-footer-safe-area"></div>
           
/* 通過加内邊距 padding 擴充高度 */
.ipx-padding-safe-area{
    padding-bottom: constant(safe-area-inset-bottom);
    padding-bottom: env(safe-area-inset-bottom);
}
/* 通過計算函數 calc 覆寫原來高度 */
.ipx-height-safe-area{
    height: calc(1rem +  constant(safe-area-inset-bottom));
    height: calc(1rem +  env(safe-area-inset-bottom));
}
/* 通過加外邊距 margin 增加底部距離 */
.ipx-margin-safe-area{
    margin-bottom: constant(safe-area-inset-bottom);
    margin-bottom: env(safe-area-inset-bottom);
}
/* 
以上方式需要吸底條必須是有背景色的,因為擴充的部分背景是跟随外容器的,否則出現镂空情況。 
還有一種方案就是,可以通過新增一個新的元素(空的顔色塊,主要用于小黑條高度的占位),然後吸底元素可以不改變高度隻需要調整位置
*/
.ipx-footer-safe-area{
	position: fixed;
	bottom: 0;
	width: 100%;
	height: constant(safe-area-inset-bottom);
	height: env(safe-area-inset-bottom);
	background-color: #fff;
}
           

2、fixed 非完全吸底元素(bottom ≠ 0),比如 “傳回頂部”、“側邊廣告” 等

第四步:使用 @supports 隔離相容樣式(可有可無)

@supports (bottom: constant(safe-area-inset-bottom)) or (bottom: env(safe-area-inset-bottom)) {
	/* 通過加内邊距 padding 擴充高度 */
	.ipx-padding-safe-area{
	    padding-bottom: constant(safe-area-inset-bottom);
	    padding-bottom: env(safe-area-inset-bottom);
	}
	...
}
           

橫豎屏适配

CSS Media Queries

通過css媒體查詢判斷橫豎屏,适配樣式:

@media screen and (orientation: portrait) {
	/* 豎屏 */
}
@media screen and (orientation: landscape) {
	/*橫屏 css*/
}
           

window.orientation

通過綁定orientationchange旋轉事件來判斷橫豎屏。

在 iOS 平台以及大部分 Android 手機都有支援這個屬性,它傳回一個與預設螢幕方向偏離的角度值:

0:代表此時是預設螢幕方向

90:代表順時針偏離預設螢幕方向90度

-90:代表逆時針偏離預設螢幕方向90度

180:代表偏離預設螢幕方向180度

如下圖所示:

在實際應用中,對于 iPhone 和大部分 Android 是沒有180度的手機豎屏翻轉的情況的,但是 iPad 是存在的。

function recordOrient() {
	if (window.orientation === 180 || window.orientation === 0) {
    	//豎屏
    } else if (window.orientation === 90 || window.orientation === -90) {
    	// 橫屏
    }
}

recordOrient();
window.addEventListener("onorientationchange" in window ? "orientationchange" : "resize", function () {
	recordOrient();
}, false);
           

軟鍵盤的彈出-影響橫豎屏判斷的情況

在 Android 下,如果頁面中出現軟鍵盤彈出的情況(存在有 Input 的元素)時,頁面有時會因為軟鍵盤的彈出而導緻頁面回縮,即頁面的寬度(豎屏時)或者高度(橫屏時)被改變。

是以通過 CSS多媒體查詢(CSS Media Queries) 、 window.matchMedia() 方法、window.innerWidth /window.innerHeight的頁面寬高比對方法來實作的橫豎屏判斷方法,都會是以受到影響,出現判斷失誤的情況(vivo x9、華為p9、Samsung SCH-i699 機型,在豎屏時由于軟鍵盤彈出導緻頁面高度小于寬度,被錯誤地判定為橫屏)。

在這樣的情況下,這幾種方式也變得不可靠。

css媒體查詢的解決辦法:

CSS 媒體查詢:給一個橫屏的最小寬度,即使高度很小,通過寬度還是可以區分橫豎屏
@media screen and (orientation: portrait) {
	/* 豎屏 */
}
@media screen and (orientation: landscape) and (min-width: 560px) {
	/* 橫屏 */
}
           

遊戲接入h5頁面強制豎屏時遇到的問題

【問題】在 iOS 鎖屏情況下,強制豎屏不成功

【方案】做一個橫屏适配頁,内容可以是提示開啟鎖屏并自動旋轉手機螢幕

【注意】做适配時,如果發現在 iOS平台,橫屏時頁面左右會有預設安全區域空白,我們需要定義頁面寬高等于手機視口寬高

@media screen and (orientation: landscape) and (min-width: 560px) {
    .page{
    	/* 定義頁面寬高等于手機視口寬高 */
        width: 100vw;
        height: 100vh;
        background: #fff;
        position: relative;
    }
}
           

【問題】做了強制橫豎屏,絕對不能使用window.orientation判斷來做橫豎屏适配,因為 ios平台 旋轉螢幕會改變橫豎屏的值進而切換樣式,但視圖布局為始終保持強制的狀态,這就會導緻旋轉螢幕時頁面樣式混亂。

【方案】:還是建議CSS多媒體查詢,比較簡單

橫豎屏旋轉canvas需要重新繪制

【問題】頁面中凡是 canvas繪制而成的局部視圖,橫豎屏旋轉,樣式不會跟着調整,

【方案】監聽旋轉螢幕事件,旋轉後重新整理重繪canvas部分

<template>
<div>
	<template v-if="!isOrientationChange">
		<canvas id="my-canvas"/>
	</template>
</div>
</template>

data() {
	return {
		isOrientationChange: false
	}
},
created() {
	const _this = this;
	window.addEventListener("onorientationchange" in window ? "orientationchange" : "resize", function () {
		_this.isOrientationChange = true;
	    setTimeout(()=> {
	    	_this.isOrientationChange = false;
	    }, 100)
	}, false);
}
           

常見問題

1px問題

【問題】:在裝置像素比大于1的螢幕上,我們寫的1px實際上是被多個實體像素渲染,這就會出現1px在有些螢幕上看起來很粗的現象

【方案】:僞類 + transform 基于media查詢判斷不同的裝置像素比對線條進行縮放

/* 邊框線條 top\bottom */
.border-top, .border-bottom{
    position: relative;
}
.border-top:before{
	content: '';
    position: absolute;
    top: 0;
    height: 1px;
    width: 100%;
    background-color: #000;
    transform-origin: 50% 0%;
}
.border-bottom:before{
	content: '';
    position: absolute;
    bottom: 0;
    height: 1px;
    width: 100%;
    background-color: #000;
    transform-origin: 50% 0%;
}
@media only screen and (-webkit-min-device-pixel-ratio:2){
	.border-top:before, .border-bottom:before{
    	transform: scaleY(0.5);
	}
}
@media only screen and (-webkit-min-device-pixel-ratio:3){
	.border_1px:before, .border-bottom:before{
    	transform: scaleY(0.33);
    }
}
           

圖檔模糊問題

【問題】:我們平時使用的圖檔大多數都屬于位圖(png、jpg…)。在dpr > 1的螢幕上,位圖的一個像素可能由多個實體像素來渲染,然而這些實體像素點并不能被準确的配置設定上對應位圖像素的顔色,隻能取近似值,是以相同的圖檔在dpr > 1的螢幕上就會模糊

【方案】:在dpr=2的螢幕上展示兩倍圖(@2x),在dpr=3的螢幕上展示三倍圖(@3x)

1、媒體查詢 隻适用于背景圖

@media only screen and (-webkit-min-device-pixel-ratio:2){
	.border-top:before, .border-bottom:before{
    	transform: scaleY(0.5);
	}
}
@media only screen and (-webkit-min-device-pixel-ratio:3){
	.border_1px:before, .border-bottom:before{
    	transform: scaleY(0.33);
    }
}
           

2、image-set

.avatar {
    background-image: -webkit-image-set( "conardLi_1x.png" 1x, "conardLi_2x.png" 2x );
}
           

3、使用img标簽的srcset屬性

<img src="conardLi_1x.png"
     srcset=" conardLi_2x.png 2x, conardLi_3x.png 3x">
           

4、使用svg可縮放矢量圖

全屏背景

<div id="parent" class="parent">
	<div class="parent__banner">
    	<img class="parent__img-portrait" src="./img/banner-portrait.png" alt="豎屏背景">
        <img class="parent__img-landscape" src="./img/banner-landscape.png" alt="橫屏背景">
    </div>
</div>
           
.parent{
	/* vw,vh适配ios安全區域頁面留白的漏洞 */
    width: 100vw;
    height: 100vh;
    background-color: #010c0f;
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
}
.parent__banner{
	width: 100vw;
    height: 100vh;
    position: fixed;
    top: 0;
    left: 0;
}
.parent__img-portrait{
	display: block;
    width: 100%;
    height: 100%;
    object-fit:cover;
}

@media screen and (orientation: portrait) {
    /* 豎屏 */
    .parent__img-landscape{
        display: none;
    }
}
@media screen and (orientation: landscape) {
    /*橫屏 css*/
    .parent__img-landscape{
        display: none;
    }
}