CSS 移动端适配布局

658 阅读2分钟

移动端适配,在开发中会遇到很多问题

  • 1px问题
  • UI图完美适配方案
  • 横屏适配
  • 高清屏图片模糊问题 ...

适配的意义

元素、字体大小能随着屏幕大小(一般相对于宽度来说)变化而变化,尺寸的大小和屏幕大小成正比。

如何适配

首先应设置viewport视口

meta标签

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

解析:

image.png

解决适配的方法

第一种:媒体查询(设定每种屏幕对应的font-size)
@media screen and (min-width:240px) {
    html, body, button, input, select, textarea {
        font-size:9px;
    }
}
@media screen and (min-width:320px) {
	html, body, button, input, select, textarea {
		font-size:12px;
	}
}

@media screen and (min-width:1000px) and (max-width: 1200px){
    html, body, button, input, select, textarea{
        font-size:16px
    }
}

⚠️:使用min-width时,小的在前面,大的在后面;同理,如果使用max-width时,就是大的在前面,小的在后面。

第二种:rem

布局思想: -一般不要给元素设置具体的宽度,但是对于一些小图标可以设定具体宽度指

  • 高度值可以设置固定值,设计稿多大,就设置多大。
  • 所有设置的固定值都用rem做单位(首先在HTML总设置一个基准值:px和rem的对应比例,然后在效果图上获取px值,布局的时候转化为rem值。)
  • js获取真实屏幕的宽度,让其除以设计稿的宽度,算出比例,把之前的基准值按照比例进行重新的设定。

rem是一个相对单位,1rem等于html元素上字体设置的大小。只需要设置html根元素的font-size的大小,就可以改变rem所代表的大小。

(1) 动态设置html的font-size

PC端

(function () {
    function setRootFontSize() {
        let rem, rootWidth;
        let rootHtml = document.documentElement;
        //限制展现页面的最小宽度
        rootWidth = rootHtml.clientWidth < 1366 ? 1366 : rootHtml.clientWidth;
        // 19.2 = 设计图尺寸宽 / 100( 设计图的rem = 100rem = rootWidth / 19.2;
        // 动态写入样式
        rootHtml.style.fontSize = `${rem}px`;
    }
    setRootFontSize();
    window.addEventListener("resize", setRootFontSize, false);
})();

移动端

(function () {
    function setRootFontSize() {
        let dpr, rem, scale, rootWidth;
        let rootHtml = document.documentElement;
    
        dpr = window.devicePixelRatio || 1; //移动端必须设置
        //限制展现页面的最小宽度
        rootWidth = rootHtml.clientWidth < 1366 ? 1366 : rootHtml.clientWidth;
        rem = rootWidth * dpr / 19.2; // 19.2 = 设计图尺寸宽1920 / 100(设计图的rem = 100)
        scale = 1 / dpr;
    
        // 设置viewport,进行缩放,达到高清效果 (移动端添加)
        let vp = document.querySelector('meta[name="viewport"]');
        vp.setAttribute('content', 'width=' + dpr * rootHtml.clientWidth + ',initial-scale=' + scale + ',maximum-scale=' + scale + ', minimum-scale=' + scale + ',user-scalable=no');
    
        // 动态写入样式
        rootHtml.style.fontSize = `${rem}px`;
    }
    setRootFontSize();
    window.addEventListener("resize", setRootFontSize, false);
    window.addEventListener("orientationchange", setRootFontSize, false); //移动端
})();

另一种写法: 在webpack中使用px2rem-loader

rules:[
  {test:/\.css$/,
  use:['style-loader','css-loader','px2rem-loader?remUnit=75&remPrecision=8']
  }
]

// 解决常见的750px的设计图,直接按照设计图的尺寸来填写属性大小就好
// 注意点:html文件需要引入flexible文件
<script src="http://g.tbcdn.cn/mtb/lib-flexible/0.3.2/??flexible_css.js,flexible.js"></script>

(2) 前端构建中,可以利用scss来解决这个问题

那么如何把UE设计稿中的获取的像素单位的值,转换为已rem为单位的值呢?公式是:元素宽度 / UE图宽度 * 100。

让我们举个例子,假设UE图尺寸是640px,UE图中的一个元素宽度是100px,根据公式100/640*100 = 15.625

p {width: 15.625rem}

逐一的计算太麻烦,我们可以写一个scss的function px2rem即:

$ue-width: 640; /* ue图的宽度 */

@function px2rem($px) {
  @return #{$px/$ue-width*100}rem;
}

p {
  width: px2rem(100);
}

第三种:vw (视口宽度的 1/100), vh (视口高度的 1/100)

vh、vw方案即将视觉视口宽度 window.innerWidth和视觉视口高度 window.innerHeight 等分为 100 份

webpack解析css 的时候用postcss-loader 有个postcss-px-to-viewport能自动实现px到vw的转化.

{
    loader: 'postcss-loader',
    options: {
    	plugins: ()=>[
        	require('autoprefixer')({
        		browsers: ['last 5 versions']
        	}),
        	require('postcss-px-to-viewport')({
        		viewportWidth: 375, //视口宽度(数字)
        		viewportHeight: 1334, //视口高度(数字)
        		unitPrecision: 3, //设置的保留小数位数(数字)
        		viewportUnit: 'vw', //设置要转换的单位(字符串)
        		selectorBlackList: ['.ignore', '.hairlines'], //不需要进行转换的类名(数组)
                minPixelValue: 1, //设置要替换的最小像素值(数字)
                mediaQuery: false //允许在媒体查询中转换px(true/false)
        	})
    	]
}
第四种:flex布局(自适应)

原则:文字流式,控件弹性,图片等比缩放。

第五种:postcss-px2rem插件
  • 第一步:在pack.json文件的webpack中配置postcss-px2rem插件
css:{
   loaderOptions:{
      postcss:{
      plugins:[
        reuqire('postcss-px2rem')({remUnit:100}) //换算的基准
      ]
     }
   }
}
  • 第二步:正常的css写法。注意:border为1px时,部分机型可能看不到,加上no,禁止rem转换。
title:{
  margin:70px;
  width:200px;
  border: 1px solid red; /*no*/
}

自适应方案的对比

  • 媒体查询 如果在浏览器大小改变时,需要改变的样式太多,那么多套样式代码会很繁琐。
  • rem布局 在响应式布局中,必须通过js来动态控制根元素font-size的大小。

移动端1像素显示变粗问题

px是一个相对单位,相对的是设备像素。

  • transform: scale(0.5) 方案
div {
    height:1px;
    background:#000;
    -webkit-transform: scaleY(0.5);
    -webkit-transform-origin:0 0;
    overflow: hidden;
}
  • css根据设备像素比媒体查询后的解决方案
/* 2倍屏 */
@media only screen and (-webkit-min-device-pixel-ratio: 2.0) {
    .border-bottom::after {
        -webkit-transform: scaleY(0.5);
        transform: scaleY(0.5);
    }
}

/* 3倍屏 */
@media only screen and (-webkit-min-device-pixel-ratio: 3.0) {
    .border-bottom::after {
        -webkit-transform: scaleY(0.33);
        transform: scaleY(0.33);
    }
}

图片模糊问题

原因

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

解决方案

为了保证图片质量,应该尽可能让一个屏幕像素来渲染一个图片像素,针对不同的DPR的屏幕,需要展示不同分辨率的图片。

  • 使用media查询判断不同的设备像素比来显示不同精度的图片(只适用于背景图)
.avatar {
   background-image:url(img_1.png)
}
@media only screen and (-webkit-device-pixel-ratio:2){
    .avatar {
       background-image:url(img_2.png)
    }
}
@media only screen and (-webkit-device-pixel-ratio:3){
    .avatar {
       background-image:url(img_3.png)
    }
}
  • image-set(只适用于背景图)
.avatar {
   background-image:-webkit-image-set("img_1.png" 1x,"img_2.png" 2x,"img_3.png" 3x)
}
  • srcset 使用img标签的属性,浏览器会自动根据像素密度匹配最佳显示图片
<img src="img_1.png" srcset="img_2.png 2x,img_3.png 3x">
  • JavaScript拼接图片URL 使用window.devicePixelRatio获取设备像素比,遍历所有图片,替换图片地址
const dpr = window.devicePixelRatio
const images = document.querySelectorAll('img');
images.forEach((img)=>{
  img.src.replace('.','@${dpr}x.')
})

横屏适配

  • window.orientation:获取屏幕旋转方向
window.addEventListener('resize',()=>{
  if(window.orientation === 180 || window.orientation === 0) {
    // 正常方向或屏幕旋转180度
     console.log('竖屏')
  };
  if(window.orientation === 90 || window.orientation === -90) {
  // 屏幕顺时针旋转90度或屏幕逆时针旋转90度
     console.log('横屏')
  }
})
  • CSS检测横屏
@media screen and (orientation:portrait){
  /*竖屏*/
}
@media screen and (orientation:landscape){
  /*横屏*/
}