个人已经开通微信公众号,需要的可以关注一波
ios
滑动不流畅上下滑动页面会产生卡顿,手指离开页面,页面立即停止运动。整体表现就是滑动不流畅,没有滑动惯性。
原来在 iOS 5.0
以及之后的版本,滑动有定义有两个值 auto
和 touch
,默认值为 auto
。
-webkit-overflow-scrolling: touch; /* 当手指从触摸屏上移开,会保持一段时间的滚动 */
-webkit-overflow-scrolling: auto; /* 当手指从触摸屏上移开,滚动会立即停止 */
.wrapper {
-webkit-overflow-scrolling: touch;
}
设置外部 overflow
为 hidden
,设置内容元素 overflow
为 auto
。内部元素超出 body 即产生滚动,超出的部分 body 隐藏。
body {
overflow-y: hidden;
}
.wrapper {
overflow-y: auto;
}
双击或者双指张开手指页面元素,页面会放大或缩小。
移动端常规写法
<meta name="viewport" content="width=device-width, initial-scale=1.0">
因此我们可以设置 maximum-scale
、minimum-scale
与 user-scalable=no
用来避免这个问题
<meta name=viewport
content="width=device-width, initial-scale=1.0, minimum-scale=1.0 maximum-scale=1.0, user-scalable=no">
监听元素 click
事件,点击元素触发时间延迟约 300ms
。
点击蒙层,蒙层消失后,下层元素点击触发。
iOS
中的 safari,为了实现双击缩放操作,在单击 300ms
之后,如果未进行第二次点击,则执行 click
单击操作。也就是说来判断用户行为是否为双击产生的。但是,在 App
中,无论是否需要双击缩放这种行为,click
单击都会产生 300ms
延迟。
touchstart
替换 clickel.addEventListener("touchstart", () => { console.log("ok"); }, false);
在 ```vue`` 中使用
fastclick
库import FastClick from 'fastclick';
FastClick.attach(document.body, options);
iPhone X
系列安全区域适配问题头部刘海两侧区域或者底部区域,出现刘海遮挡文字,或者呈现黑底或白底空白区域。
viewport-fit
为 cover
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes, 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``文件中写入下面这些代码,在手机上,也能想浏览器控制台一样进行查看。
<script src="//cdn.jsdelivr.net/npm/eruda"></script>
<script>
eruda.init();
</script>
H5
页面播放视频在H5
页面播放视频,不全屏播放,在video标签添加playsinline
和x5-playsinline
设置为true就行
<video id="my-video" class="video-js" controls preload="auto"
data-setup=" {} " playsinline="true" x5-playsinline="true"> video>
很多视口我们要对横屏和竖屏显示不同的布局,所以我们需要检测在不同的场景下给定不同的样式:
window.addEventListener('resize',() =>{
if(window.orientation === 180 || window.orientation === 0) {
//正常方向或屏幕旋转180度
console.log('竖屏')
}
if(window.orientation === 80 || window.orientation === -90) {
//屏幕顺时钟旋转90度或屏幕逆时针旋转90度
console.log('横屏')
}
})
@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
基准值,这种方式就可以像以前一样轻松愉快的写1px了。
在devicePixelRatio=2
时,设置meta
:
<meta name="viewport" content="width=device-width,initial-scale=0.5, maximum-scale=0.5, minimum-scale=0.5, user-scalable=no">
实现
<html lang="en">
<head>
<title>移动端1px问题title>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
<meta name="viewport" id="WebViewport"
content="width=device-width,initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" />
<style>
html {
font-size: 11px;
}
body {
padding: 1rem;
}
* {
padding: 0;
margin: 0;
}
.item {
padding: 1rem;
border-bottom: 1px solid gray;
font-size: 1.2rem;
}
style>
<script>
var viewport = document.querySelector("meta[name=viewport]");
var dpr = window.devicePixelRatio || 1;
var scale = 1 / dpr;
//下面是根据设备dpr设置viewport
viewport.setAttribute(
"content", +
"width=device-width," +
"initial-scale=" +
scale +
", maximum-scale=" +
scale +
", minimum-scale=" +
scale +
", user-scalable=no"
);
var docEl = document.documentElement;
var fontsize = 10 * (docEl.clientWidth / 320) + "px";
docEl.style.fontSize = fontsize;
script>
head>
<body>
<div class="item">border-bottom: 1px solid gray;div>
<div class="item">border-bottom: 1px solid gray;div>
body>
html>
rem适配
rem
适配的本质是布局等比例的缩放,通过动态设置html
的font-size
来改变rem
的大小。
<meta name="viewport" content="width=device-width; initial-scale=1; maximum-scale=1; minimum-scale=1; user-scalable=no;">
引入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')
]
}
#page>div.logo+ul#navigation>li*5>a{Item $}
可以转换为
<div id="page">
<div class="logo">div>
<ul id="navigation">
<li><a href="">Item 1a>li>
<li><a href="">Item 2a>li>
<li><a href="">Item 3a>li>
<li><a href="">Item 4a>li>
<li><a href="">Item 5a>li>
ul>
div>
使用>
运算符将元素相互嵌套
div>ul>li 生成
<div>
<ul>
<li>li>
ul>
div>
使用+
运算符将元素彼此放置在同一水平上:
div+p+bq 生成
<div>div>
<p>p>
<blockquote>blockquote>
使用括号将复杂缩写的子树分组
div>(header>ul>li*2>a)+footer>p 生成
<div>
<header>
<ul>
<li><a href="">a>li>
<li><a href="">a>li>
ul>
header>
<footer>
<p>p>
footer>
div>