移动端总结

194 阅读13分钟

移动端

视口

视口主要针对移动设备

概念

(1) 布局视口(layout viewport) 为了能在移动设备上正常显示那些为pc端浏览器设计的网站,移动设备上的浏览器都会把自己默认的 viewport 设为 980px 或其他值,一般都比移动端浏览器可视区域大很多,所以就会出现浏览器出现横向滚动条的情况

img

(2) 视觉视口(visual viewport) 视觉视口表示用户当前看到的浏览器可视区域。用户可以通过缩放(pc或者移动都是通过双指拖动来缩放,而pc的滚轮缩放是增加/减少像素比来实现,会影响布局视口的宽度和高度)来查看网页的显示内容,从而改变视觉视口。视觉视口的定义,就像拿着一个放大镜分别从不同距离观察同一个物体,视觉视口仅仅类似于放大镜中显示的内容,因此视觉视口不会影响布局视口的宽度和高度。

(3) 理想视口(ideal viewport)

理想视口或者应该全称为“理想的布局视口”,在移动设备中就是指设备的分辨率。换句话说,理想视口或者说分辨率就是给定设备物理像素的情况下,最佳的“布局视口”。 理想视口的值其实就是屏幕分辨率的值

meta:content属性

width设置layout viewport 的宽度,为一个正整数,或字符串"width-device"
initial-scale设置页面的初始缩放值,为一个数字,可以带小数
minimum-scale允许用户的最小缩放值,为一个数字,可以带小数
maximum-scale允许用户的最大缩放值,为一个数字,可以带小数
height设置layout viewport 的高度,这个属性对我们并不重要,很少使用
user-scalable是否允许用户进行缩放,值为"no"或"yes", no 代表不允许,yes代表允许
width指定布局视口的宽度 (默认980px) ,可以设置为一个具体的数值(如 width=1024)或 device-width,表示与设备屏幕宽度一致

应用

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

关键属性解析:

  • width=device-width:将视口宽度设置为设备宽度

    • 没有配置

      image-20241224135049467.png

    • 配置了:那么给第一层容器width:100%,就是设备的宽度,给高度100vh就是设备的高度

      • image-20241224135707477.png
  • initial-scale=1.0:初始缩放比例为1

  • user-scalable=no:禁用用户缩放

  • viewport-fit=cover:适配刘海屏

单位概念

设备尺寸

设备尺寸指的是设备对角线的长度,单位是英寸

image-20230922112301663转存失败,建议直接上传图片文件

像素px

  • 物理像素/设备像素(device pixel, dp) : 由一个个物理像素点组成的,通过控制每个像素点的颜色和亮度,使屏幕显示出不同的图像,屏幕从工厂出来那天起,它上面的物理像素点就固定不变了。单位pt,pt 在 css 单位中属于真正的绝对单位。所谓的一倍屏、二倍屏(Retina)、三倍屏,指的是设备以多少物理像素来显示一个CSS像素,也就是说,多倍屏以更多更精细的物理像素点来显示一个CSS像素点,在普通屏幕下1个CSS像素对应1个物理像素,而在Retina屏幕下,1个CSS像素对应的却是4个物理像素。

  • CSS像素(css pixel, px)CSS像素 =设备独立像素 = 逻辑像素

    由于不同的物理设备的物理像素的大小是不一样的,所以css认为浏览器应该对css中的像素进行调节,使得浏览器中1css像素的大小在不同物理设备上看上去大小总是差不多 ,目的是为了保证阅读体验一致。为了达到这一点浏览器可以直接按照设备的物理像素大小进行换算。

20200416211841499转存失败,建议直接上传图片文件

左边表示标清屏幕,右边表示视网膜高清屏幕

宽和高都是2个CSS像素,那么在标清屏中需要用2 * 2个物理像素来显示,即1个CSS像素用1 * 1个物理像素来描述

在高清屏需要4 * 4个物理像素来显示,即1个CSS像素用2 * 2个物理像素来描述

像素比(DPR)

设备像素比:window.devicePixelRatio = 物理像素 / 独立像素

通过devicePixelRatio,我们可以知道该设备上一个css像素代表多少个物理像素。在普通屏,1个css像素对应1个物理像素;2倍屏中,一个css像素对应4个物理像素;三倍屏中则是9个。如iPhone6dpr2,物理像素750(x轴),则它的css像素为375

影响像素比(DPR)

  • 配置设备缩放大小
  • 用户滚轮缩放。例如,当用户把页面放大一倍,那么css中1px所代表的物理像素也会增加一倍;反之把页面缩小一倍,css中1px所代表的物理像素也会减少一倍。

分辨率

是指桌面设定的分辨率(分辨率是可以改变的),而不是显示器的分辨率 。分辨率指的是宽度上和高度上最多能显示的物理像素点个数。指的是屏幕的像素尺寸。750X1334指的是横向有750个像素,纵向有1334个像素。

响应式

单位

blog.csdn.net/liwusen/art…

www.cnblogs.com/zaoa/p/8630…

rem

相对于根元素html的font-size值的大小,此单位若要用于屏幕自适应,可与vw配合使用设置根元素的字体大小。375px的网页的设计稿。此时,1vw=3.75px;4vw=15px;8vw=30px;

百分比%
  1. 子元素widthheight的百分比是父元素width或height的百分比
  2. topbottom的百分比是相对于(默认定位)父元素的height
  3. leftright的百分比是相对于(默认定位)父元素的width
  4. paddingmargin不论是垂直方向或者是水平方向,都相对于直接父元素的width
  5. border-radiustranslatebackground-size的百分比,则是相对于自身的width

两个缺点:

(1)计算困难,如果我们要定义一个元素的宽度和高度,按照设计稿,必须换算成百分比单位。 (2)各个属性中如果使用百分比,相对父元素的属性并不是唯一的。比如width和height相对于父元素的width和height,而margin、padding不管垂直还是水平方向都相对比父元素的宽度、border-radius则是相对于元素自身等等,造成我们使用百分比单位容易使布局问题变得复杂。

自适应

www.cnblogs.com/chenyoumei/…

  • 查看设计图,确定页面布局,组件的复用等

  • 尽可能的添加多的div来包含元素,并设置对应的classname

  • 外层盒子使用flex进行布局,不设置绝对宽高px(使用rem或者%布局),高度由里面的内容撑开(撑不开就用margin、padding

    box-sizing:border-box以及margin:auto

  • Tips

    • float的好处是,如果宽度太小,放不下两个元素,后面的元素会自动滚动到前面元素的下方,不会在水平方向overflow(溢出),避免了水平滚动条的出现。
    • 图片的自适应,img { width: auto; max-width: 100%; }
yd ui移动端

www.jianshu.com/p/b00cd3506…

自适应不是指你缩放页面大小(ctrl+鼠标滚动条),是指你浏览器大小变化自适应

一般不加自适应,缩放页面,会发现$(document).width()会根据缩放变大变小,但是元素宽高不会变,相应元素就会感觉变大变小。加入自适应(自适应会根据页面大小来改变px,元素宽高会变),缩放页面,相应元素感觉不会改变。

缩放页面大小有三种情况:

1. 改变浏览器宽高
2. ctrl+鼠标滚动条,改变页面大小
3. window中显示设置中的缩放与布局

像素比就是第二种和第三种情况

(function (doc, win) {
  const docEl = doc.documentElement;
  // 获取当前显示设备的物理像素分辨率与CSS像素分辨率之比;
  var dpr = window.devicePixelRatio || 1;
  //orientationchange:在用户水平或者垂直翻转设备(即方向发生变化)时触发的事件
  const resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize';
  const recalc = function () {
    var clientWidth = docEl.clientWidth;
    if (!clientWidth) return;
    if (clientWidth >= 750) {
      docEl.style.fontSize = '100px';
    } else {
      //html宽度=clientWidth
      //设计图的宽度=750(通过设计图自行配置)
      //在750的设计图下,我们为了便于开发将根元素的字体大小设置成100px
      docEl.style.fontSize = 100 * (clientWidth / 750) + 'px';
    }
  };
	// 检测是否支持0.5px
  if (dpr >= 2) {
    var fakeBody = document.createElement("body");
    var testElement = document.createElement("div");
    testElement.style.border = ".5px solid transparent";
    fakeBody.appendChild(testElement);
    docEl.appendChild(fakeBody);
    if (testElement.offsetHeight === 1) {
      docEl.classList.add("hairlines");
    }
    docEl.removeChild(fakeBody);
  }
	win.addEventListener("pageshow", function(e) {
		if (e.persisted) { // 浏览器后退的时候重新计算
			clearTimeout(tid);
			tid = setTimeout(refreshRem, 300);
		}
	}, false)

  if (!doc.addEventListener) return;
  win.addEventListener(resizeEvt, recalc, false);
  doc.addEventListener('DOMContentLoaded', recalc, false);//判断DOM是否加载完毕
})(document, window);
flexible.js

github.com/amfe/lib-fl…

// 首先是一个立即执行函数,执行时传入的参数是window和document
(function flexible(window, document) {
  // 返回文档的root元素
  var docEl = document.documentElement; 
  // 获取设备的dpr,即当前设置下物理像素与虚拟像素的比值
  var dpr = window.devicePixelRatio || 1; 

  // 设置默认字体大小,默认的字体大小继承自body
  function setBodyFontSize() {
    if (document.body) {
      // 调整body标签的fontSize,fontSize = (12 * dpr) + 'px'
      document.body.style.fontSize = 12 * dpr + 'px';
    } else {
      document.addEventListener('DOMContentLoaded', setBodyFontSize);
    }
  }
  setBodyFontSize();

  // set 1rem = viewWidth / 24
  function setRemUnit() {
    // 设置root元素的fontSize = 其clientWidth / 24 + 'px'
    var rem = docEl.clientWidth / 24;
    docEl.style.fontSize = rem + 'px';
  }
  setRemUnit();

  // 当页面展示或重新设置大小的时候,触发重新
  window.addEventListener('resize', setRemUnit);
  window.addEventListener('pageshow', function(e) {
    if (e.persisted) {
      setRemUnit();
    }
  });

  // 检测0.5px的支持,支持则root元素的class中有hairlines
  if (dpr >= 2) {
    var fakeBody = document.createElement('body');
    var testElement = document.createElement('div');
    testElement.style.border = '.5px solid transparent';
    fakeBody.appendChild(testElement);
    docEl.appendChild(fakeBody);
    if (testElement.offsetHeight === 1) {
      docEl.classList.add('hairlines');
    }
    docEl.removeChild(fakeBody);
  }
})(window, document);

常见处理

  • 清除默认样式 Normalize.css

  • 浏览器前缀,webpack:autoprefixer

    内核主要代表的浏览器前缀
    TridentIE浏览器-ms
    GeckoFirefox-moz
    PrestoOpera-o
    WebkitChrome和Safari-webkit
  • h5中如何处理移动端滑动卡顿的问题

     body {
       // height: 100%;
       -webkit-overflow-scrolling: touch;
       overflow-scrolling: touch;
       overflow-y: scroll;
     }
    
  • 遮罩层隐藏之后,底部div无法触发点击事件:给遮罩层pointer-events

     pointer-events: none;
     阻止用户的点击动作产生任何效果
     阻止缺省鼠标指针的显示
     阻止CSS里的hover和active状态的变化触发事件
     阻止JavaScript点击动作触发的事件
    

CSS媒体查询处理不同尺寸

使用媒体查询针对不同屏幕尺寸定制样式。

 /* iPhone SE */
 @media screen and (max-width: 374px) {
     .container {
         font-size: 14px;
     }
 }
 ​
 /* iPhone 6/7/8/X */
 @media screen and (min-width: 375px) and (max-width: 413px) {
     .container {
         font-size: 16px;
     }
 }
 ​
 /* iPhone 6/7/8 Plus */
 @media screen and (min-width: 414px) {
     .container {
         font-size: 18px;
     }
 }

刘海

适配iPhone X等带有刘海的机型。

 /* 适配刘海屏 */
 .safe-area-inset {
     padding-top: constant(safe-area-inset-top);
     padding-top: env(safe-area-inset-top);
     padding-bottom: constant(safe-area-inset-bottom);
     padding-bottom: env(safe-area-inset-bottom);
 }
 ​
 /* 底部固定导航适配 */
 .fixed-bottom {
     position: fixed;
     bottom: 0;
     bottom: constant(safe-area-inset-bottom);
     bottom: env(safe-area-inset-bottom);
 }

图片适配方案

针对不同分辨率设备的图片适配策略。

 <!-- 使用srcset适配不同分辨率 -->
 <img srcset="image-320w.jpg 320w,
              image-480w.jpg 480w,
              image-800w.jpg 800w"
      sizes="(max-width: 320px) 280px,
             (max-width: 480px) 440px,
             800px"
      src="image-800w.jpg" alt="Responsive image">

配合CSS的处理:

 .responsive-image {
     max-width: 100%;
     height: auto;
     display: block;
 }

横屏适配处理

处理横屏模式下的布局适配。

 /* 检测横屏 */
 @media screen and (orientation: landscape) {
     .landscape-container {
         display: flex;
         flex-direction: row;
     }
 }
 ​
 /* 检测竖屏 */
 @media screen and (orientation: portrait) {
     .portrait-container {
         display: flex;
         flex-direction: column;
     }
 }

JavaScript监听屏幕旋转:

 window.addEventListener('orientationchange', function() {
     if (window.orientation === 180 || window.orientation === 0) {
         // 竖屏
         console.log('竖屏');
     }
     if (window.orientation === 90 || window.orientation === -90) {
         // 横屏
         console.log('横屏');
     }
 });

问题

图片模糊问题

juejin.cn/post/688572…

我们平时使用的图片大多数都属于位图(png、jpg..),位图由一个个像素点构成的,每个像素都具有特定的位置和颜色值:

img

理论上,位图的每个像素对应在屏幕上使用一个物理像素来渲染,才能达到最佳的显示效果。

而在dpr > 1的屏幕上,位图的一个像素可能由多个物理像素来渲染,然而这些物理像素点并不能被准确的分配上对应位图像素的颜色,只能取近似值,所以相同的图片在dpr > 1的屏幕上就会模糊:

img

1px
原因

img

出现的原因:

按照iPhone6的尺寸,一张分辨率750像素宽的设计图,设计图时量到的1px其实是1设备像素,而当我们设置布局视口等于理想视口等于375px,并且由于iPhone6的DPR为2,写css时的1px对应的物理像素就是2pt,所以看起来会粗一点。

解决
  • 伪类+transform实现

    基于media查询判断不同的设备像素比对线条进行缩放:

     .border-1px {
         position: relative;
         &::after {
             content: '';
             position: absolute;
             left: 0;
             bottom: 0;
             width: 100%;
             height: 1px;
             background-color: #000;
             transform: scaleY(0.5);
             transform-origin: bottom;
         }
     }
     ​
     // 2x屏
     @media (-webkit-min-device-pixel-ratio: 2) {
         .border-1px::after {
             transform: scaleY(0.5);
         }
     }
     ​
     // 3x屏
     @media (-webkit-min-device-pixel-ratio: 3) {
         .border-1px::after {
             transform: scaleY(0.33);
         }
     }
    

    这种方式可以满足各种场景,如果需要满足圆角,只需要给伪类也加上border-radius即可。

  • 媒体查询

     .border { border: 1px solid #999 }
     @media screen and (-webkit-min-device-pixel-ratio: 2) {
       .border { border: 0.5px solid #999 }
     }
     @media screen and (-webkit-min-device-pixel-ratio: 3) {
       .border { border: 0.333333px solid #999 }
     }
    
  • 利用viewport + js + 使用rem实现

    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    
    • name="viewport" content="width=device-width: 本页面的 「viewport」 的宽度为设备宽度。
    • initial-scale=1.0: 初始缩放值为 1,
    • maximum-scale=1.0: 最大的缩放值为 1。
    • user-scalable=no: 禁止用户进行页面缩放。

    那么通过设置viewport的initial-scale,就可以轻松实现:

    • 当dpr = 1 时,initial-scale = 1
    • 当dpr = 2 时,initial-scale = 0.5
    • 当dpr = 3 时,initial-scale = 0.33333333333
     <html>
       <head>
         <title></title>
         <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
         <style></style>
         <script>
           let viewport = document.querySelector("meta[name=viewport]");
           //下面是根据设备像素设置viewport
           if (window.devicePixelRatio == 1) {
             viewport.setAttribute('content', 'width=device-width,initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no');
           }
           if (window.devicePixelRatio == 2) {
             viewport.setAttribute('content', 'width=device-width,initial-scale=0.5, maximum-scale=0.5, minimum-scale=0.5, user-scalable=no');
           }
           if (window.devicePixelRatio == 3) {
             viewport.setAttribute('content', 'width=device-width,initial-scale=0.3333333333333333, maximum-scale=0.3333333333333333, minimum-scale=0.3333333333333333, user-scalable=no');
           }
           let docEl = document.documentElement;
           let fontsize = 32* (docEl.clientWidth / 750) + 'px';
           docEl.style.fontSize = fontsize;
         </script>
       </head>
     </html>
     ​