移动的端常见问题
ios
滑动不流畅
上下滑动页面会产生卡顿,手指离开页面,页面立即停止运动。整体表现就是滑动不流畅,没有滑动惯性。
-
原来在
iOS 5.0
以及之后的版本,滑动有定义有两个值auto
和touch
,默认值为auto
。-webkit-overflow-scrolling: touch; /* 当手指从触摸屏上移开,会保持一段时间的滚动 */ -webkit-overflow-scrolling: auto; /* 当手指从触摸屏上移开,滚动会立即停止 */
-
在滚动容器上增加滚动 touch 方法
.wrapper { -webkit-overflow-scrolling: touch; }
-
设置 overflow
设置外部
overflow
为hidden
,设置内容元素overflow
为auto
。内部元素超出 body 即产生滚动,超出的部分 body 隐藏。body { overflow-y: hidden; } .wrapper { overflow-y: auto; }
页面放大或缩小不确定性行为
双击或者双指张开手指页面元素,页面会放大或缩小。
-
移动端常规写法
-
因此我们可以设置
maximum-scale
、minimum-scale
与user-scalable=no
用来避免这个问题
click 点击事件延时与穿透
监听元素
click
事件,点击元素触发时间延迟约300ms
。点击蒙层,蒙层消失后,下层元素点击触发。
iOS
中的 safari,为了实现双击缩放操作,在单击300ms
之后,如果未进行第二次点击,则执行click
单击操作。也就是说来判断用户行为是否为双击产生的。但是,在App
中,无论是否需要双击缩放这种行为,click
单击都会产生300ms
延迟。-
使用
touchstart
替换 click
el.addEventListener("touchstart", () => { console.log("ok"); }, false);
-
在 ```vue`` 中使用
-
使用
fastclick
库import FastClick from 'fastclick'; FastClick.attach(document.body, options);
iPhone X
系列安全区域适配问题
头部刘海两侧区域或者底部区域,出现刘海遮挡文字,或者呈现黑底或白底空白区域。
-
设置
viewport-fit
为cover
- 使用
safe area inset
变量
/* 适配 iPhone X 顶部填充*/ @supports (top: env(safe-area-inset-top)){ body, .header{ padding-top: constant(safe-area-inset-top, 40px); padding-top: env(safe-area-inset-top, 40px); padding-top: var(safe-area-inset-top, 40px); } } /* 判断iPhoneX 将 footer 的 padding-bottom 填充到最底部 */ @supports (bottom: env(safe-area-inset-bottom)){ body, .footer{ padding-bottom: constant(safe-area-inset-bottom, 20px); padding-bottom: env(safe-area-inset-bottom, 20px); padding-top: var(safe-area-inset-bottom, 20px); } }
-
safe-area-inset-left
:安全区域距离左边边界距离 -
safe-area-inset-right
:安全区域距离右边边界距离 -
safe-area-inset-top
:安全区域距离顶部边界距离 -
safe-area-inset-bottom
:安全区域距离底部边界距离
safe-area-inset-top
,
safe-area-inset-right,
safe-area-inset-bottom,
safe-area-inset-leftsafe-area-inset-*
由四个定义了视口边缘内矩形的
top,
right,
bottom和
left的环境变量组成,这样可以安全地放入内容,而不会有被非矩形的显示切断的风险。对于矩形视口,例如普通的笔记本电脑显示器,其值等于零。对于非矩形显示器(如圆形表盘,
iPhoneX` 屏幕),在用户代理设置的四个值形成的矩形内,所有内容均可见。 - 使用
H5
调试相关方案策略
-
vconsole
控制台插件import Vconsole from 'vconsole' new Vconsole()
eruda
手机端调试只要在我们的```html``文件中写入下面这些代码,在手机上,也能想浏览器控制台一样进行查看。
H5
页面播放视频
-
在
H5
页面播放视频,不全屏播放,在video标签添加playsinline
和x5-playsinline
设置为true就行
横屏适配
-
很多视口我们要对横屏和竖屏显示不同的布局,所以我们需要检测在不同的场景下给定不同的样式:
- JavaScript检测横屏
window.addEventListener('resize',() =>{ if(window.orientation === 180 || window.orientation === 0) { //正常方向或屏幕旋转180度 console.log('竖屏') } if(window.orientation === 80 || window.orientation === -90) { //屏幕顺时钟旋转90度或屏幕逆时针旋转90度 console.log('横屏') } })
CSS检测横屏
@media screen and (orientation:portrait){ /*竖屏...*/ } @media screen and (orientation:landscape){ /*横屏...*/ }
移动端解决0.5px
方案
设备像素比:dpr=window.devicePixelRatio,也就是设备的物理像素与逻辑像素的比值
-
0.5px 方案
/*这是css方式*/ .border { border: 1px solid #999 } @media screen and (-webkit-min-device-pixel-ratio: 2) { .border { border: 0.5px solid #999 } } /*ios dpr=2和dpr=3情况下border相差无几,下面代码可以省略*/ @media screen and (-webkit-min-device-pixel-ratio: 3) { .border { border: 0.333333px solid #999 } }
-
viewport + rem
-
同时通过设置对应
viewport
的rem
基准值,这种方式就可以像以前一样轻松愉快的写1px了。
在devicePixelRatio=2
时,设置meta
:实现
移动端1px问题 border-bottom: 1px solid gray;border-bottom: 1px solid gray;
移动端适配方案
-
rem适配
rem
适配的本质是布局等比例的缩放,通过动态设置html
的font-size
来改变rem
的大小。 -
引入flexible
-
flexible
方案是阿里早期开源的一个移动端适配解决方案,引用
flexible后,我们在页面上统一使用
rem`来布局。(function(win, lib) { var doc = win.document; var docEl = doc.documentElement; var metaEl = doc.querySelector('meta[name="viewport"]'); var flexibleEl = doc.querySelector('meta[name="flexible"]'); var dpr = 0; var scale = 0; var tid; var flexible = lib.flexible || (lib.flexible = {}); if (metaEl) { console.warn('将根据已有的meta标签来设置缩放比例'); var match = metaEl.getAttribute('content').match(/initial\-scale=([\d\.]+)/); if (match) { scale = parseFloat(match[1]); dpr = parseInt(1 / scale); } } else if (flexibleEl) { var content = flexibleEl.getAttribute('content'); if (content) { var initialDpr = content.match(/initial\-dpr=([\d\.]+)/); var maximumDpr = content.match(/maximum\-dpr=([\d\.]+)/); if (initialDpr) { dpr = parseFloat(initialDpr[1]); scale = parseFloat((1 / dpr).toFixed(2)); } if (maximumDpr) { dpr = parseFloat(maximumDpr[1]); scale = parseFloat((1 / dpr).toFixed(2)); } } } if (!dpr && !scale) { var isAndroid = win.navigator.appVersion.match(/android/gi); var isIPhone = win.navigator.appVersion.match(/iphone/gi); var devicePixelRatio = win.devicePixelRatio; if (isIPhone) { // iOS下,对于2和3的屏,用2倍的方案,其余的用1倍方案 if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) { dpr = 3; } else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)){ dpr = 2; } else { dpr = 1; } } else { // 其他设备下,仍旧使用1倍的方案 dpr = 1; } scale = 1 / dpr; } docEl.setAttribute('data-dpr', dpr); if (!metaEl) { metaEl = doc.createElement('meta'); metaEl.setAttribute('name', 'viewport'); metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no'); if (docEl.firstElementChild) { docEl.firstElementChild.appendChild(metaEl); } else { var wrap = doc.createElement('div'); wrap.appendChild(metaEl); doc.write(wrap.innerHTML); } } function refreshRem(){ var width = docEl.getBoundingClientRect().width; if (width / dpr > 540) { width = 540 * dpr; } var rem = width / 10; docEl.style.fontSize = rem + 'px'; flexible.rem = win.rem = rem; } win.addEventListener('resize', function() { clearTimeout(tid); tid = setTimeout(refreshRem, 300); }, false); win.addEventListener('pageshow', function(e) { if (e.persisted) { clearTimeout(tid); tid = setTimeout(refreshRem, 300); } }, false); if (doc.readyState === 'complete') { doc.body.style.fontSize = 12 * dpr + 'px'; } else { doc.addEventListener('DOMContentLoaded', function(e) { doc.body.style.fontSize = 12 * dpr + 'px'; }, false); } refreshRem(); flexible.dpr = win.dpr = dpr; flexible.refreshRem = refreshRem; flexible.rem2px = function(d) { var val = parseFloat(d) * this.rem; if (typeof d === 'string' && d.match(/rem$/)) { val += 'px'; } return val; } flexible.px2rem = function(d) { var val = parseFloat(d) / this.rem; if (typeof d === 'string' && d.match(/px$/)) { val += 'rem'; } return val; } })(window, window['lib'] || (window['lib'] = {}));
使用vw,vh布局
vh、vw
方案即将视觉视口宽度window.innerWidth
和视觉视口高度window.innerHeight
等分为 100 份。如果视觉视口为
375px
,那么1vw=3.75px
,这时UI
给定一个元素的宽为75px
(设备独立像素),我们只需要将它设置为75/3.75=20vw
。-
这里的比例关系我们也不用自己换算,我们可以使用
PostCSS
的postcss-px-to-viewport
插件帮我们完成这个过程。写代码时,我们只需要根据UI
给的设计图写px
单位即可。module.exports = { plugins: [ require('autoprefixer'), require('postcss-import'), require('postcss-url'), require('postcss-preset-env'), require('postcss-aspect-ratio-mini'), require('postcss-write-svg'), require('postcss-px-to-viewport')({ viewportWidth: 750, viewportHeight: 1334, unitPrecision: 3, viewportUnit: 'vw', selectorBlackList: [ '.ignore', '.hairlines', '.footer' ], minPixelValue: 1, mediaQuery: true }), require('cssnano') ] }
-
快速生成HTML代码结构
#page>div.logo+ul#navigation>li*5>a{Item $}
可以转换为
-
使用
>
运算符将元素相互嵌套div>ul>li 生成
-
使用
+
运算符将元素彼此放置在同一水平上:div+p+bq 生成
-
使用括号将复杂缩写的子树分组
div>(header>ul>li*2>a)+footer>p 生成