细说移动端 经典的REM布局 与 新秀VW布局

和以往一样,本次项目也放到了 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;

原文地址:https://www.cnblogs.com/imwtr/p/9648233.html

时间: 2024-11-05 16:28:06

细说移动端 经典的REM布局 与 新秀VW布局的相关文章

移动端适配之rem 笔记

/*移动端适配 width|height|font-size = 视觉稿量出来的值 / 100rem;@lbl*/ @media screen and (max-width:359px) and (min-width:320px) { html,body{ font-size: 50px !important; } } @media screen and (max-width:374px) and (min-width:360px) { html,body{ font-size: 56.25px

移动端适配之REM

随着手机等移动设备的普及,移动端带来的流量已经不可忽视,一个网站不只是只有pc的页面就足够了,移动端的适配已经势在必行.但是移动设备种类繁多,屏幕尺寸也千奇百怪,能不能找到一种方式可以适配所有的手机屏幕呢? 答案就是rem. 俗话说 “授人以鱼不如授人以渔”,但今天我就反其道而行,就是授人以鱼的.因为要跟你说什么设备像素比.css像素.ppi等东西,估计你会晕的(主要是我也搞不懂).废话不多说,先上代码. 1 /*移动端适配 width|height|font-size = 视觉稿量出来的值 /

移动适配请使用比rem等更好的布局方案

??移动端大行其道,rem/em.百分比.响应式方案更是层出不穷,看见周围的伙伴们都在对使用rem和百分比情有独钟,可我却偏不爱,之所以出现如此多的方法,其目的只有一个屏幕适配. ??屏幕适配顾名思义就是一张设计稿还原出的程序要适应于当下市场上各种大小屏幕不一的终端,做到写一次各种终端大小的屏幕都能比例协调的显示.下面我们先来看下大家耳熟能详的几种方案是如何去解决屏幕适配的. rem/em:rem根元素字体大小单位,em元素字体大小单位.使用rem的时候我们需要在css中先为html元素设定fo

使用gulp将移动端xp转为rem

使用gulp的插件可以很方便的将xp转为rem,在布局的时候使用@1x .@2x布局,即10rem=device-width:@1x即设计图为320px,1rem对应的10px像素,相对的@2x即为布局使用640px的布局,1rem=20px像素:不过浏览器有默认最小字体,谷歌浏览器默认的最小的字体大小为12px,所以1x大小设置可能会出现问题,绝大多数还是会使用2x的布局方法: 在阅读这篇文章之前至少要回使用gulp,可以去中文网先了解写gulp的基本使用方法,我也是在总结gulp的使用方法,

web前端课程技术分享之关于rem布局和vw布局的理解

通过rem来实现响应式布局: 首先来看,什么是rem单位.rem是一个灵活的.可扩展的单位,由浏览器转化像素并显示.与em单位不同,rem单位无论嵌套层级如何,都只相对于浏览器的根元素(HTML元素)的font-size.默认情况下,html元素的font-size为16px, rem单位都是相对于根元素html的font-size来决定大小的,根元素的font-size相当于提供了一个基准,当页面的size发生变化时,只需要改变font-size的值,那么以rem为固定单位的元素的大小也会发生

布局的几种方式(静态布局、自适应布局、流式布局、响应式布局、弹性布局)

一.静态布局(static layout) 即传统Web设计,网页上的所有元素的尺寸一律使用px作为单位. 1.布局特点 不管浏览器尺寸具体是多少,网页布局始终按照最初写代码时的布局来显示.常规的pc的网站都是静态(定宽度)布局的,也就是设置了min-width,这样的话,如果小于这个宽度就会出现滚动条,如果大于这个宽度则内容居中外加背景,这种设计常见于pc端. 2.设计方法 PC:居中布局,所有样式使用绝对宽度/高度(px),设计一个Layout,在屏幕宽高有调整时,使用横向和竖向的滚动条来查

关于静态布局、自适应布局、流式布局、响应式布局、弹性布局的一些概念

一.静态布局(Static Layout)即传统Web设计,网页上的所有元素的尺寸一律使用px作为单位. 1.布局特点:不管浏览器尺寸具体是多少,网页布局始终按照最初写代码时的布局来显示.常规的pc的网站都是静态(定宽度)布局的,也就是设置了min-width,这样的话,如果小于这个宽度就会出现滚动条,如果大于这个宽度则内容居中外加背景,这种设计常见与pc端.2.设计方法: PC:居中布局,所有样式使用绝对宽度/高度(px),设计一个Layout,在屏幕宽高有调整时,使用横向和竖向的滚动条来查阅

干货!各种常见布局实现+知名网站布局分析1.0版

各种页面常见布局+知名网站实例分析+相关阅读推荐 阅前必看:本文总结了各种常见的布局实现,网上搜的"史上最全布局"好像也没有这么全吧?哈哈!这篇文章就当作一个知识整理吧.当然,文章肯定还有不足的地方,至少目前来看,有两点我是不满足的.第一点,兼容性还没有时间去一一测试,后续再补吧!第二点,缺少每个方法的优缺点分析,由于害怕自己总结的不好或不足,不是很有把握的东西,暂时还是不写了,以免误导他人,以后再看吧!各位读者如果发现问题或者有什么意见,欢迎提出!还有就是这篇文章没提到的其他布局,待

页面布局的几种方式(静态化布局,流式布局,自适应布局,响应式布局,弹性布局)

一.静态布局(static layout) 即传统Web设计,网页上的所有元素的尺寸一律使用px作为单位. 1.布局特点 不管浏览器尺寸具体是多少,网页布局始终按照最初写代码时的布局来显示.常规的pc的网站都是静态(定宽度)布局的,也就是设置了min-width,这样的话,如果小于这个宽度就会出现滚动条,如果大于这个宽度则内容居中外加背景,这种设计常见于pc端. https://developers.google.com/search/mobile-sites/mobile-seo/respon