Skip to content

Latest commit

 

History

History
445 lines (343 loc) · 21.5 KB

响应式相关.md

File metadata and controls

445 lines (343 loc) · 21.5 KB

响应式相关

目录

  1. WAP端适配总结
  2. 响应式页面解决方案
  3. 基本概念、术语
  4. px转rem方案
  5. 响应式设计
  6. 不同PPI的设备使用不同分辨率的图片
  7. 放大模式下的适配(取消系统放大方案)

WAP端适配总结

  1. 一份支持响应式页面的PSD设计稿、或多份具体响应式细节的PSD设计稿(少见)。
  2. 宽屏:按比例放大项、或增加项数量。
  3. <meta>的viewport值方案:理想视口方案、或1/DPR缩放方案。
  4. 响应式设计三大要素:媒体查询、流式布局、弹性图片。
  5. 不同PPI使用不同分辨率图片
  6. font-size阶梯取值方案(位图字体):CSS媒体查询额外设置方案、或JS设置html的data-dpr统一控制方案。
  7. 半像素处理
  8. rem+@media方案
  9. 雪碧图使用百分比background-position
  10. 使用rem导致的CSS小数、百分比的四舍五入问题处理
  11. 自适应宽度布局flex优雅解决布局、自适应问题)。
  12. 是否横竖屏切换显示(特殊单屏应用,如:游戏)。

响应式页面解决方案

适配布局(与设计师协作思路)

  1. 使用vm(或vh

    参考:大漠:再聊移动端页面的适配

    1. 设置为理想视口:

      <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">

    2. vw(或vhvminvmax)设置html的font-size

      :root {
        --psd: 设计稿宽度px;  /* 无单位 */
        --rfz: 缩小倍数;      /* 无单位。可以设置为100:方便计算、也因为PC浏览器通常都有最小字体限制 */
      }
      html {
        /* 单位:vw */
        font-size: 具体vw;                                /* 兼容性 */
        font-size: calc(100vw / 设计稿宽度px * 缩小倍数);  /* 兼容性 */
        font-size: calc(100vw / var(--psd) * var(--rfz));
      }
      1. 若原本是PC页面而增加的WAP适配,就在html外添加媒体查询。

      2. 若要限制最大宽度(或高度)的页面,可以媒体查询限定html的font-size具体px

        要限制最小宽度(或高度)同理,改变min-max-、改变最大最小

        /* 在上面的基础上增加媒体查询限制最大宽度 */
        
        @media (min-width: 最大宽度px) {
          html {
            /* 单位:px(不能换算成rem) */
            font-size: 具体px;                                    /* 兼容性 */
            font-size: calc(最大宽度px / 设计稿宽度px * 缩小倍数);  /* 兼容性 */
            font-size: calc(最大宽度px / var(--psd) * var(--rfz));
          }
        }
        
        /* 或 */
        
        @media (min-height: 最大高度px) {
          html {
            /* 单位:px(不能换算成rem) */
            font-size: 具体px;                                    /* 兼容性 */
            font-size: calc(最大宽度px / 设计稿宽度px * 缩小倍数);  /* 兼容性 */
            font-size: calc(最大高度px / var(--psd) * var(--rfz));
          }
        }
    3. 元素用设计稿的px除以缩小倍数(--rfz),单位用rem(手动计算或px转rem方案)。

    原理思路:把设计稿转化为宽度的百分比(vw)显示在移动端上。
    • 针对1080px的设计稿,初始设定:--psd: 1080; --rfz: 100;

      300px的设计稿距离计算如下

      1. 1080px设计稿中300px占用:300px / 1080px ≈ 0.278

      2. 300px的设计稿距离写成CSS为:300 / var(--rfz) + rem => 300 / 100 + rem = 3rem

        3rem => 3 * html的font-size => 3 * calc(100vw / var(--psd) * var(--rfz)) => 3 * 100vw / 1080 * 100 ≈ 27.8vw

      3. 27.8vw === 设计稿宽度的0.278,从而设计稿对应了响应式页面。

  2. 已过时的方案
    1. flexible方案

      类似的还有:hotcss

      1. 用JS动态设置<meta>的viewport缩放比为1/DPR,解决0.5px问题。

        缩放比例1/DPR原因:在这个缩放比例下,CSS中的1px设备独立像素等于屏幕的1px物理像素。事实上取消了px作为设备独立像素的含义,变成物理像素的值。能够画出1px物理像素的内容。

      2. 用JS根据不同布局视口宽度动态设置html的font-size值,把要做成响应式的内容转换为rem单位。

        把视觉稿分成100份来看待(为了以后兼容vh,vw单位)。每一份被称为一个单位a,1rem单位认定为10a。拿750px宽度的视觉稿举例:750px = 100a => 75px = 10a = 1rem => 1rem = 75px,因此当设计稿宽度为750px时,量取的长度要除以75并加上rem单位。

      3. 用JS在<html>上动态设置data-dpr,对额外需要根据DPR变化而改变的内容设置样式(如:字体)。

        @mixin font-size($font-size) {
            font-size: $font-size;
            [data-dpr="2"] & {
                font-size: $font-size * 2;
            }
            [data-dpr="3"] & {
                font-size: $font-size * 3;
            }
        }

        因为字体很多都是点阵字体或位图字体,通常有16px、24px的清晰字号,其他字号大小是位图缩放的结果,有损美观。

      • 不需要设置媒体查询改变html的font-size值
    2. 媒体查询设置不同布局视口宽度html的font-size值,把要做成响应式的内容转换为rem单位

      1. 设置为理想视口:

        <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">

        若改变布局视口(放大),则只需媒体查询内容添加更大布局视口下html的font-size值。

      2. 切图取值用设计稿的px带入下面方法而生成的rem值

        /* 用Sass预处理语言换算px -> rem */
        @function rem($px, $base-font-size: 20px) {
            @if unitless($px) {
                @return rem($px + 0px);
            }
            @if unit($px) != "px" {
                @error "rem()的参数单位必须是px或不带单位";
            }
        
            // $base-font-size:切图时设计稿宽度对应的媒体查询中html的font-size
            @return $px / $base-font-size + rem;
        }
      3. 媒体查询仅设置不同布局视口宽度下html的的font-size值

      0.5px问题用WAP端半像素处理,或直接设置为1px。

当Android系统的样式出现问题又找不到原因时,有可能是系统字体大小被修改过。

基本概念、术语

  1. 视口(viewport)

    网站外层的容器,控制网站的最高块状容器:<html>

    1. PC端的viewport就是浏览器窗口的宽度高度,严格等于浏览器的窗口。

      PC端无视<meta>的viewport设置。

    2. WAP端的viewport太窄,为了能更好为CSS布局服务,所以提供了两个viewport

      1. 可视视口(visual viewport):

        浏览器的窗口,可以放大缩小、滚动页面来改变可见部分。

        1. 通过<meta name="viewport" content="initial-scale=缩放值, user-scalable=是否缩放, minimum-scale=缩放值, maximum-scale=缩放值">控制缩放。

          视觉上,可视视口永远是浏览器大小,但宽度值等于逻辑像素宽度。

          可视视口宽度(屏幕范围内CSS像素堆积的总宽度) = 浏览器宽度 / 现在的缩放值

        2. JS获取:window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight

          随页面缩放而改变。

      2. 布局视口(layout viewport):

        网站外层容器的固定大小,决定<html>宽度。不同浏览器默认大小不同,980px左右;不能小于可视视口。

        1. 通过<meta name="viewport" content="width=布局视口初始宽度值, initial-scale=缩放值">设置并缩放成为最终布局视口宽度。

          布局视口宽度 = 初始宽度值 / 初始缩放值 > 可视视口宽度 : 初始宽度值 / 初始缩放值 ? 可视视口宽度

        2. JS获取:document.documentElement.clientWidth || document.body.clientWidth

          随写入的viewport的宽度值 / 初始缩放值改变而改变。

        3. CSS媒体查询判断:@media (min-width: 宽度px) and (max-width: 宽度px)

          随写入的viewport的宽度值 / 初始缩放值改变而改变。

      概念理解

      想象下layout viewport是一张大的不能改变大小和角度的图片。现在你有个更小的框来观看这张大图片,这个框被不透明的材料包围,因此你只能看到大图片的一部分。你通过这个框看到的大图片的部分被称为visual viewport。你能拿着这个框站得离大图片远点(用户的缩小页面功能),以一次性看到这个大图片的更多区域;或你能站得近点(用户的放大页面功能),以看到更少区域。你能改变这个框,但这张大图片的大小和形状都不会改变。

      • 理想视口(ideal viewport):<meta name="viewport" content="width=device-width,initial-scale=1.0">

        1. 可视视口,初始不缩放。
        2. 布局视口,设置为屏幕宽度。
  2. 像素(pixel)

    像素是数字图像的最小组成单元,它不是一个物理尺寸,但和物理尺寸存在一个可变的换算关系(物理尺寸之间的换算是固定的),也是分辨率的单位。

    1. CSS像素(前端用的px

      viewport的像素。浏览器内部对逻辑像素进行再处理的结果(调整逻辑像素的缩放来达到适应设备的一个中间层)。

      width=device-width:让viewport的宽度等于逻辑像素的总宽度。

    2. 设备独立像素(Device-Independent Pixels,DIPs),密度无关像素(Density-Independent Pixels,DIPs)

      逻辑像素,一种度量单位。由程序使用的虚拟像素,然后由相关系统转换为物理像素呈现。

      iOS用pt(point),Android用dp(density-independent pixel),前端用px

    3. 物理像素(physical pixels),设备像素(device pixels)

      1. 对物理显示设备而言,是固定的。反映显示设备内部led灯的数量,由操作系统控制来发光填色值。
      2. 对系统层面而言,是系统解析后提供给应用展示的。所以改变屏幕分辨率后,应用面对的物理像素也改变了。

    e.g. iPhone 13 Mini的渲染像素是1125x2436,对应的逻辑像素是375x812@3x(css或js获取的像素也是这个逻辑像素值),但是它的物理像素是1080x2340(特殊,设计成渲染分辨率和物理分辨率不一致)。

  3. 比例

    1. DPR(Device Pixel Ratio,设备像素比)、dppx(dots per pixel,每物理像素包含的逻辑像素数量)、dpi(dots per inch,每英寸包含逻辑像素数量)

      DPR = 物理像素 / 设备独立像素1DPR = 1dppx = 96dpi

      1. JS获取:window.devicePixelRatio
      2. CSS媒体查询:@media (min或max-resolution: 整数dppx)@media (min或max-resolution: 整数dpi)
      1. 改变浏览器缩放,会改变DPR。
      2. 对于固定的逻辑像素,当系统分辨率改变时(导致物理像素变化),DPR不变。
    2. PPI(Pixels Per Inch,每英寸物理像素,物理像素密度)

      PPI = Math.sqrt(物理像素宽的平方 + 物理像素高的平方) / 屏幕对角线长度的英寸

      表示每英寸所拥有的物理像素数目。

  4. 主屏分辨率

    物理像素(或系统提供的、被当做物理像素的值)的宽x高。

px转rem方案

rem:相对于根元素的字体大小的单位。rem单位转换为具体px值:rem乘于html的font-size像素

  1. SCSS在.scss文件进行

    @function rem($px, $base-font-size: 20px) {
        @if unitless($px) {
            @return rem($px + 0px);
        }
        @if unit($px) != "px" {
            @error "rem()的参数单位必须是px或不带单位";
        }
    
        // $base-font-size:切图时设计稿宽度对应的媒体查询中html的font-size 或 --rfz+px
        @return $px / $base-font-size + rem;
    }
    a {
        width: rem(20);
    }
    
    /*
    a {
        width: 1rem;
    }
    */
  2. px2rem(或px2rem-postcss)在.css文件进行

    /* px2rem设置:remUnit: 20,baseDpr: 1 */
    a {
        width: 20px;
        height: 40px;   /*px*/
        padding: 60px;  /*no*/
        margin: 80PX;
    }
    
    /*
    a {
        width: 1rem;
        padding: 60px;
        margin: 80PX;
    }
    [data-dpr="1"] a {
        height: 40px;
    }
    [data-dpr="2"] a {
        height: 80px;
    }
    [data-dpr="3"] a {
        height: 120px;
    }
    */

    注意可能对base64等文本进行没必要的转化,按需加上/*no*/

响应式设计

  1. 媒体查询方式
    1. CSS属性:

      @media (min-width: 360px) and (max-width: 640px) {...}

    2. HTML标签:

      <link rel="stylesheet" type="text/css" media="(min-width: 360px) and (max-width: 640px)" href="...">

  2. 响应式设计三大要素
    1. 媒体查询

    2. 流式布局:节点用百分比remflexgrid布局

      使用自适应结构,如:自适应宽度布局

    3. 弹性图片:img {max-width: 100%;}

不同PPI的设备使用不同分辨率的图片

  1. @1、@2、@3不同分辨率的图片放大倍数是1倍、2倍、3倍

  2. 若图片是雪碧图

    1. 则其间距也要是和放大倍数一致,@1、@2、@3分别间距是2px、4px、6px
    2. 则其background-size设置为最低分辨率整个雪碧图的高宽)、background-position设置为最低分辨率时的值。单位用pxrem百分比都可以。
  1. 媒体查询

    @mixin image($url) {
        background-image: url($url + "@1.png");
    
        @media (min-resolution: 2dppx) {
            background-image: url($url + "@2.png");
        }
    
        @media (min-resolution: 3dppx) {
            background-image: url($url + "@3.png");
        }
    }
    
    .img {
        @include image("去除后缀的图片地址");
    }
  2. <img>的一些属性:srcsetsizesbackground-image的值:image-set()

  3. SVG(矢量图)代替位图使用

  • 高倍图在低PPI设备下显示,不会导致图片模糊或锯齿,因此有时可简化地把高倍图用在所有机型上

    • 高倍图使用在低PPI设备下可能导致的问题:

      1. 越大的图,加载到内存需要越大的内存空间
      2. 越大的图,显示时发送到GPU需要更大的计算空间
      3. 大图显示在更小的区域,需要系统计算图片缩放

放大模式下的适配(取消系统放大方案)

iOS、Android可以在系统设置中修改字体大小视图大小/显示大小,会导致H5页面的样式数值发生变化(PC版的缩放同理)。

  • 修改字体大小视图大小/显示大小后,手机内H5现象:

    1. iOS

      仅变化视窗,节点样式数值不变化

    2. Android

      1. 某些机型:

        1. 节点font-size数值变化
        2. 节点其他样式属性,若数值是rem单位时也跟着变化,否则不变化
      2. 某些机型:

        仅变化视窗,节点样式数值不变化

      • line-height可能都有问题

解决方案:利用 html的font-size + 元素的font-size用rem单位、元素的其他样式属性数值不要用 rem 单位(e.g. 用vwpx、等) 的响应式方案,并根据系统放大/缩小的倍数,对html的font-size进行逆向处理:

  1. 先设置某节点font-size值,再获取该节点真实font-size值,他们的倍数就是系统放大/缩小的倍数,需要逆向处理这个倍数;

  2. 获得前面的倍数,若放大/缩小多少,则html的font-size等效缩小/放大多少;

    e.g. 一开始设置50px;计算得到100px,得知放大了2倍,这时再最终设置为25px。节点的1rem=1rem x 25px x 2倍=50px,与一开始1rem=50px的期望相同。

  3. 节点的font-size数值用rem,其他样式属性数值不要用 rem(可以用vw的响应式方案);

    因为其他样式属性数值,用rem时,某些机型跟随系统变化,某些机型又不跟随,但是用vwpx等其他单位时还未发现会跟随变化。

  4. 规避系统通过其他方式放大/缩小页面的手段(e.g. iOS会通过text-size-adjust同步系统放大/缩小)。

实现
<style>
  html {
    text-size-adjust: 100% !important;
    -ms-text-size-adjust: 100% !important;
    -webkit-text-size-adjust: 100% !important;
    -moz-text-size-adjust: 100% !important;
  }
</style>

<script type="text/javascript">
  (function (window, document, widthOrHeight) {
    var eventType =
        "orientationchange" in window ? "orientationchange" : "resize",
      html = document.documentElement;

    function listener() {
      switch (widthOrHeight) {
        case "height":
          var height = Math.max(html.clientHeight, window.innerHeight),
            htmlFontSize = 100 * (height / 1334) || 50; // 设计稿
          break;
        default:
          var width = Math.max(html.clientWidth, window.innerWidth),
            htmlFontSize = 100 * (width / 750) || 50; // 设计稿
      }
      // if (htmlFontSize < 30) {  // 限制最小
      //   htmlFontSize = 30
      // }
      // if (htmlFontSize > 70) {  // 限制最大
      //   htmlFontSize = 70
      // }

      // 先设置一个设计稿的font-size
      html.style.fontSize = htmlFontSize + "px";
      // 获取设置font-size之后计算得出的真实font-size
      var realHtmlFontSize = +window.getComputedStyle(html).fontSize.replace("px", "");

      // 若不相等则说明被放大/缩小了,那么就逆向设置一个新的font-size
      if (htmlFontSize !== realHtmlFontSize) {
        html.style.fontSize =
          htmlFontSize * (htmlFontSize / realHtmlFontSize) + "px"; // e.g. 一开始设置50px;计算得到100px,得知放大2倍,这时再最终设置为25px
      }
    }

    window.addEventListener(eventType, listener, false);
    listener();
  })(window, document, "width");
</script>


节点的font-size数值都用rem作为单位,节点的其他样式属性数值不要用rem,可规避系统导致的放大/缩小(就是使系统导致的变化失效)

CodePen demo

非纯前端解决方案:让客户端打开WebView时就禁止变化字体大小视图大小/显示大小。e.g. WebView初始化时客户端设置textZoom: 100