移动端 1px 问题
产生原因
简单点说就是因为 Retine 屏的分辨率始终是普通屏幕的 2 倍,1px 的边框在devicePixelRatio=2
的 retina 屏下会显示成 2px,所以在高清屏下 1px 就显得和 2px 差不多,具体细节可以参考这篇文章
解决方案
- 使用媒体查询
.border_1px {
border: 1px solid #f1f1f1;
}
@media (-webkit-min-device-pixel-ratio: 2) {
.border_1px {
border: 0.5px solid #f1f1f1;
}
}
缺点
兼容性差,有的 retina 屏会将 0.5 识别成 0px
- 伪类配合 transform
border-1px($color = #ccc, $radius = 2px, $style = solid)
position relative
&:after
content ''
pointer-events none // 解决iphone上的点击无效Bug
display block
position absolute
left 0
top 0
transform-origin 0 0
border 1px $style $color
border-radius $radius
box-sizing border-box
width 100%
height 100%
@media (-webkit-min-device-pixel-ratio: 2), (min-device-pixel-ratio: 2)
width 200%
height 200%
border-radius $radius * 2
transform scale(0.5) translateZ(0)
@media (-webkit-min-device-pixel-ratio: 3), (min-device-pixel-ratio: 3)
width 300%
height 300%
border-radius $radius * 3
transform scale(0.33) translateZ(0)
缺点
如果碰上其他伪类并存,则需要多层嵌套
- 使用 box-shadow 模拟
.border_1px {
box-shadow: inset 0px -1px 1px -1px #f1f1f1;
}
缺点
边框有阴影,颜色略浅
- 使用 view-port
对于devicePixelRatio=2
的:
<meta name="viewport" content="initial-scale=0.5, maximum-scale=0.5, minimum-scale=0.5, user-scalable=no">
对于devicePixelRatio=3
的:
<meta name="viewport" content="initial-scale=0.3333333333333333, maximum-scale=0.3333333333333333, minimum-scale=0.3333333333333333, user-scalable=no">
可以使用js
判断后动态插入:
let dpr = 1;
let ratio = window.devicePixelRatio;
let doc = document;
let head = doc.querySelector("head");
if (ratio === 2) {
dpr = 2;
} else if (ratio === 3) {
dpr = 3;
}
let scale = 1 / dpr;
let meta = doc.createElement("meta");
meta.setAttribute("name", "viewport");
meta.setAttribute(
"content",
`initial-scale=${scale}, maximum-scale=${scale}, minimum-scale=${scale}, user-scalable=no`
);
head.appendChild(meta);
rem
和 px
的取舍
如果项目只适配手机端,px
和rem
可以根据个人习惯自主选择。
如果项目适配多种设备,比如手机和 pad 设备,分辨率差的比较大的情况下,使用rem
最优
移动端点击背景图/链接有底色
/* 点击的时候底色透明即可 */
-webkit-tap-highlight-color: transparent;
禁止选择文字/图片
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
禁止复制/保存图片
img {
-webkit-touch-callout: none;
}
页面动画容易出现闪烁白屏
使用
translate3d
优化
-webkit-transform: translate3d(0, 0, 0);
-moz-transform: translate3d(0, 0, 0);
-ms-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
transform 和 fixed
在父元素使用transform
下使用fixed
,将会使fixed
失效。
解决方案是:
- 在使用了
transform
的容器中不使用fixed
,换absolute
,并需要通过 js 计算固定位置 - 依然使用
fixed
,不过使用fixed
的容器放在使用transform
的容器之外。
遮罩层滚动点透问题
在出现遮罩层的时候,我们可以使用@touchmove.prevent
来防止点透的问题出现,也就是遮罩层背后的内容还能滚动的问题。但是如果这样的话,如果遮罩层内有需要滚动的容器,那将使容器的滚动也失效。
通过自身的了解和网上的收集,主要解决方案有以下几种:
-
给
body
设置 css{overflow:hidden}
问题
IOS 上无效
-
给
body,html
都设置 css{overflow:hidden}
问题
a - 滚动位置会丢失,需要通过 js 设置 scrollTop
b - 没效果
-
目前比较完美的解决方案
(function() { var scrollTop = 0; // 显示弹出层 open.onclick = function() { // 在弹出层显示之前,记录当前的滚动位置 scrollTop = getScrollTop(); // 使body脱离文档流 document.body.classList.add("dialog-open"); // 把脱离文档流的body拉上去!否则页面会回到顶部! document.body.style.top = -scrollTop + "px"; mask.style.display = "block"; }; // 隐藏弹出层 close.onclick = function() { mask.style.display = "none"; // body又回到了文档流中(我胡汉三又回来啦!) document.body.classList.remove("dialog-open"); // 滚回到老地方 to(scrollTop); }; function to(scrollTop) { document.body.scrollTop = document.documentElement.scrollTop = scrollTop; } function getScrollTop() { return document.body.scrollTop || document.documentElement.scrollTop; } })(); function fixedBody(){ var scrollTop = document.body.scrollTop || document.documentElement.scrollTop; document.body.style.cssText += 'position:fixed;top:-'+scrollTop+'px;'; } function looseBody() { var body = document.body; body.style.position = ''; var top = body.style.top; document.body.scrollTop = document.documentElement.scrollTop = -parseInt(top); body.style.top = ''; }
不过这样依旧会有点问题,就是如果在遮罩层中滚动的时候,做点击操作,会容易出现卡死的现象,就是滚动后需要进行两次点击,再能进行对应的点击操作。对此的解决方案是,改click
事件为touchstart
事件。
使用webkit-overflow-scrolling:touch
的坑
今天遇到的了一个问题就是内容滚动有时候会出现卡住的情况,尤其是当滚动条位于最顶端或者最底下的时候,继续下拉或者上拉会导致页面卡住,这样的情况颇为频繁,找了很久都没发现问题所在,所以在尝试的情况下注释掉了webkit-overflow-scrolling
后,发现居然好了!!!居然好了!!!
然后就针对这个问题谷歌上找了下,发现其实也很很多:
取消
webkit-overflow-scrolling:touch
,但是这样会导致滚动时手指离开就马上停住滚动,没有了惯性滚动让子内容的高度
+1px
,比如父容器使用了webkit-overflow-scrolling:touch
,那么子内容的容器设置高度height:calc(100% + 1px)
既可以解决。
SPA路由使用history
模式下的微信分享bug
会出现的一个情况是,我已进入页面是可以分享的,当时如果这时候路由变化,比如replace
或者push
操作之后,那么分享就会出现问题,并不会如同配置那样分享出来。因为报了invaild signature
的错误。
这主要还区分安卓和苹果,两者的情况不同。
出现这个的原因是:
在history
模式下,vue-router
操作的是浏览器的历史记录,从而改变URL
的变化。
而官方JSSDK文档中也有提到:
同一个url仅需调用一次,对于变化url的SPA的web app可在每次url变化时进行调用,目前Android微信客户端不支持pushState的H5新特性,所以使用pushState来实现web app的页面会导致签名失败,此问题会在Android6.2中修复
但是坑爹的是,android
中出现的问题不多,因为每次URL
变化后,微信记录的都是当前那个URL
,而IOS
则不然,IOS
是微信只记录页面第一次加载进来的URL
地址。
举个栗子:
一进入系统的地址是https://a.com
,然后先后进入a
、b
,也就是https://a.com/a
和https://a.com/b
,然后停在b
页面,在android
中,微信记录的是https://a.com/b
这个地址,而IOS
中记录的却是https://a.com
,也就是第一次进来的路由地址。
这个时候我们刷新页面,那么https://a.com/b
就是IOS
第一次进来的地址了,所以IOS
微信中记录的地址也就变成了https://a.com/b
了。
解决方式:
注册一个全局变量,比如可以在window
下挂在一个wxSignURL
的变量,当一进入页面的时候保存第一次记录的地址:window.wxSignURL = location.href
。
这段代码可以写在main.js
里,也可以写在router.js
的钩子函数中,不过要区别对待,如果是android
,则使用每次跳转后的路由,如果是IOS
则使用记录的wxSignURL
。
然后获取微信签名
的时候,把这个URL
传过去即可。
IOS
和android
的判断使用浏览器的ua
判断即可。根据资料,微信会在window
下挂载一个__wxjs_is_wkwebview
属性,如果是在IOS
中,该值为true
,也可以通过这个属性去判断。