自适应布局方案

前置知识

关于像素

  1. 逻辑像素 :也可以称为 css 像素 ,是我们在 css 代码中设置的像素,比如 width=20px
  2. 设备像素 :也可以称为 物理像素 ,指的是设备能控制显示的最小物理单位。即显示器上一个个小点。是在出厂的时候就固定不变的。
  3. 位图像素:指的是栅格图像(png,jpg,gif等)中最小的数据单元,每个位图像素都存储着显示信息(比如颜色、透明度等)。

设备像素比

设备像素比 DPR= 设备像素/逻辑像素,用于衡量设备屏幕上的物理像素与逻辑像素之间的关系。

一般情况下在普通的电脑浏览器上,1个CSS像素通常对应1个物理像素,因此设备像素比为1。但在高分辨率的设备(如Retina显示屏、高DPI手机屏幕等)上,1个CSS像素可能对应多个物理像素,这时设备像素比会大于1。

例如,对于具有2倍像素密度的Retina显示屏,设备像素比通常为2,这意味着在这样的屏幕上,1个CSS像素将对应4个物理像素(一个CSS像素在水平和垂直方向上各对应2个物理像素,总共为4个物理像素)。

在代码中可以通过 var dpr = window.devicePixelRatio || 1; 获取设备像素比。

分辨率

分辨率=水平像素*垂直像素

分辨率(Resolution)是指显示设备(如显示器、手机屏幕等)上可以显示的像素数量,通常用水平像素数和垂直像素数来描述。分辨率决定了显示设备的清晰度和细节程度。

rem

众所周知,rem 是相对于 html 的 font-size 而言的。所以可以通过动态设置 html 的 font-size 从而实现响应式的布局。

文本字号为什么不建议使用 rem

比如在阅读小说的时候,用户使用更大的屏幕是为了获取更多的文本,而不是更大的字体。同理用户也不会希望在很小的屏幕上字体变得很小,那对于阅读是存在障碍的。

viewport 的 meta 标签

<meta>标签有很多种,而这里要着重说的是viewport的meta标签,其主要用来告诉浏览器如何规范的渲染Web页面,而你则需要告诉它视窗有多大。在开发移动端页面,我们需要设置meta标签如下:

js 复制代码
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">

代码以显示网页的屏幕宽度定义了视窗宽度。网页的比例和最大比例被设置为100%。

移动端布局实现方案

  1. flexible.js:淘宝移动端适配方案,开箱即用。
  2. vw 布局方案:相对于
  3. rem + media
  4. vw
  5. %:不推荐,因为不同情况下的参照物不同

flexible.js

flexible.js 是淘宝移动端适配方案。

该库解决移动端网页开发中的屏幕适配问题而设计的,其主要实现原理是通过设置 <meta> 标签的 viewport 属性来动态调整页面的缩放比例,从而实现不同尺寸设备上页面的适配。

原理

1.获取设备的像素密度(DPR)

js 复制代码
var dpr = window.devicePixelRatio || 1;

2.计算页面的根元素字体大小

计师给的视觉稿一般是 640px 或者是 750px (这里指的是宽度),我们会选定一种尺寸的稿子作为参考去布局。

假设我们选择 750px 去布局,

  1. 第一件事情就是要设置根元素 htmlfont-size,那我们把 750px 分成 10 份,$rem = 750px / 10 = 75px,也就是,1rem = 75px10rem = 750px。也就相当于将页面的宽度等分成10个$rem

  2. 视觉稿上的单位是 px,我们需要转成 rem,因为前面说了 1rem = 75px1px = 1/75rem,所以用视觉稿上的 px 值除以 75 就可以了,比如,100px = 100/75rem = 1.33333rem

    这个过程可以通过 sass 去实现,避免自己手动计算:

    scss 复制代码
    /* 基准 font-size,可设置成其他值 */
    $UIWidth:750px;
    $rem: $UIWidth/10;
    @mixin px2rem($name, $px){
      #{$name}: $px / $rem * 1rem;
    }
    
    /* 使用示例:*/
    .container {
      @include px2rem(height, 240);
    }
    
    /* Scss 编译后:*/
    .container {
      height: 2.4rem;
    }

为了方便运算,有些老司机会把 750 分成 7.5 份,即 $rem = 750px / 7.5 = 100px,所以,用视觉稿上的 px 直接除以 100 就好了。

3.设置 <meta> 标签的 viewport 属性

将页面的 viewport 设置为一个固定的宽度,使得页面的宽度与设备的物理宽度之间建立起一个固定的关系。这样可以保证页面在不同设备上以相同的比例进行缩放,从而实现适配。

js 复制代码
// 获取设备像素密度
var dpr = window.devicePixelRatio || 1;

// 计算页面缩放比例
var scale = 1 / dpr;

// 构建 viewport meta 标签
var metaEl = document.createElement('meta');
metaEl.setAttribute('name', 'viewport');
metaEl.setAttribute('content', 'width=device-width, initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');

// 插入 meta 标签到文档头部
document.head.appendChild(metaEl);

4.动态调整根元素字体大小

通过监听窗口的 resize 事件,在窗口大小发生变化时,动态调整根元素的字体大小,以保持页面的布局比例不变。

事实上他做了这几样事情:

  • 动态改写<meta>标签
  • <html>元素添加data-dpr属性,并且动态改写data-dpr的值
  • <html>元素添加font-size属性,并且动态改写font-size的值

使用注意事项

px2rem

需要在编写样式的时候将px转化成rem。可以使用scss实现(这个上文已经介绍过了)。

也可以使用postcss-px2rem,会自动对代码中的px进行转换。

文本处理

文本字号不建议使用 rem

因为我们希望文本能够流动,使得在大屏手机能看到更多的文本,而不是文本因为 rem 等比缩放使得大字号显得突兀;同样也不希望在小屏手机字体因为等比缩放显得太小

所以需要根据不同的 dpr 动态设置固定字号的字体:

css 复制代码
div {
  width: 1rem; 
  height: 0.4rem;
  font-size: 12px; // 默认写上 dpr 为 1 的 fontSize
}
[data-dpr="2"] div {
  font-size: 24px;
}
[data-dpr="3"] div {
  font-size: 36px;
}

为了能更好的利于开发,在实际开发中,我们可以定制一个 font-dpr() 这样的 Sass 混合:

scss 复制代码
@mixin font-dpr($font-size){
    font-size: $font-size;

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

    [data-dpr="3"] & {
        font-size: $font-size * 3;
    }
}

最大屏幕控制

如果不设置最大宽度,font-size 将随着布局视口的增大而增大,所有尺寸等比例放大,如果在 PC 端打开网页,巨大的按钮将不堪入目。所以设置一个宽度断点,超过断点将使用同一宽度。

js 复制代码
function refreshRem(){
  var width = docEl.getBoundingClientRect().width;
  // 最大宽度控制
  if (width / dpr > 540) {
    width = 540 * dpr;
  }
  var rem = width / 7.5;
  docEl.style.fontSize = rem + 'px';
  flexible.rem = win.rem = rem;
}

或者也可以通过媒体查询:

css 复制代码
/* 当屏幕宽度小于等于 768px 时,应用这些样式 */
@media (max-width: 768px) {
  /* 添加你需要应用的样式 */
}

/* 当屏幕宽度大于 768px 时,应用这些样式 */
@media (min-width: 769px) {
  /* 添加你需要应用的样式 */
}

图像的高清显示

先来看看位图在 Retina 屏是怎样显示的

位图像素: 一个位图像素是栅格图像 (如:png, jpg, gif 等)最小的数据单元。每一个位图像素都包含着一些自身的显示信息(如:显示位置,颜色值,透明度等)

对于 dpr=2retina 屏幕而言,1 个位图像素对应于 4 个物理像素,由于单个位图像素不可以再进一步分割,所以只能就近取色,从而导致图片模糊(注意上述的几个颜色值)

为了使这张图高清显示,需要提供(@2x)两倍图,比如,200×300(css pixel) img 标签,就需要提供 400×600 的图片,使得位图像素跟物理像素 1:1 对应,图片就自然清晰了

但是对于普通屏幕,就没有必要加载(@2x)两倍图了,一个是因为会造成资源浪费,另一个是会让图片失去了一些锐利度(或色差)

对于图片高清,最好的解决方案是,不同的 dpr,加载不同的图片。

vw 布局方案

vw 布局方案中,所有元素采用 vw 布局,对于 750 设计稿,750px = 100vw ,即 1px = 100/750vw

可以借助sass:

scss 复制代码
@mixin px2vw($name, $px){
  #{$name}: $px * (100/750) * 1vw;
}

/* 使用示例:*/
.container {
  @include px2vw(height, 750);
}

/* Scss 编译后:*/
.container {
  height: 100vw;
}

无法做最大屏幕控制

纯 vw 布局,没有办法控制最大宽度,因为 vw 单位只受视觉视口影响。

rem单位+动态html的font-size

rem 单位是相对于 html 元素的 font-size 来设置的,通过在不同屏幕尺寸下,动态的修改 html 元素的 font-size 以此来达到适配效果。

  1. 可以通过媒体查询去根据屏幕尺寸设置 html 元素的 font-size。

    缺点:

    • 需要针对不同的屏幕编写大量的媒体查询
    • 如果动态改变尺寸,不会实时更新,只是一个个区间
  2. 通过监听屏幕尺寸的变化来动态修改 html 元素的 font-size 大小

js 复制代码
function setRemUnit() {  
  // 获取所有的 html 元素  
  const htmlEl = document.documentElement  
  // 375 -> 16px  
  // 320px -> 12px  
  // 我们需要动态更改字体大小,因此需要获取网页的宽度  
  // 拿到客户端宽度  
  const htmlWidth = htmlEl.clientWidth  
  // 将宽度分成10份  
  const htmlFontSize = htmlWidth / 10  
  console.log('htmlFontSize', htmlFontSize);  
  // 将值给到html的font-size  
  htmlEl.style.fontSize = htmlFontSize + 'px'  
}  
  
setRemUnit()  
// 给 window 添加监听事件  
window.addEventListener('resize', setRemUnit)

参考

github.com/PolluxLee/b... mp.weixin.qq.com/s/b5VMNgml3...

相关推荐
轻口味1 小时前
命名空间与模块化概述
开发语言·前端·javascript
前端小小王1 小时前
React Hooks
前端·javascript·react.js
迷途小码农零零发2 小时前
react中使用ResizeObserver来观察元素的size变化
前端·javascript·react.js
娃哈哈哈哈呀2 小时前
vue中的css深度选择器v-deep 配合!important
前端·css·vue.js
旭东怪2 小时前
EasyPoi 使用$fe:模板语法生成Word动态行
java·前端·word
ekskef_sef4 小时前
32岁前端干了8年,是继续做前端开发,还是转其它工作
前端
sunshine6414 小时前
【CSS】实现tag选中对钩样式
前端·css·css3
真滴book理喻5 小时前
Vue(四)
前端·javascript·vue.js
蜜獾云5 小时前
npm淘宝镜像
前端·npm·node.js