移动端(安卓、ios、端)的兼容性问题
前⾔:这⾥移动端主要指 hybrid app 中的H5页⾯。app 中对页⾯样式和功能的需求会更精细⼀点。
1、适配:⼿机端的尺⼨多样,3.5英⼨的 iPhone4应该是最⼩的,只要考虑兼容到iPhone4 就可以了。(iPhone4的⽤户量现在也很少,有时只要兼容到iPhone5 就可以了)  总结:
rem + 媒体查询适配(rem基本可以适配⼤部分的移动端的适配,解决不了的使⽤媒体查询基本可以解决),重点熟练下媒体查询⼿机是⽤height还是device-height来查询的。有的⼿机,下⾯有⼀条⿊⾊的⼿机按键,此时的device-height有没有包含这块⾼度。
注:不同⼿机宽度基本没什么适配的,rem基本就解决了。主要是针对需要⼀屏显⽰的页⾯,⾼度差距太⼤,⽤rem基本没什么效果。
2、 JS 呼起和隐藏键盘(默认是需要⽤户点击输⼊框才能呼起):智能机的键盘都是软键盘。应⽤中有呼起键盘和隐藏键盘的需求。
 呼起键盘:进⼊搜索页,要求键盘拉起。主要聚焦到input框就可以。(安卓上没有问题,ios上⽆效)
this.$refs.input.focus() // 在⼀个demo上安卓和 ios都有效,但是正式项⽬中,ios中⽆效
 隐藏键盘:通过给input标签,设置 readonly 属性就可以使键盘收起来了。(安卓上没有问题,ios上⽆效)
<input type="text" ref="input" :readonly="readonly">
或,下⾯的⽅法安卓和 ios上都有效
this.$refs.input.blur()
3、软键盘呼起引起的兼容性问题:
  a、会使webview⾼度变⼩(这⾥就要求body最好设置⼀个最⼩的⾼度为没有键盘时的⾼度),并且 fixed定位元素跟着键盘上移(安卓的问题):
   问题原因:
      安卓⼿机:同⼀个⼿机,H5页⾯的视⼝⾼度在键盘拉起和隐藏时,不⼀样的。拉起时,键盘部分的⾼度变成原⽣的内容了,H5容器⾼度就变⼩了。(H5页⾯本⾝的⾼度是不变的)
         ⼀般情况这个都不影响,但是如果底部有fixed定位的话。键盘拉起时,会把这块内容也移上去
的,需要做⼀个判断进⾏隐藏(这个隐藏必须是针对对安卓机,ios机上不能隐藏。ios上也隐藏了,键盘)。
      ios⼿机:更不正常,键盘弹出不影响H5容器的⼤⼩变化的,即clientHeight的⼤⼩不变,但是却出现了滚动(如果clientHeight同步变⼩,可以理解为变成了⼀个⼩屏的容器。但是ios这样,只是把滚动条的范围变⼩了;
  滚动条到底了,页⾯的底部在键盘的上了。键盘遮挡的部分也是属于clientHeight,这⼀部分的⾼度,凭空给H5页⾯了)。总结起来就,ios中键盘弹起,会给H5页⾯的⾼度增加了⼀个键盘的⾼度。
      个⼈看法:键盘弹出,可以这样理解为h5的底板的⼤⼩就是webview的⼤⼩,包括键盘。底板上⼀张画布,画布⾼度可以被撑⾼,html、body等元素是固定在画布上的。浏览器中页⾯的滚动的跟着画布滚动的。
         安卓上,画布的最⼩⾼度是底板不包括键盘的⾼度;ios上,画布的最⼩⾼度是低板的⾼度加上键盘的⾼度。(css⽆⾮操作画布的属性)
    解决问题:
      页⾯只能是⼀屏的:这个要考虑⾃适应。键盘呼起,webview的⾼度变⼩了。使⽤ absolute 定位,
bottom 设置,定位参考的元素⾼度不能受webview⾼度的影响(给body设置⼀个最⼩⾼度,开始就通过js获取);或者使⽤top设置⾼度。
               如果,要考虑所有⼿机中底部的按钮必须离底部相同的距离。这个可能就要使⽤js获取没有键盘时屏幕的⾼度。然后把定位参考元素⾼度设置为这个值。
      页⾯可以滚动:这个⽐较简单,没有键盘时,页⾯⾼度已经⼤于容器的⾼度了。呼起键盘后还是滚动的。没有任何的问题。 
  b、软件盘唤起,在表单外滑动,软键盘不关闭,点击才会关闭(这点表现,两者是⼀样的,没有兼容性问题,记录下这个特性)。
手机上可以打html与css的app
  c、软件盘的关闭⽅法,表单失去焦点 / 软件盘上的关闭按钮(这点表现,两者是⼀样的,没有兼容性问题,记录下这个特性)
  d、点击软键盘上的关闭按钮、安卓⼿机不会触发表单的  blur 事件,ios可以。(安卓的问题)
    需要软件盘关闭时执⾏代码,在安卓上就需要做兼容处理。(解决安卓的⼀些兼容性问题,必须要在软键盘关闭时执⾏。如键盘拉起时,会把底部fixed元素移上去的,需要做⼀个判断进⾏隐藏;关闭软键盘时,再显⽰)
  e、软键盘唤起时fixed 元素失效(ios的问题):如下说明
4、软键盘呼起时fixed失效(fixed+input,ios的问题):(有⼀个inobounce插件,可以直接解决这个问题。功能上相当于使得画布的⾼度,始终等于键盘上⾯到顶部的⾼度。不管键盘有没有出来都这样。有时间可以看下源码)
  a、顶部 fixed元素,input框在顶部:点击这个input框,fixed虽然失效了,但是页⾯不会滚动。fiexd元素会滚动是因为软键盘的弹出,fixed失效了(或变成了absolute),只要input失去焦点时,⽴刻把键盘关闭,fixed元素就没有滚动的机会。
    这样就可以解决fixed⽆效的问题了。
  b、底部 fixed元素,input框在底部:这个input输⼊框,点击这个输⼊框,页⾯都会向上滚动的。input框始终在键盘上⾯,键盘关闭,滚上去的页⾯也不会滚下来。
    这个当表单失去焦点时(或触发键盘隐藏事件时),让页⾯滚动到之前的位置就可以解决:
   document.documentElement.scrollTop = 100; // 这个值是弹出键盘前的位置
5、解决页⾯,返回时重复的问题(重要,app中返回是经常会⽤到的,所以浏览器历史记录需要页⾯控制下。页⾯前进或回退时,url只是参数的改变,页⾯是不刷新的)
   A(列表页) =》 B(详情页,B中有跳到A页⾯的按钮):列表A1 =》 B 点击跳到A的按钮 =》
  列表A2  =》B。这个时候回退时,B=》A=》B=>A 会出现不断重复的问题
  解决⽅案:使⽤vue路由的 vm.$place() ⽅法跳转,或原⽣的 place(URL)
6、input中占位⽂字,⽆法上下居中对齐(应该是字体⼩于12px,引起的问题)。
7、ios监听软键盘确认按钮:会⽆效(那个确认按钮会变化为换⾏按钮,换⾏时,监听key=13是对的。变成确认时,监听不了key=13的这个键。【猜测可能同⼀个键,确认的键盘码不是13】)
<form action=""> // form 标签必须加
<input v-model="wordName" type="search" placeholder="输⼊垃圾名称搜⼀搜" @input="inputText" ref="input"/>
</form>
 这个⽅法可以不⽤监听key=13事件,直接监听form的 submit 事件同样可以监听确认按钮。
 (项⽬中是在搜索框右边加⼀个搜索按钮,不使⽤软键盘上的确认按钮)
input[type="search"]::-webkit-search-cancel-button{
-webkit-appearance: none;
}
  原因(个⼈猜测):移动端是⽀持12px以下的字体的。安卓上⼩于12px的字体,字体会溢出标签⼀点。亲测,如下图,设置了居中的样式,字体设置为10px,字体溢出他的包含标签sapn。即,
          浏览器12px以下的字体以12px显⽰;移动端⼩于12px的字体,也可以正常显⽰,但是排版有点⼩问题(会上移)。
 常规的居中⽅案都没有⽤的,使⽤scale可以近似解决,但是不够完美。⽬前没有其他的可⾏⽅法。或者使⽤媒体查询,⼩于12px的尺⼨,就以12px显⽰。折腾了好久,不到好的办法
9、ios上vue框架中返回keep-alive 的页⾯(这个页⾯⽐较长,有滚动。滚到顶部,进⼊下⼀个页⾯),会出现⽩屏(被什么东西遮挡住了)。⼿轻轻滑动下,遮挡层就消失了。
10、ios中 h5页⾯输⼊框点击空⽩处不会失去焦点软键盘不会收起(重要):
11、ios-H5 中,不过页⾯的⾼度是多少(html,body设置为50px,只有⼀个input标签),只要键盘弹出,页⾯就⼀定会滚动。通过上⾯的⽅法,input失去焦点马上隐藏键盘。这样活动的时候键盘隐藏,页⾯就不会滚动了。
  但是,在input内部滑动,还是会带动页⾯滚动的,给input标签添加⼀个touchmove阻⽌默认事件。就完美了
event.preventDefault()
},false)
  (直接让页⾯不滚动,暂时没有到解决⽅案,因为html,body的⾼度根本没有超出键盘的范围。我想应该是有直接的解决办法的,百度H5页⾯,不知道是怎么解决的,有时间研究下)
12、禁⽌ ios 页⾯上下滚动回弹(橡⽪筋效果):没有橡⽪筋效果,就不会出现fixed失效的问题。
  解决⽅案1、使⽤ inobounce.js 插件(亲测有效)。但是整个页⾯都不能滑动了,有溢出的屏幕的元素也不能滑动的。
     ⾥⾯的⽅案1:【纯 css 实现】
<template>
<div class="container">
<div>
<p v-for="item in 200" :key="item">测试测试测试测试测试测试</p>
</div>
</div>
</template>
<style scoped>
.container {
position: fixed;
top: 0;
bottom: 0;
width: 100%;
height: 100%;
overflow: scroll;
}
</style>
      ⾥⾯的⽅案2:【使⽤js,阻⽌ body 默认的 touchmove事件,会导致整个页⾯的的上下滑动被禁⽌。还要解决⾥⾯内容的上下滑动问题】
             ⽬前测试这个⽅案还没有成功,后续会继续验证。
  说明:通过阅读 inobounce.js 的源码(源码代码量很少),发现原理就是使⽤window代理屏幕上的 touchmove 事件( window.addEventListener)。判断当前触发的对象是⾮滚动区域,则阻⽌事件的默认
⾏为;滚动区域,不阻⽌事件。
     具体实现,插件中考虑的⽐较全⾯,⽐较复杂。直接使⽤插件是最⽅便的。
  解决⽅案3【完美,但是局限于⾃⼰的app】:ios 原⽣的 app 是可以禁⽤ webview 的回弹效果的。但是,在第三⽅的app 中运⾏,如中运⾏H5。我们是⽆法让原⽣app 禁⽤橡⽪筋效果。只能从 H5 本⾝考虑解决⽅案。
  安卓和 ios 对软键盘关闭的⾏为是不⼀样的。安卓中软键盘关闭,webview⾼度变化(blur事件不⼀定触发);ios中软键盘关闭,触发 blur事件【或focusout】(webview⾼度没有变化)。
14、 fixed的遮罩层上⾯可以滚动,下⾯内容禁⽌滚动。(有时间再单独研究下,fiexd遮罩层上的事件,如点击,滚动对下⾯的内容的影响)
  解决⽅案:移动端开发,body内的元素,最外层的标签要设置⼀个⾼度(⼀般是容器的⾼度100vh),溢出属性设置滚动。不要让body的⾼度超过100vh了,不然 fixed的遮罩层上⾯滚动,下⾯的内容⼀会滚动。处理起来⽐较⿇烦。
  亲测问题:在fixed层上使⽤vue的    @touchmove.prevent  阻⽌默认事件,结果上下两层的滚动都禁⽌了;使⽤  @touchmove.stop 阻⽌冒泡事件,结果⽆效。
  问题分析:
    a、body 的⾼度超过容器⾼度,body是⽐较特殊的标签。fixe层的touchmove事件,会导致body滚动。
    b、⾮ body的标签,如 div标签是⼀个溢出滚动的标签,其内的⼀个标签作为 fixed遮罩层,则遮罩层内的内容滚动,不会触发这个div标签的⾼度。(个⼈猜测,遮罩层虽然在div标签内,但是已经脱离标准⽂档流,事件上于div已经没有关系)
      c、body是⽐较特殊的标签,fixed遮罩层内的 touchmove 还是会到 body上的。
15、 IOS获取短信验证码--⾃动填充被复制两遍问题:
  说明:测试测出这个bug,⾃⼰测试⼀直没复现出来。可能触发机制不是很准确。
       在安卓的容器(webview)中,视频播放需要点击两次,即播放-暂停-播放,才能开始播放。(暂时没有到解决⽅案)
  注意:1、移动端1像素问题,和视⼝⼤⼩没有关系。css中的像素是逻辑像素。
     2、最后展⽰到屏幕上的是物理像素实现的,即逻辑像素经过计算,转化为物理像素显⽰。
     3、0.5px是有兼容性问题的,安卓⼿机会把  0.5px  处理成 1px,显⽰还是⽐较粗的。
  产⽣原因:在retina屏中,1个逻辑像素,可能需要2个或3个物理像素来显⽰。⽽px已经是css最⼩的逻辑像素单位,css中0.5像素会处理成0px。
       所以⽆法实现1px的物理像素渲染。
      【注意:css中的px,是渲染在布局视⼝上的。是布局视⼝⽆法正常处理0.5px,布局视⼝的1px是可以渲染出0.5px的物理像素的】
  解决⽅法:
     1、0.5px ⽅案:可能⼿机⼚商知道存在这个问题,所以现在有的⼿机已经可以实现0.5px渲染1px的物理像素了。
            在IOS8+,苹果系列都已经⽀持0.5px了,可以借助媒体查询来处理。
     2、最佳解决⽅案:transform: scaleY(0.5);    【在⼀个⽅向缩⼩⼀半】
<meta name="viewport" content="width=device-width,initial-scale=0.5, maximum-scale=0.5, minimum-scale=0.5, user-scalable=no">
      原理:将布局视⼝放⼤,布局视⼝最后还是要全部呈现到屏幕宽度的【即缩⼩】。如dpr=2时,这时候 css 中1px就是布局视⼝的1px,
        布局视⼝中的1px会被处理成 0.5px对应的物理像素进⾏渲染。
总结:安卓和ios的兼容性,主要的问题,还是软件键盘引起的。所以两者关于软键盘的处理⼀定要让他们尽可能的保持⼀致。如下要做到⼀致:
  a、输⼊框,聚焦时弹出软件盘。失去焦点时,隐藏软件盘(ios需要处理的)。
  b、ios 的橡⽪筋效果⼀定要禁掉(使⽤ inobounce 插件)。
  c、ios 和安卓两者对软键盘关闭的判断的逻辑不同(软键盘关闭时,执⾏代码这个功能是⼀定会⽤到的)。现在的⼿机不是ios就是安卓⼿机,可以把这两段代码封装在
⼀个⽅法⾥,变成⼀个软键盘关闭的事件(实际开发中,⼀般只是对⼀种情况做出处理)。
if(isIOS){ // 在 ios 中执⾏下⾯监听事件,捕获软键盘关闭事件。(isIOS 通过获取 navigator.userAgent就可以判断)
window.addEventListener('focusin', () => {  // 键盘弹出事件处理
alert("iphone 键盘弹出事件处理")
});
window.addEventListener('focusout', () => {  // 键盘收起事件处理
alert("iphone 键盘收起事件处理")
});
}
if(isAndroid){ // 在 android 中执⾏下⾯监听事件,捕获软键盘关闭事件
const innerHeight = window.innerHeight;
window.addEventListener('resize', () => {
const newInnerHeight = window.innerHeight;
if (innerHeight > newInnerHeight) {  // 键盘弹出事件处理
alert("android 键盘弹窗事件");
} else {  // 键盘收起事件处理
alert("android 键盘收起事件处理")
}
});
}
感悟:1、如果不⽤输⼊框,基本没有什么兼容性问题。css的兼容性问题也基本没有,就⼏个默认样式的区别;没有软键盘,ios回弹效果也没什么影响,可以不做处理。
   2、上⾯讲的安卓和ios上的兼容性问题,都是webview内核的兼容性问题。hybrid-app中SDK⽅法也会有兼容问题的,这种bug完全就是安卓和ios软件开发者没有(或⽆
法)统⼀引起的。⼀般安卓和ios是不同的⼈开发的。
     同样的软件,在ios上和安卓上清除缓存的功能都是不⼀样的。JS-SDK,在处理分享链
接链接也是有差异。⾃⼰公司开发是“杭州办事服务”APP,JS调⽤原
⽣的语⾳功能也存在兼容性问题。
⽂章阅读
1、移动端项⽬实战⼼得:
1、touch事件:
  touchstart:⼿指触摸到屏幕会触发
  touchmove:当⼿指在屏幕上移动时,会触发
  touchend:当⼿指离开屏幕时,会触发。
  touchcancel:可由系统进⾏的触发,⽐如⼿指触摸屏幕的时候,突然alert了⼀下,或者系统中其他打断了touch的⾏为,则可以触发该事件。(,Alt+Tab键)
 注意:使⽤ touchend 事件时,⼀定要考虑touchcancel事件。⽐如,语⾳功能。按下说话,突然弹出语⾳权限未开。这个时候⼿指拿开就不会触发touchend事件了,touch
事件被打断了,屏幕上⼀直是touchstart的状态。
    必须加上 touchcancel 事件,补充⼿指拿开后的页⾯状态。
移动端各种⾼度:(在pc上,浏览的⾼度对⽤户基本什么问题。但是在移动端浏览的各种⾼度问题。⽐如webview的⾼度,是否包含软键盘,不同的系统有差异性。的H5
页⾯,底部有左右前进按钮栏,webview的⾼度是否⼜包含这个)
1、window.outerHeight:【不常⽤】窗⼝的外部⾼度,包括所有界⾯元素(如⼯具栏/滚动条)。⼿机端,这个值和innerHeight⼀样。
3、body.offsetHeight:【不⽤】⽹页可见区域⾼(不确定具体的含义,值和scrollHeight⼀样。)。
4、body.clientHeight(兼容问题):浏览器窗⼝⾼度(不包括横向滚动条)。(不同浏览器表达⽅式不⼀样,⾕歌中他的值⽆效。使⽤ documentElement.clientHeight【常⽤】)
8、screen.height:【不常⽤】屏幕分辨率的⾼,即显⽰器的⾼度。
各种⾼度键盘没有弹出(安卓)键盘弹出后(安卓)键盘没有弹出(ios)键盘弹出后(ios)⽹页底部左右箭头栏隐藏(ios)⽹页底部左右箭头栏显⽰(ios)window.outerHeight647380 603 603
window.innerHeight647 380 603 603,293(滚动到底部)
body.offsetHeight771 771 793 793
body.clientHeight771 771793 793
body.scrollHeight795 795817 817
body.scrollTop0 68 0 0
window.screenTop0 0 0 0
screen.height720 720 667 667
screen.availHeight720 720 667 667