移动端适配方案

移动端适配方案

1、媒体查询

通过媒体查询的方式,编写适应不同分辨率设备的的css样式

1
2
3
4
5
6
7
8
9
10
@media screen and (max-width: 320px){
    ....适配iphone4的css样式
}
@media screen and (max-width: 375px){
     ....适配iphone6/7/8的css样式
}
@media screen and (max-width: 414px){
    ....适配iphone6/7/8 plus的css样式
}
......

常用媒体类型:

all:所有设备

screen:用于电脑屏幕或其他使用屏幕显示设备的设备类型

speech:屏幕阅读器等发声设备

优点:

  • 方法简单,只需修改css文件

  • 调整屏幕宽度时不用刷新页面就可以响应页面布局

缺点:

  • 代码量大,不方便维护

  • 不能够完全适配所有的屏幕尺寸,需要编写多套css样式

2、动态 rem 方案(flexible + postcss-pxtorem)

lib-flexible的思想是根据不同屏幕修改根元素 font-size 大小,一般设置为屏幕宽度的十分之一

把整个视图宽度区域分为10份,每一份为一个1rem,总的就是10rem。

动态rem适配方案的流程:

  • meta 标签设置 viewport 宽度为屏幕宽度;
1
<meta name="viewport" content="width=device-width, initial-scale=1.0 user-scalable=no"></meta>
  • 开发环境安装 lib-flexible,安装完成后在根目录index.js中引入lib-flexible:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 安装
npm install postcss-pxtorem -D
 
 
// 配置参数
const path = require("path")
module.exports = ({ webpack }) => {
  // 某些组件库如vant视口宽度是375
  const designWidth = webpack.resourcePath.includes(path.join("node_modules", "vant")) ? 375 : 750
  // 以设备宽度 750 为基准计算 vw 的值
 
  return {
    plugins: {
      autoprefixer: {},
            'postcss-pxtorem',
             {
               rootValue: 75, //设计图最大宽度除以10  //比如750的宽就写成75
               selectorBlackList: ['test-'],
               propList: ['*'],
               exclude: /node_modules/i
             }
  }
}
  • 根据设计稿写样式,元素宽高直接取设计稿宽高即可,单位为 px,插件会将其转换为 rem;
  • 段落文本也按照设计稿写,单位为px,不需要转换为 rem;

lib-flexible 其核心代码如下:

  • 不同屏幕修改根元素 font-size 大小
1
2
3
4
5
6
7
// set 1rem = viewWidth / 10
function setRemUnit () {
    var rem = docEl.clientWidth / 10
    // docEl为document.documentElement,即html元素
    docEl.style.fontSize = rem + 'px'
}
setRemUnit();
  • 控制 viewport 的 width 和 scale 值适配高倍屏显示(规避了大家熟知的“1px问题)
1
2
3
4
var metaEL= doc.querySelector('meta[name="viewport"]');
var dpr = window.devicePixelRatio;
var scale = 1 / dpr
metaEl.setAttribute('content', 'width=device-width, initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');

  详细代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
(function(win, lib) {
      var doc = win.document;
      var docEl = doc.documentElement;
      var metaEl = doc.querySelector('meta[name="viewport"]');
      var flexibleEl = doc.querySelector('meta[name="flexible"]');
      var dpr = 0;
      var scale = 0;
      var tid;
      var flexible = lib.flexible || (lib.flexible = {});
      
      if (metaEl) {
          console.warn('将根据已有的meta标签来设置缩放比例');
          var match = metaEl.getAttribute('content').match(/initial\-scale=([\d\.]+)/);
          if (match) {
              scale = parseFloat(match[1]);
              dpr = parseInt(1 / scale);
          }
      } else if (flexibleEl) {
          var content = flexibleEl.getAttribute('content');
          if (content) {
              var initialDpr = content.match(/initial\-dpr=([\d\.]+)/);
              var maximumDpr = content.match(/maximum\-dpr=([\d\.]+)/);
              if (initialDpr) {
                  dpr = parseFloat(initialDpr[1]);
                  scale = parseFloat((1 / dpr).toFixed(2));   
              }
              if (maximumDpr) {
                  dpr = parseFloat(maximumDpr[1]);
                  scale = parseFloat((1 / dpr).toFixed(2));   
              }
          }
      }
 
      if (!dpr && !scale) {
          var isAndroid = win.navigator.appVersion.match(/android/gi);
          var isIPhone = win.navigator.appVersion.match(/iphone/gi);
          var devicePixelRatio = win.devicePixelRatio;
          if (isIPhone) {
              // iOS下,对于2和3的屏,用2倍的方案,其余的用1倍方案
              if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) {               
                  dpr = 3;
              } else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)){
                  dpr = 2;
              } else {
                  dpr = 1;
              }
          } else {
              // 其他设备下,仍旧使用1倍的方案
              dpr = 1;
          }
          scale = 1 / dpr;
      }
 
      docEl.setAttribute('data-dpr', dpr);
      if (!metaEl) {
          metaEl = doc.createElement('meta');
          metaEl.setAttribute('name', 'viewport');
          metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
          if (docEl.firstElementChild) {
              docEl.firstElementChild.appendChild(metaEl);
          } else {
              var wrap = doc.createElement('div');
              wrap.appendChild(metaEl);
              doc.write(wrap.innerHTML);
          }
      }
 
      function refreshRem(){
          var width = docEl.getBoundingClientRect().width;
          if (width / dpr > 540) {
              width = 540 * dpr;
          }
          var rem = width / 10;
          docEl.style.fontSize = rem + 'px';
          flexible.rem = win.rem = rem;
      }
 
      win.addEventListener('resize', function() {
          clearTimeout(tid);
          tid = setTimeout(refreshRem, 300);
      }, false);
      win.addEventListener('pageshow', function(e) {
          if (e.persisted) {
              clearTimeout(tid);
              tid = setTimeout(refreshRem, 300);
          }
      }, false);
 
      if (doc.readyState === 'complete') {
          doc.body.style.fontSize = 12 * dpr + 'px';
      } else {
          doc.addEventListener('DOMContentLoaded', function(e) {
              doc.body.style.fontSize = 12 * dpr + 'px';
          }, false);
      }
      
 
      refreshRem();
 
      flexible.dpr = win.dpr = dpr;
      flexible.refreshRem = refreshRem;
      flexible.rem2px = function(d) {
          var val = parseFloat(d) * this.rem;
          if (typeof d === 'string' && d.match(/rem$/)) {
              val += 'px';
          }
          return val;
      }
      flexible.px2rem = function(d) {
          var val = parseFloat(d) / this.rem;
          if (typeof d === 'string' && d.match(/px$/)) {
              val += 'rem';
          }
          return val;
      }
 
  })(window, window['lib'] || (window['lib'] = {}));
 

缺点:

  • 对高倍屏的安卓手机没做处理(安卓设备下默认都设置为 dpr = 1)
  • 不兼容响应式布局。
  • 无法正确响应系统字体大小。
  • flexible 对 iframe 的使用不兼容

3、Viewport 方案(postcss-px-to-viewport)

即将视觉视口宽度 window.innerWidth 和视觉视口高度 window.innerHeight 等分为 100 份。1vw 等于视口宽度的1%

vw 适配方案的流程:

  • meta 标签设置 viewport 宽度为屏幕宽度;
1
<meta name="viewport" content="width=device-width, initial-scale=1.0 user-scalable=no"></meta>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 安装
npm install postcss-px-to-viewport -D
# or
yarn add -D postcss-px-to-viewport
# or
pnpm add -D postcss-px-to-viewport
 
// 配置参数
const path = require("path")
module.exports = ({ webpack }) => {
  // 某些组件库如vant视口宽度是375
  const designWidth = webpack.resourcePath.includes(path.join("node_modules", "vant")) ? 375 : 750
  // 以设备宽度 750 为基准计算 vw 的值
 
  return {
    plugins: {
      autoprefixer: {},
      "postcss-px-to-viewport": {
        unitToConvert: "px", // 要转化的单位
        viewportWidth: designWidth,
        unitPrecision: 6, // 转换后的精度,即小数点位数
        propList: ["*"], // 指定转换的css属性的单位,*代表全部css属性的单位都进行转换
        viewportUnit: "vw", // 指定需要转换成的视窗单位,默认vw
        fontViewportUnit: "vw", // 指定字体需要转换成的视窗单位,默认vw
        selectorBlackList: [], // 指定不转换为视窗单位的类名,
        minPixelValue: 1, // 默认值1,小于或等于1px则不进行转换
        mediaQuery: true, // 是否在媒体查询的css代码中也进行转换,默认false
        // replace: true, // 是否转换后直接更换属性值
        exclude: [], // 设置忽略文件,用正则做目录名匹配 /node_modules/,使vant css转换
        landscape: false // 是否处理横屏情况
      }
    }
  }
}
  • 根据设计稿写样式,元素宽高直接取设计稿宽高即可,单位为 px,插件会将其转换为 vw;

  • 段落文本也按照设计稿写,单位为px,不需要转换为 vw;

优点:

  1. 实现简单:只需要通过安装插件和配置即可快速实现 px 转换为 vw 或 vh 单位。
  2. 提高开发效率:使用自适应布局,可以减少对不同屏幕尺寸的适配工作,提高开发效率。
  3. 适配性强:可以自适应不同设备和屏幕尺寸,适配性强,可以适应各种移动端设备的屏幕尺寸。

  4. 不需要动态计算根字体,页面渲染速度更快

缺点:

  • 不适用于字体大小:由于 vw 和 vh 单位不适用于字体大小,因此需要单独设置字体大小的转换方式。

  • 无法把行内样式中的 px 转换成视口单位(vw, vh, vmin, vmax),建议使用class,不适用行内样式
  • 兼容性问题:一些老版本的浏览器不支持 vw 和 vh 单位,需要使用兼容性处理或回退方案
  • 无法精确控制样式:由于浏览器的视口宽度和高度不同,转换后的样式可能会有一定的误差,无法精确控制样式
  • 比如当容器使用vwmargin采用px时,很容易造成整体宽度超过100vw,从而影响布局效果。当然我们也是可以避免的,例如使用padding代替margin,结合calc()函数使用等等...
 
posted @   alisa.huang  阅读(67)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
点击右上角即可分享
微信分享提示