移动端h5项目的兼容和适配问题

解决兼容性问题的关键在于对移动端产品的生存环境进行梳理,在此基础之上制定应对策略。

所谓生存环境主要分为三个维度:
①操作系统,比如iOS6和iOS7,或者安卓各家的定制系统的不同版本
②浏览器,主要分为App容器,原生浏览器,各品牌浏览器
③硬件环境,如不同的机型,品牌决定了屏幕大小性能等硬件限制。

为什么要做页面适配?

不同机型的屏幕尺寸、物理像素和逻辑像素都有差异,而ui 的尺寸一一般是固定的。

为什么都使用px的写法,会导致不同手机上样式的错乱?
因为不同屏幕尺寸不同,像素比不同。如在375x667尺寸的屏幕上,像素比为1,那么设置375px刚好占满屏,在750×1334 的屏幕上,像素比是2,那么刚刚好也占满了。
而在360*640屏幕上,由于该屏幕的deviceRatio同为1,水平方向渲染一个css像素实际使用1个像素,即该div水平方向由375个独立像素渲染,超出屏幕宽度,布局受到影响。
移动端h5项目的兼容和适配问题_第1张图片

什么是像素?
像素(Pel,pixel;pictureelement),为组成一幅图像的全部亮度和色度的最小图像单元。单位面积内的像素越多,图像的效果就越好。

什么是分辨率?
屏幕分辨率是指纵横向上的像素点数,单位是px。屏幕分辨率高时(例如 1600 x 1200),在屏幕上显示的像素多,单个像素尺寸比较小.

为什么移动端会存在1px的问题?
还是因为在高清屏幕出现之后,出现了设备像素比的概念,以 iPhone6 为例,其 dpr = 2、屏幕尺寸(CSS 像素) 为 375x667,一般设计稿提供 2 倍图尺寸为 750x1334 。那么设计稿中的 1px,对应屏幕尺寸其实应该写成 0.5px。再由 dpr 计算公式可知,0.5 * 2 = 1px 物理像素。
所以直接写1px会偏粗。

那能不能直接写0.5px呢?
不行,在 PC 端浏览器的最小识别像素为 1px。即便代码中是 0.5px ,但默认会被浏览器识别并渲染为 1px
手机端不同系统的不同浏览器对小数点的px有不同的处理,例如:手机上观察iOS的Chrome会画出0.5px的边,而安卓(5.0)原生浏览器是不行的。所以如果我们把单位设置成小数的px包括宽高等,其实不太可靠,因为不同浏览器表现不一样。

如何解决1px的问题?
这个文章还不错,有解决方案。
https://mp.weixin.qq.com/s?__biz=MzUyMDk4OTU5OA==&mid=2247528392&idx=7&sn=d025e40c3dafa46108118f351ed1758f&chksm=f9e3d139ce94582fa75c29d871a2b2adde65ea2e68c682ff7b1f218417321e783243c7925f5b&scene=27

移动端页面适配有什么方案?
1、媒体查询使用CSS3的媒体查询功能,根据不同的屏幕尺寸和方向,应用不同的样式规则。通过设置不同的样式,可以适配不同的设备。个人认为是一种相对比较麻烦的方案,需要在每个页面上写媒体查询的代码。

@media screen and (max-width: 800px) {
  body {
    width: 200px;
    height: 200px;
    background-color: blue;
  }
}
@media screen and (max-width: 500px) {
  body {
    width: 200px;
    height: 200px;
    background-color: red;
  }
}

2、百分比布局使用百分比单位设置元素的宽度、高度和边距等。这样可以根据父元素的尺寸自动调整子元素的大小,实现一定程度的自适应效果。因为不同属性的百分比值,相对的可能是不同的参照物,所以百分比往往很难统一,在移动端适配中使用是非常少的。
3、弹性盒子布局使用CSS3弹性盒子布局模型,通过设置弹性容器和弹性项目的属性,实现灵活的布局。弹性盒子布局可以根据容器的尺寸和内容的大小,自动调整项目的 布局和排列。
4、rem单位 + 动态的font-size
使用rem(根元素字体大小的倍数)作为单位来设置元素的尺寸。通过设置根元素的字体大小,可以控制整个页面的缩放比例,从而实现不同设备的适配。
rem(font size of the root element)是CSS3新增的一个相对单位,是指相对于根元素的字体大小的单位。flexible 的原理就是这个。
在开发中,我们需要考虑两个问题:
问题一:针对不同的屏幕,设置html不同的font-size(编写js代码、flexible库)
问题二:将原来要设置的尺寸,转化成rem单位(less/scss函数、postcss-pxtorem、vscdoe 的 px to rem 的插件,在编写时自动转化)

为什么rem不设置可以直接写?
因为他和vw、vh一样是相对单位,相对根html字体大小,没设置默认就是16px;

root 的字体大小就是根元素字体大小嘛?
rem 是根html的字体大小,如果没有设置,浏览器默认就是16px;不是root的字体大小,root 可能继承子组件库。
html {
  font-size: 50px; 
}
1rem = 50px;假如我想设置一个280px,应该写多少rem?那应该是280 / 50 px = 4.5rem 
但是根的字体大小也不应该是写死的,应该根据不同的屏幕大小写成动态的,然后根据ui提供的图的尺寸,换算出rem的值;

如何使用js 设置根html 字体的大小?
window.onload = function(){/*720代表设计师给的设计稿的宽度,你的设计稿是多少,就写多少;100代表换算比例,这里写100是为了以后好算,比如,你测量的一个宽度是100px,就可以写为1rem,以及1px=0.01rem等等*/getRem(720,100)
};
window.onresize = function(){getRem(720,100)
};
function getRem(pwidth,prem){var html = document.getElementsByTagName("html")[0];var oWidth = document.body.clientWidth || document.documentElement.clientWidth;html.style.fontSize = oWidth/pwidth*prem + "px";
}

由于viewport单位得到众多浏览器的兼容,lib-flexible这个过渡方案已经可以放弃使用,不管是现在的版本还是以前的版本,都存有一定的问题。建议大家开始使用viewport来替代此方。

5、使用vw、vh适配
vw(Viewport Width)、vh(Viewport Height)是基于视图窗口的单位,是css3中提出来的,基于视图窗口的单位。
vh、vw方案即将视觉视口宽度 window.innerWidth和视觉视口高度 window.innerHeight 等分为 100 份。
如何换算?
①less/scss函数②postcss-px-to-viewport-8-plugin ③VSCode插件
px to vw 的插件 ④

三、 个人推荐
本人推荐vw适配方案,之所以推荐此种方案,vw相对于rem的优势如下:
优势一:不需要去计算html的font-size大小,也不需要给html设置这样一个font-size;
优势二:不会因为设置html的font-size大小,而必须给body再设置一个font-size,防止继承;
优势三:因为不依赖font-size的尺寸,所以不用担心某些原因html的font-size尺寸被纂改,页面尺寸混乱;
优势四:vw相对于rem更加语义化,1vw刚好是1/100的viewport的大小;
优势五:可以具备rem之前所有的优点

移动端项目遇到的在不同机型上兼容性问题记录:

问题一:visibilitychange 事件,无论是桌面端 Safari,还是 iOS Safari,visibilitychange 事件不总是触发的
需求?
用户在当前页面打开新页面,再左滑返回时页面需要刷新。用户离开页面后,进行数据上报。
原因?
对于窗口最小化,Tab 隐藏等行为 visibilitychange 事件是正常的,但是如果是点击页面某个链接发生的当前页导航跳转,则 visibilitychange 事件不会触发。
如下代码,再Safari 浏览器下不会触发。

document.addEventListener('visibilitychange', function logData() {
  if (document.visibilityState === 'hidden') {
    navigator.sendBeacon('/log', { /* 要发送的数据 */ });
  }
});

解决方案?
使用 pageshow 和 pagehide 事件,判断是不是 Safari 浏览器,然后额外增加一个 pagehide 事件:

document.addEventListener('visibilitychange', function logData() {
    if (document.visibilityState === 'hidden') {
      navigator.sendBeacon('/log', postData );
    }
});
if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) {
    window.addEventListener('pagehide', function () {
        navigator.sendBeacon('/log', postData );
    });
}

有什么区别?
‘visibilitychange’ 事件通常都是挂载在 document 对象上,虽然现在最新的浏览器也支持挂载在 window 对象上,不过由于 Safari 14 之前的版本不支持,因此,是不推荐使用下面的语法的:

window.addEventListener('visibilitychange', () => {});
document.addEventListener('visibilitychange', function () {
    if (document.hidden) {
        log('visibilitychange: 页面隐藏');
    } else {
        log('visibilitychange: 页面显示');
    }
});

而 pageshow 和 pagehide 事件都是通过 window 对象进行注册的。

window.addEventListener('pagehide', function () {
    log('pagehide: 页面隐藏');
});

问题二:window.open 在ios上不跳转
历史文章

问题三: unload 和 beforeunload 事件在移动端经常未触发
原因?
因为用户访问完一个页面,往往是直接切换到其他 APP,然后通过杀进程关掉整个浏览器 APP,unload 事件就不会触发。unload 和 beforeunload 会阻止浏览器把页面存入缓存,影响浏览器前进和后退时候的响应速度。所以不建议在移动端写unload 和 beforeunload事件。

问题四:ios 上2022-01-13 报错invalidDate
原因:ios无法识别 - 格式的日期
解决方案:let newTime = oldTime.replace(/-/g, “/”)

问题五、移动端 HTML5 audio autoplay 失效问题
原因?
由于自动播放网页中的音频或视频,会给用户带来一些困扰或者不必要的流量消耗,所以苹果系统和安卓系统通常都会禁止自动播放和使用 JS 的触发播放,必须由用户来触发才可以播放。

问题六、IOS键盘唤起,输入内容,软键盘弹出,页面内容整体上移,但是键盘收起,页面内容不下滑
原因?
固定定位的元素 在元素内 input 框聚焦的时候 弹出的软键盘占位 失去焦点的时候软键盘消失
但是还是占位的。position: fixed的元素在IOS里,收起键盘的时候会被顶上去,特别是第三方键盘。
导致input框不能再次输入 在失去焦点的时候给一个事件
解决方案?

滑动到滚动的地方
changeBlur () {
     let u = navigator.userAgent, app = navigator.appVersion;
     let isIOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/);
     if(isIOS){
        setTimeout(() => {
          const scrollHeight = document.documentElement.scrollTop || document.body.scrollTop || 0
          window.scrollTo(0, Math.max(scrollHeight - 1, 0))
          }, 200)
     }
或者直接滚到底部
onblur="window.scrollTo(0, 0);"

问题七、IOS端h5页面上下滑动时卡顿
添加iso独有的属性

 webkit-overflow-scrolling: touch;
 overflow-scrolling: touch;Android3+和 iOS5+支持 CSS3 的新属性为 overflow-scrolling。

-webkit-overflow-scrolling 属性控制元素在移动设备上是否使用滚动回弹效果.

auto: 使用普通滚动, 当手指从触摸屏上移开,滚动会立即停止。

touch: 使用具有回弹效果的滚动, 当手指从触摸屏上移开,内容会继续保持一段时间的滚动效果。继续滚动的速度和持续的时间和滚动手势的强烈程度成正比。同时也会创建一个新的堆栈上下文。

问题八、IOS双击页面缩放禁止
问题描述:IOS10中自带的Safari浏览器不识别meta viewport
IOS10以外解决移动端禁止页面缩放的方法:

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

IOS10解决方案:
(1)禁用双指缩放

document.documentElement.addEventListener('touchstart', function (event) {
  if (event.touches.length > 1) {
    event.preventDefault();
  }
}, false);

(2)禁用手指双击缩放

var lastTouchEnd = 0;
document.documentElement.addEventListener('touchend', function (event) {
  var now = Date.now();
  if (now - lastTouchEnd <= 300) {
    event.preventDefault();
  }
  lastTouchEnd = now;

问题九、安卓部分版本input的placeholder偏上
安卓部分版本的input的lineHeight层级比父级高

 input{
      line-height:normal;
  }

问题十、ios 设置input 按钮样式会被默认样式覆盖

input,textarea {
border: 0;
-webkit-appearance: none;
}

5.IOS键盘字母输入,默认首字母大写
解决方案,设置如下属性

<input type="text"autocapitalize="off"/>

问题十一、input上传文件multitype在安卓机上存在兼容问题,图片可以选择多个,文件只能选一个
解决办法:
使用企微的api解决

问题十二、IOS手机测试时会发现加了margin-bottom的属性无效
解决:替换为padding-bottom或者放个空盒子有高度宽度占位即可。

你可能感兴趣的:(工作沉淀,前端)