移动端
视口
视口主要针对移动设备
概念
(1) 布局视口(layout viewport) 为了能在移动设备上正常显示那些为pc端浏览器设计的网站,移动设备上的浏览器都会把自己默认的 viewport 设为 980px 或其他值,一般都比移动端浏览器可视区域大很多,所以就会出现浏览器出现横向滚动条的情况
(2) 视觉视口(visual viewport) 视觉视口表示用户当前看到的浏览器可视区域。用户可以通过缩放(pc或者移动都是通过双指拖动来缩放,而pc的滚轮缩放是增加/减少像素比来实现,会影响布局视口的宽度和高度)来查看网页的显示内容,从而改变视觉视口。视觉视口的定义,就像拿着一个放大镜分别从不同距离观察同一个物体,视觉视口仅仅类似于放大镜中显示的内容,因此视觉视口不会影响布局视口的宽度和高度。
(3) 理想视口(ideal viewport)
理想视口或者应该全称为“理想的布局视口”,在移动设备中就是指设备的分辨率。换句话说,理想视口或者说分辨率就是给定设备物理像素的情况下,最佳的“布局视口”。 理想视口的值其实就是屏幕分辨率的值
meta:content属性
width | 设置layout viewport 的宽度,为一个正整数,或字符串"width-device" |
---|---|
initial-scale | 设置页面的初始缩放值,为一个数字,可以带小数 |
minimum-scale | 允许用户的最小缩放值,为一个数字,可以带小数 |
maximum-scale | 允许用户的最大缩放值,为一个数字,可以带小数 |
height | 设置layout viewport 的高度,这个属性对我们并不重要,很少使用 |
user-scalable | 是否允许用户进行缩放,值为"no"或"yes", no 代表不允许,yes代表允许 |
width | 指定布局视口的宽度 (默认980px) ,可以设置为一个具体的数值(如 width=1024 )或 device-width ,表示与设备屏幕宽度一致 |
应用
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
关键属性解析:
-
width=device-width:将视口宽度设置为设备宽度
-
没有配置
-
配置了:那么给第一层容器width:100%,就是设备的宽度,给高度100vh就是设备的高度
-
-
initial-scale=1.0:初始缩放比例为1
-
user-scalable=no:禁用用户缩放
-
viewport-fit=cover:适配刘海屏
单位概念
设备尺寸
设备尺寸指的是设备对角线的长度,单位是英寸
像素px
-
物理像素/设备像素(device pixel, dp) : 由一个个物理像素点组成的,通过控制每个像素点的颜色和亮度,使屏幕显示出不同的图像,屏幕从工厂出来那天起,它上面的物理像素点就固定不变了。单位pt,pt 在 css 单位中属于真正的绝对单位。所谓的一倍屏、二倍屏(Retina)、三倍屏,指的是设备以多少物理像素来显示一个CSS像素,也就是说,多倍屏以更多更精细的物理像素点来显示一个CSS像素点,在普通屏幕下1个CSS像素对应1个物理像素,而在Retina屏幕下,1个CSS像素对应的却是4个物理像素。
-
CSS像素(css pixel, px) : CSS像素 =设备独立像素 = 逻辑像素
由于不同的物理设备的物理像素的大小是不一样的,所以
css
认为浏览器应该对css
中的像素进行调节,使得浏览器中1css像素的大小在不同物理设备上看上去大小总是差不多 ,目的是为了保证阅读体验一致。为了达到这一点浏览器可以直接按照设备的物理像素大小进行换算。
左边表示标清屏幕,右边表示视网膜高清屏幕
宽和高都是2个CSS像素,那么在标清屏中需要用2 * 2个物理像素来显示,即1个CSS像素用1 * 1个物理像素来描述
在高清屏需要4 * 4个物理像素来显示,即1个CSS像素用2 * 2个物理像素来描述
像素比(DPR)
设备像素比:window.devicePixelRatio
= 物理像素 / 独立像素
通过devicePixelRatio,我们可以知道该设备上一个css像素代表多少个物理像素。在普通屏,1个css像素对应1个物理像素;2倍屏中,一个css像素对应4个物理像素;三倍屏中则是9个。如iPhone6
的dpr
为2
,物理像素750
(x轴),则它的css像素为375
。
影响像素比(DPR)
- 配置设备缩放大小
- 用户滚轮缩放。例如,当用户把页面放大一倍,那么css中1px所代表的物理像素也会增加一倍;反之把页面缩小一倍,css中1px所代表的物理像素也会减少一倍。
分辨率
是指桌面设定的分辨率(分辨率是可以改变的),而不是显示器的分辨率 。分辨率指的是宽度上和高度上最多能显示的物理像素点个数。指的是屏幕的像素尺寸。750X1334
指的是横向有750
个像素,纵向有1334
个像素。
响应式
单位
rem
相对于根元素html的font-size值的大小,此单位若要用于屏幕自适应,可与vw配合使用设置根元素的字体大小。375px的网页的设计稿。此时,1vw=3.75px;4vw=15px;8vw=30px;
百分比%
- 子元素width或height的百分比是父元素width或height的百分比
- top、bottom的百分比是相对于(默认定位)父元素的height
- left、right的百分比是相对于(默认定位)父元素的width
- padding、margin不论是垂直方向或者是水平方向,都相对于直接父元素的width
- border-radius、translate、background-size的百分比,则是相对于自身的width
两个缺点:
(1)计算困难,如果我们要定义一个元素的宽度和高度,按照设计稿,必须换算成百分比单位。 (2)各个属性中如果使用百分比,相对父元素的属性并不是唯一的。比如width和height相对于父元素的width和height,而margin、padding不管垂直还是水平方向都相对比父元素的宽度、border-radius则是相对于元素自身等等,造成我们使用百分比单位容易使布局问题变得复杂。
自适应
-
查看设计图,确定页面布局,组件的复用等
-
尽可能的添加多的div来包含元素,并设置对应的classname
-
外层盒子使用flex进行布局,不设置绝对宽高px(使用rem或者%布局),高度由里面的内容撑开(撑不开就用margin、padding)
box-sizing:border-box以及margin:auto
-
Tips
- float的好处是,如果宽度太小,放不下两个元素,后面的元素会自动滚动到前面元素的下方,不会在水平方向overflow(溢出),避免了水平滚动条的出现。
- 图片的自适应,
img { width: auto; max-width: 100%; }
yd ui移动端
自适应不是指你缩放页面大小(ctrl+鼠标滚动条),是指你浏览器大小变化自适应。一般不加自适应,缩放页面,会发现
$(document).width()
会根据缩放变大变小,但是元素宽高不会变,相应元素就会感觉变大变小。加入自适应(自适应会根据页面大小来改变px,元素宽高会变),缩放页面,相应元素感觉不会改变。
缩放页面大小有三种情况:
1. 改变浏览器宽高
2. ctrl+鼠标滚动条,改变页面大小
3. window中显示设置中的缩放与布局
像素比就是第二种和第三种情况
(function (doc, win) {
const docEl = doc.documentElement;
// 获取当前显示设备的物理像素分辨率与CSS像素分辨率之比;
var dpr = window.devicePixelRatio || 1;
//orientationchange:在用户水平或者垂直翻转设备(即方向发生变化)时触发的事件
const resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize';
const recalc = function () {
var clientWidth = docEl.clientWidth;
if (!clientWidth) return;
if (clientWidth >= 750) {
docEl.style.fontSize = '100px';
} else {
//html宽度=clientWidth
//设计图的宽度=750(通过设计图自行配置)
//在750的设计图下,我们为了便于开发将根元素的字体大小设置成100px
docEl.style.fontSize = 100 * (clientWidth / 750) + 'px';
}
};
// 检测是否支持0.5px
if (dpr >= 2) {
var fakeBody = document.createElement("body");
var testElement = document.createElement("div");
testElement.style.border = ".5px solid transparent";
fakeBody.appendChild(testElement);
docEl.appendChild(fakeBody);
if (testElement.offsetHeight === 1) {
docEl.classList.add("hairlines");
}
docEl.removeChild(fakeBody);
}
win.addEventListener("pageshow", function(e) {
if (e.persisted) { // 浏览器后退的时候重新计算
clearTimeout(tid);
tid = setTimeout(refreshRem, 300);
}
}, false)
if (!doc.addEventListener) return;
win.addEventListener(resizeEvt, recalc, false);
doc.addEventListener('DOMContentLoaded', recalc, false);//判断DOM是否加载完毕
})(document, window);
flexible.js
// 首先是一个立即执行函数,执行时传入的参数是window和document
(function flexible(window, document) {
// 返回文档的root元素
var docEl = document.documentElement;
// 获取设备的dpr,即当前设置下物理像素与虚拟像素的比值
var dpr = window.devicePixelRatio || 1;
// 设置默认字体大小,默认的字体大小继承自body
function setBodyFontSize() {
if (document.body) {
// 调整body标签的fontSize,fontSize = (12 * dpr) + 'px'
document.body.style.fontSize = 12 * dpr + 'px';
} else {
document.addEventListener('DOMContentLoaded', setBodyFontSize);
}
}
setBodyFontSize();
// set 1rem = viewWidth / 24
function setRemUnit() {
// 设置root元素的fontSize = 其clientWidth / 24 + 'px'
var rem = docEl.clientWidth / 24;
docEl.style.fontSize = rem + 'px';
}
setRemUnit();
// 当页面展示或重新设置大小的时候,触发重新
window.addEventListener('resize', setRemUnit);
window.addEventListener('pageshow', function(e) {
if (e.persisted) {
setRemUnit();
}
});
// 检测0.5px的支持,支持则root元素的class中有hairlines
if (dpr >= 2) {
var fakeBody = document.createElement('body');
var testElement = document.createElement('div');
testElement.style.border = '.5px solid transparent';
fakeBody.appendChild(testElement);
docEl.appendChild(fakeBody);
if (testElement.offsetHeight === 1) {
docEl.classList.add('hairlines');
}
docEl.removeChild(fakeBody);
}
})(window, document);
常见处理
-
清除默认样式 Normalize.css
-
浏览器前缀,webpack:autoprefixer
内核 主要代表的浏览器 前缀 Trident IE浏览器 -ms Gecko Firefox -moz Presto Opera -o Webkit Chrome和Safari -webkit -
h5中如何处理移动端滑动卡顿的问题
body { // height: 100%; -webkit-overflow-scrolling: touch; overflow-scrolling: touch; overflow-y: scroll; }
-
遮罩层隐藏之后,底部div无法触发点击事件:给遮罩层pointer-events
pointer-events: none; 阻止用户的点击动作产生任何效果 阻止缺省鼠标指针的显示 阻止CSS里的hover和active状态的变化触发事件 阻止JavaScript点击动作触发的事件
CSS媒体查询处理不同尺寸
使用媒体查询针对不同屏幕尺寸定制样式。
/* iPhone SE */
@media screen and (max-width: 374px) {
.container {
font-size: 14px;
}
}
/* iPhone 6/7/8/X */
@media screen and (min-width: 375px) and (max-width: 413px) {
.container {
font-size: 16px;
}
}
/* iPhone 6/7/8 Plus */
@media screen and (min-width: 414px) {
.container {
font-size: 18px;
}
}
刘海
适配iPhone X等带有刘海的机型。
/* 适配刘海屏 */
.safe-area-inset {
padding-top: constant(safe-area-inset-top);
padding-top: env(safe-area-inset-top);
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
/* 底部固定导航适配 */
.fixed-bottom {
position: fixed;
bottom: 0;
bottom: constant(safe-area-inset-bottom);
bottom: env(safe-area-inset-bottom);
}
图片适配方案
针对不同分辨率设备的图片适配策略。
<!-- 使用srcset适配不同分辨率 -->
<img srcset="image-320w.jpg 320w,
image-480w.jpg 480w,
image-800w.jpg 800w"
sizes="(max-width: 320px) 280px,
(max-width: 480px) 440px,
800px"
src="image-800w.jpg" alt="Responsive image">
配合CSS的处理:
.responsive-image {
max-width: 100%;
height: auto;
display: block;
}
横屏适配处理
处理横屏模式下的布局适配。
/* 检测横屏 */
@media screen and (orientation: landscape) {
.landscape-container {
display: flex;
flex-direction: row;
}
}
/* 检测竖屏 */
@media screen and (orientation: portrait) {
.portrait-container {
display: flex;
flex-direction: column;
}
}
JavaScript监听屏幕旋转:
window.addEventListener('orientationchange', function() {
if (window.orientation === 180 || window.orientation === 0) {
// 竖屏
console.log('竖屏');
}
if (window.orientation === 90 || window.orientation === -90) {
// 横屏
console.log('横屏');
}
});
问题
图片模糊问题
我们平时使用的图片大多数都属于位图(png、jpg..
),位图由一个个像素点构成的,每个像素都具有特定的位置和颜色值:
理论上,位图的每个像素对应在屏幕上使用一个物理像素来渲染,才能达到最佳的显示效果。
而在dpr > 1
的屏幕上,位图的一个像素可能由多个物理像素来渲染,然而这些物理像素点并不能被准确的分配上对应位图像素的颜色,只能取近似值,所以相同的图片在dpr > 1
的屏幕上就会模糊:
1px
原因
出现的原因:
按照iPhone6的尺寸,一张分辨率750像素宽的设计图,设计图时量到的1px其实是1设备像素,而当我们设置布局视口等于理想视口等于375px,并且由于iPhone6的DPR为2,写css时的1px对应的物理像素就是2pt,所以看起来会粗一点。
解决
-
伪类+transform实现
基于
media
查询判断不同的设备像素比对线条进行缩放:.border-1px { position: relative; &::after { content: ''; position: absolute; left: 0; bottom: 0; width: 100%; height: 1px; background-color: #000; transform: scaleY(0.5); transform-origin: bottom; } } // 2x屏 @media (-webkit-min-device-pixel-ratio: 2) { .border-1px::after { transform: scaleY(0.5); } } // 3x屏 @media (-webkit-min-device-pixel-ratio: 3) { .border-1px::after { transform: scaleY(0.33); } }
这种方式可以满足各种场景,如果需要满足圆角,只需要给伪类也加上
border-radius
即可。 -
媒体查询
.border { border: 1px solid #999 } @media screen and (-webkit-min-device-pixel-ratio: 2) { .border { border: 0.5px solid #999 } } @media screen and (-webkit-min-device-pixel-ratio: 3) { .border { border: 0.333333px solid #999 } }
-
利用viewport + js + 使用rem实现
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
- name="viewport" content="width=device-width: 本页面的 「viewport」 的宽度为设备宽度。
- initial-scale=1.0: 初始缩放值为 1,
- maximum-scale=1.0: 最大的缩放值为 1。
- user-scalable=no: 禁止用户进行页面缩放。
那么通过设置viewport的initial-scale,就可以轻松实现:
- 当dpr = 1 时,initial-scale = 1
- 当dpr = 2 时,initial-scale = 0.5
- 当dpr = 3 时,initial-scale = 0.33333333333
<html> <head> <title></title> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no"> <style></style> <script> let 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'); } 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.3333333333333333, minimum-scale=0.3333333333333333, user-scalable=no'); } let docEl = document.documentElement; let fontsize = 32* (docEl.clientWidth / 750) + 'px'; docEl.style.fontSize = fontsize; </script> </head> </html>