細說移動端 經典的REM佈局 與 新秀VW佈局

来源:https://www.cnblogs.com/imwtr/archive/2018/09/15/9648233.html
-Advertisement-
Play Games

和以往一樣,本次項目也放到了 Github 中,歡迎圍觀 star ~ 1. 前言 2. 基本概念 3. REM佈局 4. VW佈局 實現單邊邊框1px 實現多邊邊框1px 實現邊框圓角 實現容器固定縱橫比 5. REM + VW佈局 6. 對比選擇 方案選擇 食用方式 一、前言 說到前端頁面的佈局 ...


和以往一樣,本次項目也放到了 Github 中,歡迎圍觀 star ~

 

1. 前言

2. 基本概念

3. REM佈局

4. VW佈局

 實現單邊邊框1px

 實現多邊邊框1px

    實現邊框圓角

 實現容器固定縱橫比

5. REM + VW佈局

6. 對比選擇

    方案選擇

    食用方式

 

一、前言

說到前端頁面的佈局方案,可以從遠古時代的Table佈局說起,然後來到 DIV+CSS佈局,之後有了Float佈局,Flex佈局,Column佈局,Grid佈局等等。

而另一方面,還有一些 佈局概念

1. 靜態佈局

直接使用px作為單位

2. 流式佈局

寬度使用%百分比,高度使用px作為單位

3. 自適應佈局

創建多個靜態佈局,每個靜態佈局對應一個屏幕解析度範圍。使用 @media媒體查詢來切換多個佈局

4. 響應式佈局

通常是糅合了流式佈局+彈性佈局,再搭配媒體查詢技術使用

5. 彈性佈局

通常指的是rem或em佈局。rem是相對於html元素的font-size大小而言的,而em是相對於其父元素(非font-size的是相對於自身的font-size)

 

本文不對這些概念做太多的解釋說明,主要記錄一下整理過程中比較重要的點

如今移動端佈局中免不了要支持高清設備,機型也比較複雜,需要一套比較完善的佈局方案來支持(在整體結構上解決多設備寬的適配問題)。

淘寶的 Flexible 讓REM佈局得以流行開來,而此Flexible實現也有一些不足,此外,也涌現出了多種實現REM佈局的方案

比如直接使用  html{ font-size:625%; } 基準值,配合JS來設置根元素字體大小

或者使用媒體查詢來設置根元素字體大小

  @media screen and (min-width: 320px) {
        html,body,button,input,select,textarea {
            font-size:12px!important;
        }
    }

    @media screen and (min-width: 374px) {
        html,body,button,input,select,textarea {
            font-size:14px!important;
        }
    }

 

但使用rem來佈局的方案並不太正統,它有一些hack的特點

比較規範的方式是使用vw單位,隨之而來的就是後起之秀 VW佈局

 

花了一些時間整理了REM佈局和VW佈局在實際頁面中是如何運用的,如果你有興趣,就往下看吧~

項目地址,歡迎圍觀~

 

二、基本概念

物理像素(physical pixel)

物理像素又被稱為設備像素,它是顯示設備中一個最微小的物理部件。每個像素可以根據操作系統設置自己的顏色和亮度。正是這些設備像素的微小距離欺騙了我們肉眼看到的圖像效果。

設備獨立像素(density-independent pixel)

設備獨立像素也稱為密度無關像素,可以認為是電腦坐標系統中的一個點,這個點代表一個可以由程式使用的虛擬像素(比如說CSS像素),然後由相關係統轉換為物理像素。

CSS像素

CSS像素是一個抽像的單位,主要使用在瀏覽器上,用來精確度量Web頁面上的內容。一般情況之下,CSS像素稱為與設備無關的像素(device-independent pixel),簡稱DIPs。

屏幕密度

屏幕密度是指一個設備錶面上存在的像素數量,它通常以每英寸有多少像素來計算(PPI)。

設備像素比(device pixel ratio)

設備像素比簡稱為dpr,其定義了物理像素和設備獨立像素的對應關係。它的值可以按下麵的公式計算得到:

設備像素比 = 物理像素 / 設備獨立像素

在Javascript中,可以通過 window.devicePixelRatio 獲取到當前設備的dpr。

在css中,可以通過 -webkit-device-pixel-ratio-webkit-min-device-pixel-ratio和 -webkit-max-device-pixel-ratio進行媒體查詢,對不同dpr的設備,做一些樣式適配。

或者使用 resolution | min-resolution | max-resolution 這些比較新的標準方式

 

 

上圖中, Retina為高清設備屏幕,它的一個css像素對應 了4個物理像素

 

點陣圖像素

一個點陣圖像素是柵格圖像(如:png, jpg, gif等)最小的數據單元。每一個點陣圖像素都包含著一些自身的顯示信息(如:顯示位置,顏色值,透明度等)。

理論上,1個點陣圖像素對應於1個物理像素,圖片才能得到完美清晰的展示

如上圖:對於dpr=2的retina屏幕而言,1個點陣圖像素對應於4個物理像素,由於單個點陣圖像素不可以再進一步分割,所以只能就近取色,從而導致圖片模糊(註意上述的幾個顏色值)。

所以,對於圖片高清問題,比較好的方案就是兩倍圖片(@2x)。

如:200×300(css pixel)img標簽,就需要提供400×600的圖片。

縮放比 scale

縮放比:scale = 1/dpr

視窗 viewport

簡單的理解,viewport是嚴格等於瀏覽器的視窗。在桌面瀏覽器中,viewport就是瀏覽器視窗的寬度高度。但在移動端設備上就有點複雜。

移動端的viewport太窄,為了能更好為CSS佈局服務,所以提供了兩個viewport:虛擬的visualviewport和佈局的layoutviewport。

viewport的內容比較深,推薦閱讀PPK寫的文章,以及中文翻譯

視窗縮放 viewport scale

在開發移動端頁面,我們可以設置meta標簽的viewport scale來對視窗的大小進行縮放定義

<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">

rem單位

font size of the root element.

rem就是相對於根元素<html>font-size來做計算

視窗單位

  • vw : 1vw 等於視窗寬度的1%
  • vh : 1vh 等於視窗高度的1%
  • vmin : 選取 vw 和 vh 中最小的那個
  • vmax : 選取 vw 和 vh 中最大的那個

 

相容性:在移動端 iOS 8 以上以及 Android 4.4 以上獲得支持

可以去 Can I usecss3test 查看相容情況

 

三、REM佈局

講的太亂了?自己去看代碼

rem佈局的核心是設置好根html元素的font-size

一般來說,為了防止在高清屏幕下像素不夠用導致模糊,我們拿到的設計稿是640px(iphone5 設備寬為320px)或750px的兩倍稿(iphone6 設備寬為375px),按照設備寬度做了兩倍的大小。

那開發的時候在CSS中要設置什麼尺寸呢,如何做到一份設計稿適配到不同機型中

最佳方案是:在photoshop或其他工具中量出某個元素或圖片或文字的尺寸,然後直接寫到代碼中。額外的適配不需要理會。

width: px2rem(200);

基於此,可以使用SCSS來提供一系列的基礎支持

/* 移動端頁面設計稿寬度 */
$design-width: 750;
/* 移動端頁面設計稿dpr基準值 */
$design-dpr: 2;
/* 將移動端頁面分為10塊 */
$blocks: 10;
/* 縮放所支持的設備最小寬度 */
$min-device-width: 320px;
/* 縮放所支持的設備最大寬度 */
$max-device-width: 540px;

/*
    rem與px對應關係,1rem代表在JS中設置的html font-size值(為一塊的寬度),$rem即為$px對應占多少塊

        $px                     $rem
    -------------    ===    ------------
    $design-width              $blocks
*/

/* 單位px轉化為rem */
@function px2rem($px) {
    @return #{$px / $design-width * $blocks}rem;
}

/* 單位rem轉化為px,可用於根據rem單位快速計算原px */
@function rem2px($rem) {
    @return #{$rem / $blocks * $design-width}px;
}

為了便於計算,我們將頁面分為10個塊,根據映射關係,我們只需要計算某個元素在頁面中占了多少塊($rem),結合html中font-size的大小,就能在頁面上設置好正確的元素大小

在對應的JS文件中

var docElem = document.documentElement,
        metaElem = document.querySelector('meta[name="viewport"]'),
        dpr = window.devicePixelRatio || 1,
        // 將頁面分為10塊
        blocks = 10,
        // 需要限制的最小寬度
        defaultMinWidth = 320,
        // 需要限制的最大寬度
        defaultMaxWidth = 540,
        // 計算的基準值
        calcMaxWidth = 9999999;

將頁面按照clientWidth進行分割成塊,和CSS對應起來

// 設置docElem字體大小
    function setFontSize() {
        var clientWidth = docElem.clientWidth;

        clientWidth = Math.max(clientWidth, defaultMinWidth * dpr)

        // 調整計算基準值
        if (calcMaxWidth === defaultMaxWidth) {
            clientWidth = Math.min(clientWidth, defaultMaxWidth * dpr);
        }

        docElem.style.fontSize = clientWidth / blocks + 'px';
    }

    setFontSize();

    window.addEventListener(window.orientationchange ? 'orientationchange' : 'resize', setFontSize, false);

 

1px在高清屏幕中的顯示問題

上圖左邊設置了css為1px的效果,實際上我們需要的是右邊的效果

明顯左邊的粗了一些,因為此時1個css像素包含了4個(dpr為2)物理像素,實際需要的是1px的物理像素,而非css像素

為瞭解決這個問題,有很多方法

在REM佈局中普遍採用的是viewport scale 視窗縮放的方式

視窗縮放很簡單,其實就是直接將meta標簽中的scale進行更改。比如dpr為3,則scale為

但縮放在某些安卓設備中支持度不太好,我們還需要做其他檢測(檢測了現用的一些機型,應該還不完整哈)

// 大部分dpr為2以下的安卓機型不識別scale,需設置不縮放
    if (navigator.appVersion.match(/android/gi) && dpr <= 2) {
        dpr = 1;
    }

    setScale(dpr);

    // 企業QQ設置了scale後,不能完全識別scale(此時clientWidth未收到縮放的影響而翻倍),需設置不縮放
    if (navigator.appVersion.match(/qq\//gi) && docElem.clientWidth <= 360) {
        dpr = 1;
        setScale(dpr);
    }

    docElem.setAttribute('data-dpr', dpr);

    // 設置縮放
    function setScale(dpr) {
        metaElem.setAttribute('content', 'initial-scale=' + 1 / dpr + ',maximum-scale=' + 1 / dpr + ',minimum-scale=' + 1 / dpr + ',user-scalable=no');
    }

同時將最終計算的dpr放到html中,供css做一些特殊適配。看看頁面效果

 

設置容器的最大最小寬度

上圖中,隨著拉伸,內容區越來越大,各元素尺寸也越來越大。已經進行了最小寬度的處理。

要控制縮放的程度,關鍵有兩個點:尺寸計算基準、容器寬度

<!DOCTYPE html>
<html>
    <head>
        <title>REM佈局</title>
        <meta charset="utf-8">
        <meta lang="zh-CN">
        <meta name="viewport" data-content-max content="width=device-width,initial-scale=1,user-scalable=no">
        <link rel="stylesheet" href="./rem.css">
        <script src="./rem.js"></script>
    </head>

    <body data-content-max>
        <section class="container">

尺寸計算基準位於 meta標簽中的 data-content-max,容器寬度位於 body標簽中

在JS中進行匹配控制,需要註意的是,因為我們已經進行了視窗的縮放,clientWidth將會比設備寬度大,要記得以dpr進行翻倍

    // 需要限制的最小寬度
    var defaultMinWidth = 320,
        // 需要限制的最大寬度
        defaultMaxWidth = 540,
        // 計算的基準值
        calcMaxWidth = 9999999;


    if (metaElem.getAttribute('data-content-max') !== null) {
        calcMaxWidth = defaultMaxWidth;
    }


    ...

    // 設置docElem字體大小
    function setFontSize() {
        var clientWidth = docElem.clientWidth;

        clientWidth = Math.max(clientWidth, defaultMinWidth * dpr)

        // 調整計算基準值
        if (calcMaxWidth === defaultMaxWidth) {
            clientWidth = Math.min(clientWidth, defaultMaxWidth * dpr);
        }

        docElem.style.fontSize = clientWidth / blocks + 'px';
    }

在CSS中,簡單地調用一下,核心方法已經抽離

html {
    @include root-width();
}
/* html根的寬度定義 */
@mixin root-width() {
    body {
        @include container-min-width();

        &[data-content-max] {
            @include container-max-width();
        }
    }

    /* 某些機型雖然設備dpr大於1,但識別不了scale縮放,這裡需要重新設置最小寬度防止出現橫向滾動條 */
    &[data-dpr="1"] body {
        min-width: $min-device-width;
    }
}

/* 設置容器拉伸的最小寬度 */
@mixin container-min-width() {
    margin-right: auto;
    margin-left: auto;
    min-width: $min-device-width;

    @media (-webkit-device-pixel-ratio: 2) {
        min-width: $min-device-width * 2;
    }

    @media (-webkit-device-pixel-ratio: 3) {
        min-width: $min-device-width * 3;
    }
}

/* 設置容器拉伸的最大寬度 */
@mixin container-max-width() {
    margin-right: auto;
    margin-left: auto;
    max-width: $max-device-width;

    @media (-webkit-device-pixel-ratio: 2) {
        max-width: $max-device-width * 2;
    }

    @media (-webkit-device-pixel-ratio: 3) {
        max-width: $max-device-width * 3;
    }
}

要註意的是,這裡的max-width也要配上dpr繫數

看看成果圖

 

如果僅僅限制計算基準值,容器不限制(將body標簽中的屬性去掉),就可以實現某種流式效果(另一種方案)

 

 文本大小是否用rem單位

有時我們不希望文本在Retina屏幕下變小,另外,我們希望在大屏手機上看到更多文本,以及,現在絕大多數的字體文件都自帶一些點陣尺寸,通常是16px和24px,所以我們不希望出現13px和15px這樣的奇葩尺寸。

我們可以選擇使用px直接定義

/* 設置字體大小,不使用rem單位, 根據dpr值分段調整 */
@mixin font-size($fontSize) {
    font-size: $fontSize / $design-dpr;

    [data-dpr="2"] & {
        font-size: $fontSize / $design-dpr * 2;
    }

    [data-dpr="3"] & {
        font-size: $fontSize / $design-dpr * 3;
    }
}
@include font-size(30px);

當然了,如果要求不嚴格,也可以直接使用rem單位

 

四、VW佈局

講的太亂了?自己去看代碼

REM佈局中用到了JS來動態設置html的font-size,可能造成頁面的抖動。

可以考慮比較新的VW佈局,無需使用JS,雖說在移動端 iOS 8 以上以及 Android 4.4 以上才獲得支持,不過還是值得一用的。如果需要相容,可以嘗試 viewport-units-buggyfill

在REM佈局中處理1px問題是用了視窗縮放的方案,在VW佈局中就不用了,轉而使用容器縮放(transform)的方案

 

調用方式形如

height: px2vw(300);

同樣的,我們需要寫個轉換方法

/* 移動端頁面設計稿寬度 */
$design-width: 750;
/* 移動端頁面設計稿dpr基準值 */
$design-dpr: 2;

/*
    vw與px對應關係,100vw為視窗寬度,$vw即為$px對應占多寬

        $px                    $vw
    -------------    ===    ------------
    $design-width              100vw
*/

/* 單位px轉化為vw */
@function px2vw($px) {
    @return ($px / $design-width) * 100vw;
}

/* 單位vw轉化為px,可用於根據vw單位快速計算原px */
@function vw2px($vw) {
    @return #{($vw / 100) * $design-width}px;
}

對於高清屏幕邊框1px問題,有三個方面需要考慮

1. 單邊邊框

2. 多邊邊框

3. 邊框的圓角

 

1. 單邊邊框比較簡單,本質是在目標元素上加個偽類,設置寬度(左|右邊框)或高度(上|下邊框)為1px,然後在高清屏幕下對齊進行縮放

transform-origin: 0 0;
transform: scaleY(.5);

 

2. 要讓偽類支持設置多邊邊框,已經不能僅僅使用寬度或高度,而應該在這個偽類上設置多邊邊框,然後設置dpr倍的寬高,再進行縮放(自左上方)

width: 200%;
height: 200%;

transform-origin: top left;
transform: scale(.5, .5);

 

3. 邊框圓角一般作用於多邊邊框,使用了偽類設置邊框之後,元素本身並沒有邊框,所以我們需要對偽類設置圓角,此外,也需要對元素本身設置圓角

否則就會出現這種尷尬的情況

 

如果只是需要設置圓角,其實也可以不設置邊框,可以使用背景顏色來營造出這種“邊框”的分界,在VW佈局中,顯示地設置邊框可能會造成代碼量太多

另外要註意的是,圓角如果設置為像素值(比如50px),在不同的dpr下它產生的圓角效果還是有區別的,所以最好也把dpr作為繫數放在圓角中

 

針對上面三種情況,我們需要寫好一個scss的1px邊框生成器

先來看看怎麼調用

/* 底部單個邊框 */
.f-border-bottom {
    @include border(
        $direction: bottom,
        $size: 1px,
        $color: #ddd,
        $style: solid
    );
}
/* 常規多邊邊框 */
.f-border {
    @include border(
        $direction: all,
        $size: 1px,
        $color: #ddd,
        $style: solid
    );
}
/* 多個邊框不同的屬性 */
    &.hover {
        @include border(
            $direction: (top, right, bottom, left),
            $size: (3px, 2px, 1px),
            $color: (#0f0, #ddd),
            $style: dotted
        );
    }

 

/* 圓角邊框百分比 */
.f-border-radius {
    @include border(
        $direction: all,
        $radius: 50%
    );
}
/* 圓角邊框自定義多個角,順序 */
.f-border-radius {
    @include border(
        $radius: (10px, 20px, 30px, 40px)
    );
}
/* 多個邊框調用 */
    &:not(.info-item__tel) {
        @include border(
            $direction: all,
            $size: 1px,
            $color: #ddd,
            $style: solid,
            $radius: 50px
        );
    }

看起來調用方式還是有點複雜的,不過應該也還好吧,實在是實現不了像scale縮放那樣直接寫原生border屬性,除非使用構建工具了

這個 border生成器 是怎麼實現的呢? Show you the code ..

/**
 * 元素邊框
 * @param  {string|list} $direction: all           為all或列表時表示多個方向的邊框,否則為單個邊框
 * @param  {string|list} $size:      1px           邊框尺寸,為列表時表將按照direction的順序取值
 * @param  {string|list} $style:     solid         邊框樣式,高清設備下僅支持solid,同上
 * @param  {string|list} $color:     #ddd          邊框顏色,同上
 * @param  {string}      $position:  relative      元素定位方式,一般為relative即可
 * @param  {string}      $radius:    0             邊框圓角
 */
@mixin border(
    $direction: all,
    $size: 1px,
    $style: solid,
    $color: #ddd,
    $position: relative,
    $radius: 0
) {
    /* 多個邊框 */
    @if $direction == all or type-of($direction) == list {
        /* 普通設備 */
        @media not screen and (-webkit-min-device-pixel-ratio: 2) {
            @include border-radius($radius);

            @if $direction == all {
                border: $size $style $color;
            }
            @else {
                @for $i from 1 through length($direction) {
                    $item: nth($direction, $i);

                    border-#{$item}: getBorderItemValue($size, $i) getBorderItemValue($style, $i) getBorderItemValue($color, $i);
                }
            }
        }

        /* 高清設備 */
        @media only screen and (-webkit-min-device-pixel-ratio: 2) {
            @include border-multiple(
                $direction: $direction,
                $size: $size,
                $color: $color,
                $position: $position,
                $radius: $radius
            );
        }
    }

    /* 單個邊框 */
    @else {
        /* 普通設備 */
        @media not screen and (-webkit-min-device-pixel-ratio: 2) {
            border-#{$direction}: $size $style $color;
        }

        /* 高清設備 */
        @media only screen and (-webkit-min-device-pixel-ratio: 2) {
            @include border-single(
                $direction: $direction,
                $size: $size,
                $color: $color,
                $position: $position
            );
        }
    }
}

入口 mixin 判斷設備的dpr,然後選擇直接生成border代碼,或者分發到 border-single 和 border-multiple 中進行高清屏幕的處理

 

/* 實現1物理像素的單條邊框線 */
@mixin border-single(
    $direction: bottom,
    $size: 1px,
    $color: #ddd,
    $position: relative
) {
    position: $position;

    &:after {
        content: "";
        position: absolute;
        #{$direction}: 0;

        pointer-events: none;
        background-color: $color;

        @media only screen and (-webkit-min-device-pixel-ratio: 2) {
            -webkit-transform-origin: 0 0;
                    transform-origin: 0 0;
        }

        /* 上下 */
        @if ($direction == top or $direction == bottom) {
            left: 0;
            width: 100%;
            height: $size;

            @media only screen and (-webkit-device-pixel-ratio: 2) {
                -webkit-transform: scaleY(.5);
                        transform: scaleY(.5);
            }

            @media only screen and (-webkit-device-pixel-ratio: 3) {
                -webkit-transform: scaleY(.333333333333);
                        transform: scaleY(.333333333333);
            }
        }

        /* 左右 */
        @elseif ($direction == left or $direction == right) {
            top: 0;
            width: $size;
            height: 100%;

            @media only screen and (-webkit-device-pixel-ratio: 2) {
                -webkit-transform: scaleX(.5);
                        transform: scaleX(.5);
            }

            @media only screen and (-webkit-device-pixel-ratio: 3) {
                -webkit-transform: scaleX(.333333333333);
                        transform: scaleX(.333333333333);
            }
        }
    }
}
/* 實現1物理像素的多條邊框線 */
@mixin border-multiple(
    $direction: all,
    $size: 1px,
    $color: #ddd,
    $position: relative,
    $radius: 0
) {
    position: $position;

    @include border-radius($radius);

    &:after {
        content: "";
        position: absolute;
        top: 0;
        left: 0;
        pointer-events: none;
        box-sizing: border-box;
        -webkit-transform-origin: top left;

        @media only screen and (-webkit-device-pixel-ratio: 2) {
            width: 200%;
            height: 200%;

            @include border-radius($radius, 2);

            -webkit-transform: scale(.5, .5);
                    transform: scale(.5, .5);
        }

        @media only screen and (-webkit-device-pixel-ratio: 3) {
            width: 300%;
            height: 300%;

            @include border-radius($radius, 3);

            -webkit-transform: scale(.333333333333, .333333333333);
                    transform: scale(.333333333333, .333333333333);
        }

        @if $direction == all {
            border: $size solid $color;
        }
        @else {
            @for $i from 1 through length($direction) {
                $item: nth($direction, $i);

                border-#{$item}: getBorderItemValue($size, $i) solid getBorderItemValue($color, $i);
            }
        }
    }
}

在多邊邊框中,pointer-events: none 的使用屬於核心。為了能夠看到偽類的邊框,偽類將會被置於元素上方,如此便導致了元素被覆蓋不可點擊,這個css屬性就解除了這個障礙。

圓角如果使用了百分比,就不需要設置dpr繫數了

可以看到,不過這麼一來,會造成代碼比較冗餘的問題,特別是當我們需要再次覆蓋之前的邊框屬性時。

 

實現容器固定縱橫比

縱橫比其實還是第一次聽說,做方案調研設計就一併整合過來了

它主要是用於響應式設計中的iframe、img 和video之類的元素,實現縱橫比有很多方法

 這裡使用 padding-top 百分比的方法,實現一下容器內文本區的固定縱橫比

 

/**
 * 實現固定寬高比
 * @param  {string} $position: relative      定位方式
 * @param  {string} $width:    100%          容器寬度
 * @param  {string} $sub:      null          容器的目標子元素
 * @param  {number} $aspectX:  1             容器寬
 * @param  {number} $aspectY:  1             容器高
 */
@mixin aspect-ratio(
    $position: relative,
    $width: 100%,
    $sub: null,
    $aspectX: 1,
    $aspectY: 1
) {
    overflow: hidden;
    position: $position;
    padding-top: percentage($aspectY / $aspectX);
    width: $width;
    height: 0;

    @if $sub == null {
        $sub: "*";
    }

    & > #{$sub} {
        position: absolute;
        left: 0;
        top: 0;
        width: 100%;
        height: 100%;
    }
}
/* 容器寬高比 */
.header {
    @include aspect-ratio(
        // $width: px2vw(600),
        // $sub: ".header-content",
        $aspectX: 375,
        $aspectY: 150
    )
}

padding的百分比是基於元素寬度的,將容器高度設為0,根據盒子模型,則整個元素最終的高度有padding-top來決定

子元素設置絕對定位防止被擠壓,同時撐滿父級容器,即可實現

 

從效果圖能夠看出,美中不足是無法設置容器最大最小寬度,vw是根據設備寬度進行計算的,所以無法解決。

 

五、REM + VW佈局

講的太亂了?自己去看代碼

為瞭解決純VW佈局不能設置最大最小寬度的問題,我們引入REM。

通過配置html根元素的font-size為vw單位,並且配置最大最小的像素px值,在其他css代碼中可以直接使用rem作為單位

調用方式炒雞簡單

html {
    @include root-font-size();
}
line-height: px2rem(300);

而scss裡面的實現,同樣是先定義一個映射關係。將頁面寬度進行分塊(只是為了防止值太大)

/* 移動端頁面設計稿寬度 */
$design-width: 750;
/* 移動端頁面設計稿dpr基準值 */
$design-dpr: 2;
/* 將移動端頁面分為10塊 */
$blocks: 10;
/* 縮放所支持的設備最小寬度 */
$min-device-width: 320px;
/* 縮放所支持的設備最大寬度 */
$max-device-width: 540px;

/*
    rem與px對應關係,1rem代表html font-size值(為一塊的寬度),$rem即為$px對應占多少塊

        $px                    $rem
    -------------    ===    ------------
    $design-width              $blocks
*/

/* html根元素的font-size定義,簡單地將頁面分為$blocks塊,方便計算 */
@mixin root-font-size() {
    font-size: 100vw / $blocks;

    body {
        @include container-min-width();
    }

    /* 最小寬度定義 */
    @media screen and (max-width: $min-device-width) {
        font-size: $min-device-width / $blocks;
    }

    /* 最大寬度定義 */
    &[data-content-max] {
        body[data-content-max] {
            @include container-max-width();
        }

        @media screen and (min-width: $max-device-width) {
            font-size: $max-device-width / $blocks;
        }
    }
}
/* 設置容器拉伸的最小寬度 */
@mixin container-min-width() {
    margin-right: auto;
    margin-left: auto;
    min-width: $min-device-width;
}

/* 設置容器拉伸的最大寬度 */
@mixin container-max-width() {
    margin-right: auto;
    margin-left: auto;
    max-width: $max-device-width;
}

這裡的max-width直接使用寬度值,因為使用的是vw,視窗未縮放

而在頁面標簽(html和body)中,簡單地配上屬性代表是否需要限制寬度即可。

<!DOCTYPE html>
<html data-content-max>
    <head>
        <title>VW-REM佈局</title>
        <meta charset="utf-8">
        <meta lang="zh-CN">
        <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no">
        <link rel="stylesheet" href="./vw-rem.css">
    </head>

    <body data-content-max>

同樣是計算基準值和容器寬度兩個方面。

 

如果僅僅限制計算的基準值,也能實現“流式效果”

 

六、對比選擇

1. 方案選擇

方案是挺多的,可能也不夠完善。要選哪種方案呢?

每個方案都能保證在不同機型下做到一定的適配。

1. 一般來說,可用直接考慮使用REM佈局

2. 因REM使用了JS動態設置html的font-size,且scale對安卓機型不太友好,要求極致的可以選用VW

3. 純VW佈局不支持設置容器最大最小寬高,如果需要此功能則選用 REM + VW佈局

 

 

2. 食用方式

怎麼使用呢?

可在Github中對應目錄的 html,js,css文件,看看是怎麼調用的

常規方式是引入公共基礎代碼,然後在業務代碼中調用

在html文件中可以配置 data-content-max 參數來限制最大最小寬度

在scss基礎部分還可以自定義這幾個值(如果是REM佈局的,修改這些值還需要在rem.js 文件中同步修改)

/* 移動端頁面設計稿寬度 */
$design-width: 750;
/* 移動端頁面設計稿dpr基準值 */
$design-dpr: 2;
/* 將移動端頁面分為10塊 */
$blocks: 10;
/* 縮放所支持的設備最小寬度 */
$min-device-width: 320px;
/* 縮放所支持的設備最大寬度 */
$max-device-width: 540px;

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 概述 ScreenMatch是根據你的需要,生成需要適配的尺寸的文件,手機會根據屏幕相關參數自動尋找合適的尺寸文件 添加插件 如圖,打開Android Studio的Settings設置,找到Plugins,點擊Browse Repositories,在彈出的輸入框里填入screenMatch,就可 ...
  • 一、butterknife介紹 ①官網 butterknife ②Field and method binding for Android Views which uses annotation processing to generate boilerplate code for you 【功能】 ...
  • 都說程式猿學習是不分平臺的,做了一輩子的Xaml,也想看看現在最牛逼的移動技術。 看了看Google 的Flutter,好像很牛逼,不怎麼需要Android和IOS基礎(應該還是要的), 不過現在是Beta版本,但是又說跟Fuschia 有關係,又是一個很牛逼的東西。 於是下載來品嘗一下。 先上幾個 ...
  • 概述 .9.PNG是安卓開發裡面的一種特殊的圖片,這種格式的圖片通過ADT自帶的編輯工具生成,使用九宮格切分的方法。點九圖是一種可拉伸的點陣圖,android會自動調整它的大小,來使圖像在充當背景時可以在界面中自適應展示 生成圖片 .9.png只能放在drawable文件夾中,右鍵.png圖片,找到如 ...
  • 參考文章:https://blog.csdn.net/qq_24216407/article/details/72842614 在build.gradle引用了Vlc的安卓包:de.mrmaffen:vlc-android-sdk:3.0.0 編譯一切正常,運行報錯: 大致意思是com.androi ...
  • 《一統江湖的大前端》系列是自己的前端學習筆記,旨在介紹javascript在非網頁開發領域的應用案例和發現各類好玩的js庫,不定期更新。如果你對前端的理解還是寫寫頁面綁綁事件,那你真的是有點OUT了,前端能做的事情已經太多了, , , , , , 甚至 ,什麼火就搞什麼,活脫脫一個 蹭熱點小能手 。 ...
  • 一、Controller的創建 1.name:控制器的名稱(建議參考Java包的命名規範:點的方式進行命名); 2.function:回調函數的構造方法(實際是對象,主要考慮到類的調用); 3.$scope相當於mvvm模式的viewmodel,支持變數、函數; 3.1.$ccope的由來: $in ...
  • Canvas的API提供了 save() 和 restore() 的方法,用於保存及恢復當前canvas繪圖環境的所有屬性。 save()與restore()方法可以嵌套調用 save()方法將當前繪圖環境壓入堆棧頂部,restore()方法從堆棧頂部彈出一組狀態信息,並據此恢復當前繪圖環境的各個狀 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...