1px 边框问题:
问题:在高清屏下,1px 边框会变粗。
产生原因:UI 设计师想要的 1px 的线,是 1 物理像素;而开发工程师代码中的 1px,是 1 css 像素。在 dpr = 1 的标清屏下,一个方向上,1 css 像素 = 1 物理像素,此时没有问题;在 dpr > 1 的高清屏下,比如 dpr = 2 时,在一个方向上 1 css 像素 = 2 物理像素,此时,如果开发工程师把边框设置为 1px,其实在设备上就是 2 物理像素,并不是最细的线了。
解决方法:
-
使用媒体查询,针对不同设备像素比设置对应的小数:
@media screen and (-webkit-min-device-pixel-ratio: 2) { .border { border: 0.5px solid #ccc } } @media screen and (-webkit-min-device-pixel-ratio: 3) { .border { border: 0.333333px solid #ccc } }
优点:简单、好理解。
缺点: 兼容性差,目前只 IOS8+ 才支持,在 IOS7 及其以下、安卓系统都是显示 0px。 -
使用伪元素和 transform:
// 可以像方案一增加媒体查询分别针对不同 DPR 的设备进行不同的缩放 .line{ position:relative; } .line::after{ content:""; width:100px; height:1px; transform:scaleY(.5); position:absolute; top:0; left:0; }
优点:所有场景都能满足。
缺点:对于已经使用伪类的元素,可能需要多层嵌套。 -
动态设置 viewport 的缩放:
<meta name="viewport" content="initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"> var viewport = document.querySelector("meta[name=viewport]"); if (window.devicePixelRatio == 1) { viewport.setAttribute('content', 'width=device-width,initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no'); } 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'); } if (window.devicePixelRatio == 3) { viewport.setAttribute('content', 'width=device-width,initial-scale=0.3333333333333333, maximum-scale=0.3333, minimum-scale=0.3333333333333333, user-scalable=no'); }
优点:所有场景都能满足;一套代码,可以兼容基本所有布局。
缺点:老项目修改代价过大,只适用于新项目。 -
使用
box-shadow
模拟像素:.box-shadow-1px { box-shadow: inset 0px -1px 1px -1px #c8c7cc; }
优点:代码量少;可以满足所有场景。
缺点:边框有阴影;颜色变浅。# click 事件 300ms 延迟问题:
问题:手指触摸到屏幕的那一刻开始,到 click 事件被触发,可能会有 300ms 的延迟。
产生原因:双击缩放。移动端浏览器在点击屏幕后并不会马上触发 click 事件,而是会等待大概 300ms,来检测用户是否是双击。
解决方法:
- Touch 触摸事件不会有延迟,可以使用 touchstart 或者 touchend 代替 click 事件。
- 禁止双击缩放。浏览器就会认为不需要双击检测,点击屏幕后也就不会再延迟 300ms 了。可以使用以下方式中的任意一种:
- 设置
touch-action: manipulation;
,只允许滚动和持续缩放操作。 - viewport 中设置禁止缩放(推荐):
<meta name='viewport' content='width=device-wdith, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalabe=no' />
。
- 设置
Touch 事件点击穿透问题:
* {
margin: 0;
padding: 0;
}
.mask {
width: 100%;
height: 100px;
background-color: rgba(0, 0, 0, .2);
position: absolute;
top: 0;
left: 0;
}
.btn {
width: 100%;
height: 100px;
}
<div id="mask" class="mask"></div>
<button id="btn" class="btn">提交</button>
// 点击遮罩层,但是也触发了按钮的 click 事件
const mask = document.getElementById('mask')
const btn = document.getElementById('btn')
mask.addEventListener('touchstart', () => {
mask.style.display = 'none'
console.log('touchstart 到了遮罩层')
})
btn.addEventListener('click', () => {
console.log('click 到了按钮')
})
点击遮罩层,首先触发 touchstart 事件,遮罩层隐藏;然后触发 click 事件,由于这时遮罩层已经消失,所以触发 click 事件的元素就变为了下一层的同样位置的按钮。
问题:某些时候,用手指进行点击触摸时,如果触摸的是层级比较高的上层的元素(例如:遮罩层),有可能导致下层的元素被点击中。也就是穿透了上面一层的元素,点到了下层的元素。
产生原因:移动端 Touch 事件会立即触发,而 click 事件会延迟一段时间再触发。
解决方法:
- 使用定时器延时消失上层元素。
// 不推荐。比较生硬,感觉像卡顿了一下 mask.addEventListener('touchstart', () => { setTimeout(() => { mask.style.display = 'none' }, 200) console.log('touchstart 到了遮罩层') })
- 上层元素消失过程中添加动画效果。
// 推荐 .mask { opacity: 1; transition: opacity .5s; } mask.addEventListener('touchstart', () => { mask.style.opacity = 0 console.log('touchstart 到了遮罩层') }) // opacity 为 0 只是将元素变成了透明,并没有真正消失,会导致再次想要点击下层元素点击不到,因此还需要监听到动画效果完成后,设置元素真正消失 mask.addEventListener('transitionend', () => { mask.style.display = 'none' })
图片显示问题:
前端引入图片显示有两种方式:通过 img 标签引入和通过背景图片引入。
通过 img 标签引入:
问题:在 PC 端的时候,直接给通过 img 标签引入的图片设置固定宽高显示即可。但是在移动端,由于屏幕大小多变,直接给图片设置固定宽高就不可取了。那么,如何让图片在移动端更好地显示呢?
解决方法:
-
宽高使用百分比,并且不同时设置,只设置其中的一个,让图片能够等比例缩放。
缺点:当图片实际的宽度小于父容器的宽度时,图片会被强行拉伸到与父容器同宽,导致图片失真。
img { width: 100%; }
-
第一种解决方法中,使用
max-width
代替 width。缺点:当图片实际的宽度小于父容器的宽度时,图片不会随着父容器宽度的变大而进一步拉伸,导致父容器中会出现一片空白区域。
img { max-width: 100%; }
通过背景图片显示引入:
.bg-container {
width: 100%;
height: 200px;
background: url(./bg.png) no-repeat;
}
<div class="bg-container"></div>
问题:背景图片的大小不会变化,但是随着屏幕宽度的变化,越小的屏幕图片显示越不完整。
解决方法:
- 如果背景区域的高度是固定的,可以使用
background-size: cover;
等比例缩放背景图片以完全覆盖背景区域,但是可能会导致背景图片部分看不见。.bg-container { width: 100%; height: 200px; background: url(./bg.png) no-repeat; background-size: cover; }
- 如果背景区域的高度是不固定的,可以让背景区域的宽高比等于背景图片的宽高比,然后让背景图片覆盖整个背景区域。
.bg-container { width: 100%; background: url(./bg.png) no-repeat; // 宽度的百分比是相对于父容器的宽度,高度的百分比是相对于父容器的高度。因此不能直接使用 height 设置百分比。 // padding 的百分比是相对于父容器的宽度。因此可以使用 padding-top 设置百分比撑起父元素的高度。 padding-top: 50%; // 假设背景图片的宽度为 100px,高度为 50px,那么宽高比就为 100:50 background-size: cover; }
IOS 系统和安卓系统的兼容性问题:
- PC考虑的是浏览器的兼容性,而移动端开发考虑的更多的是手机兼容性。(因为目前不管是 android 手机还是 ios 手机,一般浏览器使用的都是 webkit 内核)
- 在部分事件的处理上,移动端多出来的事件是触屏事件,而缺少的是 hover 事件。 另外包括移动端弹出的手机键盘的处理等。
IOS 系统:
z-index
无效问题:
Safari 浏览器直接忽略 z-index
,可以使用 translateZ 来替代,translateZ 值越大,越在上层。
点击 input 输入框,页面会自动放大:
产生原因:IOS 有点击输入框自动放大的功能,认为这样用户体验好。
解决方法:使用禁止缩放页面来解决。
<meta name="viewport" content="width=device-width, initial-scale=1.0,minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
有时莫名出现没内容的地方一片白(在设置了背景色之后即可看到这个问题的存在):
解决方法:在底部加个透明度为 0 的标签。
<div class="footer">iOS有时莫名出现没内容的地方一片白</div>
.footer{
position: fixed;
bottom: 0;
right: 0;
color: transparent;
}
安卓系统:
键盘弹起时页面布局压缩变形的问题:
产生原因:安卓手机出现软键盘导致页面压缩变形,苹果手机不会。原因是安卓手机计算的是页面可显示的区域,软键盘那块区域不算,所以在 body 没有固定的情况下就会被压缩、顶起。
解决方案:在页面加载完后,获取当前屏幕的高度对 body 进行固定赋值。
mounted(){
document.getElementsByTagName('body')[0].style.height=`${document.documentElement.clientHeight}px`
}
absolute 或 fixed 定位在底部的元素被键盘顶起的问题:
解决方法:
- 初始设置默认屏幕高度和实时屏幕高度。
- 通过
window.onresize
监听屏幕的高度变化,获取实时屏幕高度。 - 当默认屏幕高度小于实时屏幕高度时,隐藏底部定位的元素。
<div class="footer" v-if="isShowFooter">
<img src="../../assets/login/ic_agree.png" class="agree-img"/>
</div>
data(){
return {
isShowFooter:true,
docmHeight: document.documentElement.clientHeight, //默认屏幕高度
showHeight: document.documentElement.clientHeight, //实时屏幕高度
}
}
watch:{
showHeight() {
if(this.docmHeight > this.showHeight){
this.isShowFooter = false
}else{
this.isShowFooter = true
}
}
},
mounted() {
// window.onresize监听页面高度的变化
window.onresize = ()=>{
return(()=>{
this.showHeight = document.body.clientHeight;
})()
}
}