移动端h5项目的兼容和适配问题
推测可能是Android在排版计算的时候参考了primyfont字体的相关属性(即HHead Ascent、HHead Descent等),而primyfont的查找是看font-family里哪个字体在fonts.xml里第一个匹配上,而原生Android下中文字体是没有family name的,导致匹配上的始终不是中文字体,所以解决这个问题就要在font-family里显式申明中文,或者通过什
移动端产品生存环境?
解决兼容性问题的关键在于对移动端产品的生存环境进行梳理,在此基础之上制定应对策略。
所谓生存环境主要分为三个维度:
①操作系统,比如iOS6和iOS7,或者安卓各家的定制系统的不同版本
②浏览器,主要分为App容器,原生浏览器,各品牌浏览器
③硬件环境,如不同的机型,品牌决定了屏幕大小性能等硬件限制。
为什么要做页面适配?
不同机型的屏幕尺寸、物理像素和逻辑像素都有差异,而ui 的尺寸一一般是固定的;
这就会导致同样的元素样式在不同设备上的呈现不一致;
举个例子:设计稿的尺寸是375
假设在ipone6上,其逻辑像素为375x667,设备的物理像素为750x1334 ,设备像素比为2;相当于1个设备独立像素对应2个物理像素,刚好占满。
假设在ipone5上,其逻辑像素为320x568,设备的物理像素为640x1136 ,设备像素比为2;假设通用设置元素宽度为375px,在设备上占750个物理像素,就会超出屏幕实际宽度,有可能出现滚动条。
那我们刚才提到的逻辑像素、设备的物理像素、设备像素比都是什么意思呢?
什么是像素?
像素(Pel,pixel;pictureelement),为组成一幅图像的全部亮度和色度的最小图像单元。单位面积内的像素越多,图像的效果就越好。
什么是分辨率?
屏幕分辨率是指纵横向上的像素点的个数,单位是px。屏幕分辨率高时(例如 1600 x 1200),在屏幕上显示的像素多,单个像素尺寸比较小。
什么是物理(设备)像素?
真实存在的,物理硬件屏幕上的最小显示单元,屏幕的分辨率,像素越高越清晰。
什么是虚拟(css)像素?
css中使用的逻辑单位,用于浏览器渲染web页面,通常情况下,一个css像素等于一个设备独立像素;
什么是设备独立像素 ?
是一种基于css像素的视觉单位,与设备的物理性质无关,是浏览器和操作系统用来确定在各种分辨率和尺寸的设备上如何呈现web页面的重要因素。
DPR:设备像素与设备独立像素之间的比率,表示屏幕上一个css像素对应几个设备像素
PPI:每英寸的像素点数量,通常用于描述屏幕的分辨率,越高屏幕的分辨率越高。
为什么移动端会存在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的问题?
方法一:使用边框图片实现,但是笔记模糊
border:1px solid transparent
border-image: url('./../../image.jpg') 2 repeat
方法二:使用box-shadow 实现,模拟的方案,仔细看能看出来
border-shandow:0 -1p 1px red(上边), 1px 0 1px -1px red(右边), 0 1px 1px -1px red(下边), -1px 0, 1px -1px red(左边);
方法三、使用伪元素+transform
同样为伪元素设置绝对定位,并且和父元素左上角对齐,将伪元素的宽度和长度放大2倍,然后再设置一个边框,以左上角为中心,缩放到原来的0.5倍
::after {
content: '';
display:block;
position: absolute;
top:0;
left:0;
width: 200%;
height: 200%;
border:1px solid red;
box-sizing: border-box;
transform:scale(1, 0.5);
transform-origin: left top;
}
方案四、设置对应viewport的rem基准值,这种方式就可以像以前一样轻松愉快的写1px
<script>
var 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') }
else 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') }
else { viewport.setAttribute('content', 'width=device-width,initial-scale=0.33,maximum-scale=0.33,minimum-scale=0.33,user-scalable=no') }
var doce1 = document.documentElement;
var fontsize = 32 * (doce1.clientWidth / 750 ) + 'px'
doce1.style.fontSize = fontsize
</script>
移动端页面适配有什么方案?
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(font size of the root element)是CSS3新增的一个相对单位,是指相对于根元素的字体大小的单位。flexible 的原理就是这个。
使用rem(根元素字体大小的倍数)作为单位来设置元素的尺寸。通过设置根元素的字体大小,可以控制整个页面的缩放比例,从而实现不同设备的适配。
在开发中,我们需要考虑两个问题:
问题一:针对不同的屏幕,设置html不同的font-size(编写js代码、flexible库)
问题二:将原来要设置的尺寸,转化成rem单位(less/scss函数、postcss-pxtorem、vscdoe 的 px to rem 的插件,在编写时自动转化)
为什么rem不设置可以直接写?
因为他和vw、vh一样是相对单位,相对根html元素的字体大小,没设置默认就是16px;
root 类名的字体大小就是根元素字体大小嘛?
不是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在低版本安卓设备上还有兼容问题,而vw,vh还没能实现所有浏览器兼容,
但是现在浏览器对vw、vh的兼容已经比较了,这种方案基本上不再使用了
5、使用vw、vh适配
vw(Viewport Width)、vh(Viewport Height)是基于视图窗口的单位,是css3中提出来的,基于视图窗口的单位。
vh、vw方案即将视觉视口宽度 window.innerWidth和视觉视口高度 window.innerHeight 等分为 100 份。
vh和vw方案和rem类似也是相当麻烦需要做单位转化,而且px转换成vw不一定能完全整除,因此有一定的像素差。我们可以使用less或者scss进行换算,也可以在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布局应用较多、还有百分比、vw、vh都会使用到。
什么是兼容?
所谓的 浏览器兼容性问题,是指因为不同的浏览器对同一段代码有不同的解析,造成页面显示效果不统一的情况,造成浏览器兼容性问题的根本原因就是 各浏览器使用了不同的内核,并且它们处理同一件事情的时候思路不同。通常在开发中我们可以访问(Can I Use)这个查询网站。它是一个针对前端开发人员定制的一个查询CSS、JS、HTML5、SVG在主流浏览器中特性和兼容性的网站,可以很好的保证网页在浏览器中的兼容性。
移动端项目遇到的在不同机型上兼容性问题记录:
JS 兼容问题
问题一: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 的触发播放,必须由用户来触发才可以播放。
css 兼容问题
问题六、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"/>
问题十二、IOS手机测试时会发现加了margin-bottom的属性无效
解决:替换为padding-bottom或者放个空盒子有高度宽度占位即可。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)