移動(dòng)端適配方案

適配

在不同尺寸的移動(dòng)設(shè)備上, 頁面相對(duì)性的達(dá)到合理的展示(自適應(yīng)), 或者保持同一效果的等比縮放(看起來差不多)

適配的元素

  1. 字體
  2. 寬高
  3. 間距
  4. 圖像(圖標(biāo), 圖片)

適配的方法

  1. 百分比適配
  2. viewport 縮放適配
  3. DPR 縮放適配
  4. rem 適配
  5. vw, wh 適配

百分比適配

360手機(jī)站
拉鉤 H5 頁面 頂部底部, 職位列表 都是高度定死, 寬度 100% 自適應(yīng)
高度固定, 寬度百分比, 在高度不能定死的狀況下, 不是很好用, 一般都是配合其他是適配使用
當(dāng)元素為奇數(shù)或者某個(gè)元素占比不均勻的時(shí)候, 不是很好算

360頂部模擬

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="format-detection" content="telephone=no,email=no"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        body {
            font-family: helvetica;
            margin: 0;
        }
        body *{
            -webkit-user-select: none;
            -webkit-text-size-adjust: 100%;
            -webkit-text-size-adjust: 100%;
        }
        a, button, input {
            -webkit-tap-highlight-color: rgba(0,0,0,0)
        }
        button, input{
            -webkit-appearance: none;
            border-radius: 0;
        } 
        input::-webkit-input-placeholder{
            color: #000;
        }
        input:focus::-webkit-input-placeholder{
            color: #f00;
        }
        .header {
            width: 100%;
            height: 48px;
            background: #23ac38;
            display: flex;
            justify-content: space-between;
            align-items: center;
            box-sizing: border-box;
            padding: 0 10px;
        }
        .logo img{
            width: 80px;
        }
        .list img{
            width: 20px;
        }
    </style>
</head>
<body>
    <header class="header">
        <span class="logo"><img src="http://p2.qhmsg.com/t01ecc3b6b24e7bdbd8.png" alt=""></span>
        <span class="list"><img src="http://p9.qhmsg.com/t010fa93a99715aad32.png" alt=""></span>
    </header>
</body>
</html>

viewport 適配

把所有機(jī)型的css像素設(shè)置成一致的

  1. viewport 需要使用 js 動(dòng)態(tài)設(shè)置, (不能直接把 device 的值設(shè)置為數(shù)值)
  2. 通過設(shè)置比例(初始比例以及縮放比例), 把寬度縮放成一致
    縮放比 = css 像素 / 375
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="format-detection" content="telephone=no,email=no" />
    <meta name="viewport"
        content="width=device-width, initial-scale=1.0, user-scalable=no, maximum-scale=1.0, minimum-scale=1.0" id="view">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        body {
            font-family: helvetica;
            margin: 0;
        }

        body * {
            -webkit-user-select: none;
            -webkit-text-size-adjust: 100%;
            -webkit-text-size-adjust: 100%;
        }
        a, button, input {
            -webkit-tap-highlight-color: rgba(0, 0, 0, 0)
        }
        button, input {
            -webkit-appearance: none;
            border-radius: 0;
        }

        input::-webkit-input-placeholder {
            color: #000;
        }
        input:focus::-webkit-input-placeholder {
            color: #f00;
        }
        div {
            width: 75px;
            height: 100px;
            float: left;
        }
        div:nth-child(1) {
            background: #f00;
        }
        div:nth-child(2) {
            background: #ff0;
        }
        div:nth-child(3) {
            background: #f0f;
        }
        div:nth-child(4) {
            background: #0ff;
        } 
        div:nth-child(5) {
            background: #0f0;
        }
    </style>
    <script>
        ; (function () {
            /* 獲取 css 像素 (viewport沒有縮放, initial-scale=1.0)*/
            var curWidth = document.documentElement.clientWidth
            var curWidth = window.innerWidth
            var curWidth = window.screen.width
            /* 以上三種方式都可以準(zhǔn)確的獲取到 html 的 width */
            var targetWidth = 375 // 目標(biāo)值
            var scale = curWidth / targetWidth
            var meta = document.getElementById('view')
            var content = 'initial-scale=' + scale + ', user-scalable=no, maximum-scalable=' + scale + ', minimum-scalable=' + scale
            meta.content = content
        })()
    </script>
</head>
<body>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
</body>
</html>

但是這種肯定是有問題的
ipad 上, 寬度為 768, pro1024, 一張 375 的圖片放上去 ...

DPR 適配

把 CSS 像素縮放成與設(shè)備像素一樣大的尺寸
只有在 PC 端這兩個(gè)值才是對(duì)應(yīng)的 1css像素 = 1物理像素

iphone6 的物理像素 750 * 1334. 通過縮放, 將 CSS 像素的 375 * 667 縮放, 按照設(shè)計(jì)稿 750 切圖.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="format-detection" content="telephone=no,email=no" />
    <meta name="viewport"
        content="width=device-width, initial-scale=1.0, user-scalable=no, maximum-scale=1.0, minimum-scale=1.0" id="view">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        body {
            font-family: helvetica;
            margin: 0;
        }

        body * {
            -webkit-user-select: none;
            -webkit-text-size-adjust: 100%;
            -webkit-text-size-adjust: 100%;
        }
        a, button, input {
            -webkit-tap-highlight-color: rgba(0, 0, 0, 0)
        }
        button, input {
            -webkit-appearance: none;
            border-radius: 0;
        }

        input::-webkit-input-placeholder {
            color: #000;
        }
        input:focus::-webkit-input-placeholder {
            color: #f00;
        }
        div {
            width: 20%;
            height: 100px;
            float: left;
        }
        div:nth-child(1) {
            background: #f00;
        }
        div:nth-child(2) {
            background: #ff0;
        }
        div:nth-child(3) {
            background: #f0f;
        }
        div:nth-child(4) {
            background: #0ff;
        } 
        div:nth-child(5) {
            background: #0f0;
        }
    </style>
    <script>
        ; (function () {
            /*
                要將 375 => 750 就是 375 / 0.5 dpr = 2
                375 / ? = 750  這個(gè) ? 就是 dpr 的倒數(shù)
            */
            var meta = document.querySelector('meta[name="viewport"]')
            var scale = 1 / window.devicePixelRatio
            if(!meta){ // 沒有默認(rèn)設(shè)置 viewport 的 meta, 創(chuàng)建
                meta = document.createElement('meta')
                meta.name = 'viewport'
                meta.content = 'width=device-width, initial-scale=' + scale + ', user-scalable=no, maximum-scalable=' + scale + ', minimum-scalable=' + scale
                document.head.appendChild(meta)
            } else { // 有默認(rèn)設(shè)置 viewport 的 meta, 修改  
                meta.setAttribute('content', 'width=device-width, initial-scale=' + scale + ', user-scalable=no, maximum-scalable=' + scale + ', minimum-scalable=' + scale)
            }
        })()
    </script>
</head>
<body>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
    <div></div>
</body>
</html>

雖然一個(gè)CSS 像素對(duì)應(yīng)了一個(gè)物理像素, 但是對(duì)于不同尺寸的設(shè)備, 依然不是一個(gè)好的解決方案, 需要 rem 進(jìn)行配合

rem 適配

把所有的設(shè)備分成相同的若干份, 再計(jì)算元素寬度所占的份數(shù)

em: 當(dāng)意義為 font-size 的時(shí)候, 1em 代表父元素的字體大小, 當(dāng)作為其他單位(寬度高度)的時(shí)候, 代表自身字體大小
chrome 下有最小字體限制 12px, 字體小于 12px 無法再變小

rem: r root, 根元素的 font-sizehtmlfont-size 設(shè)置為 20px, 1rem = 20px

  1. 元素適配的寬度 = 元素所占的列數(shù) * 一列的寬度
  2. 元素在設(shè)計(jì)稿里面的寬度
  3. 列數(shù) (隨便給的) 100
  4. 一列的寬度 = 屏幕寬度(css像素) / 列數(shù)
  5. 元素實(shí)際占的列數(shù) = 設(shè)計(jì)稿里面的寬度 / 一列的寬度
var colWidth = 0
var col = 100   // 假設(shè) 100 列
// 計(jì)算 iPhone5 iPhone6里面一列的寬度
colWidth = 375 / col // 3.75px 6
colWidth = 320 / col // 3.20px 5

// 一個(gè) div 占 10 列
var width = 10 * 3.75 // 37.5px 6
var width = 10 * 3.20 // 32.0px 5

// 一個(gè) div 50px 
divCol = 50 / 3.75 // 13.333
divCol = 50 / 3.20 // 15.625

// 所以按照此方案, 50px 的 div, 在不同的設(shè)備里, 會(huì)占到不同的份數(shù)

屏幕已經(jīng)被分成了若干份
那么 width: 10rem; (寫入 CSS 文件的代碼)
元素適配的寬度 => 元素所占的列數(shù) * 一列的寬
元素適配的寬度 => width 10 * 1rem
元素所占的列數(shù) => 10
一列的寬 => 1rem

1rem = htmlfont-size

根據(jù)柵格系統(tǒng)的原理, 將屏幕分成若干份, 不同的屏幕被分成相同的份數(shù), 所以一份的寬度會(huì)不一樣
但是份數(shù)是一樣的, 所以整體比例是一樣的

 ; (function (fs) {
            var html = document.documentElement
            var width = html.clientWidth    // css 像素
            html.style.fontSize = width / fs + 'px'
            // 分成 16 列, iPhone5 為例 320px 得到 1rem = 20px
         })(16)

這種方式并沒有一個(gè)基準(zhǔn)點(diǎn), 只是按照屏幕的增大, htmlfont-size 在變大
我們希望增加一個(gè)基準(zhǔn)點(diǎn), 這個(gè)點(diǎn)就是 iPhone6, 屏幕仍然分成 16
如果屏幕尺寸為 375, 那么就 html 字體也是16px
如果屏幕小于 375font-size 也會(huì)小于 16px
反之, 大于 16px
如下代碼:

; (function (doc, win, designWidth) {
    const html = doc.documentElement
    const refreshDom = () => {
        const clientWidth = html.clientWidth
        if(clientWidth >= designWidth){
            html.style.fontSize = "100px"
        }else{
            html.style.fontSize = 16 * clientWidth / 375 + 'px'
        }
    }
    doc.addEventListener('DOMContentLoaded', refreshDom)
})(document, window, 750)

但是這樣做有一個(gè)弊端, 當(dāng)計(jì)算出了 rem 值之后, 還要除以 dpr, 才是真正應(yīng)該寫的值
而且很多是除不盡的, 跟著一大串小數(shù), 雖然有 less sass 去做這些事情, 但是還是覺得美中不足

rem 最終方案

; (function (doc, win, designWidth) {
    const html = doc.documentElement
    const refreshDom = () => {
        const clientWidth = html.clientWidth
        if(clientWidth >= designWidth){
            html.style.fontSize = "100px"
        }else{
            html.style.fontSize = 100 * (clientWidth / designWidth) + 'px'
        }
    }
    doc.addEventListener('DOMContentLoaded', refreshDom)
})(document, window, 750)

只有一行代碼不同
那么html.style.fontSize = 100 * (clientWidth / designWidth) + 'px' 又是什么意思呢 ?
基于 iPhone6 的尺寸, 將整個(gè)頁面適配成7.5 rem.
所以, 只要設(shè)計(jì)稿是750px, 在切圖的時(shí)候, 量出來出多少px, 直接將這個(gè)值除以 100, 加上remok
這個(gè)時(shí)候, 屏幕被分割成多少份這個(gè)概念已經(jīng)不怎么明朗了, 說是7.5份嗎? 好像不太對(duì)勁. 那就索性不管了.

如果設(shè)計(jì)圖是 640px, 那么整屏的寬度就是 6.4rem, 只需要將參數(shù)修改一下就可以了.
但是上面的代碼是用到了 ES6 的語法, ios9 不是原生支持的, 直接在上面跑會(huì)出現(xiàn)很大的問題, 就相當(dāng)于適配沒有做一樣.
所以改寫吧:

; (function (doc, win, designWidth) {
           var html = doc.documentElement
           function refreshDom(){
               var clientWidth = html.clientWidth
               if(clientWidth >= designWidth){
                   html.style.fontSize = "100px"
               }else{
                   html.style.fontSize = 100 * (clientWidth / designWidth) + 'px'
               }
           }
           doc.addEventListener('DOMContentLoaded', refreshDom)
         })(document, window, 750)

這種方案總體來說沒什么大問題了. 如果在設(shè)計(jì)稿上量出來長度為50px, 那么實(shí)際寫上去的長度應(yīng)該是 50 / 2 = 25px. 因?yàn)?750 的設(shè)計(jì)稿, 375px就會(huì)占滿屏幕. 或者是寫.5rem.

字體也要用標(biāo)注的字體大小除以 2, 如標(biāo)注 28px, 實(shí)則應(yīng)該寫 14px, 或者 .28rem

hotcss

大佬寫的號(hào)稱移動(dòng)端終極解決方案, 原理跟如上一樣, 只是考慮的東西更多, 功能更強(qiáng)大.
github 上下載或者 clone , 地址是: https://github.com/imochen/hotcss
需要做一點(diǎn)點(diǎn)小小的處理, 將第十四行左右的 maxWidth = 540, 修改為設(shè)計(jì)稿的尺寸, 這個(gè) 540 是幾年前寫的, 相較現(xiàn)在來說有點(diǎn)小了.

然后在頁面中引入這個(gè) hotcss.js. 使用 sass.
在寫 scss 的地方引入 px2remscss 文件, 然后就可以在scss上愉快的寫代碼了.

量出來多少 (100px) 就直接 px2rem(100)

vw, vh 適配

這倆單位就是為適配而生的

vw: viewport's width 1vw = 視口寬度的 1%
vh:viewport's height 1vh = 視口高度的 1%
vmax: 取 vw, vh 中的最大值
vmin: 取 vw, vh 中的最小值

支持情況: ios >= 8; android >= 4.4

瀏覽器將任何屏幕都分成 100 份, 只要使用此單位, 就不會(huì)翻車

兩種方案

方案一: 通篇使用vw

@function vw($px){
    @return $px / 750 * 100vw;
}

為方便切圖, 寫了一個(gè)如上函數(shù)(scss), 以 750 為基準(zhǔn), 在任何屏幕上都可以看做是'750'
因?yàn)槔绱撕瘮?shù)傳 250 進(jìn)去, 會(huì)被轉(zhuǎn)換成 33.33333vw, 三個(gè)這樣的大小將占滿屏幕寬度, 在任何尺寸屏幕上的都是這樣.

方案二: 通過 vw 設(shè)置根節(jié)點(diǎn)的字體大小, 頁面尺寸依然使用 rem

一個(gè)很暴力的方式, 還是以 750 為基準(zhǔn). 375(css像素) / 750(設(shè)計(jì)稿寬度) * 100 這是 rem 適配的原理所在
這個(gè)值最終是 50, 這個(gè)值就是 rem 適配中, html 的字體大小 .
那通過 vw 適配, 要達(dá)到 rem 同樣的適配效果, 是不是應(yīng)該讓 ?vw = 50px ?
所以 50 / 3.75 = 13.333333333333334 (15位小數(shù))
然后:

html {
    font-size: 13.333333333333334vw;
}

然后, rem 適配需要的 js 代碼可以刪除了. 然后在任意尺寸的屏幕上也是可以橫著走的.
整個(gè)屏幕 7.5rem. 與 rem 適配的結(jié)果都是一樣的
需要注意一些問題:

  1. css 尺寸寫 rem, 比如 1/4 屏幕大小的 div, width: 1.875rem;
  2. htmlfont-size 會(huì)帶來負(fù)面影響. 很多有文本屬性(受font-size影響)的標(biāo)簽里面的內(nèi)容會(huì)繼承 html 的字體大小, 13.33vw 字體其實(shí)是非常大的, 所以不處理的話, 頁面將會(huì)亂套. imgdisplay屬性默認(rèn)是 inline-block, 它會(huì)根據(jù)字體來對(duì)齊. 如果字體繼承了 html. 圖片就會(huì)跑到一個(gè)匪夷所思的地方. 這個(gè)時(shí)候需要將img的包裹層的 font-size 做出相應(yīng)的調(diào)整
  3. 比較一勞永逸的方法, 在該設(shè)置的字體的地方設(shè)置字體大小, 然后最主要的是, 在body里面, 將 font-size 設(shè)置為chrome最小字體 12px, 阻止頁面的元素去繼承html的字體大小. 即現(xiàn)在 html 的字體大小只能影響 rem 這個(gè)單位.

這些問題逐一解決之后, 這也是一個(gè)很好的方案.

完...

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容