开发场景
在开发关于App的内嵌网页时,UI的设计图往往是按照 iphone6/7/8 或者 iphone6/7/8 Plus 来进行定稿设计,但是市面上的手机种类有很多,安卓的、IOS的、全面屏的、刘海屏的等,面对不同设备的适配,我们需要对代码进行调整,以满足对不同设备的显示。
基本概念
物理像素
英寸
1英寸 = 2.54cm
像素
即具有特定颜色和位置的小方块
图片、电子屏幕就是由无数个像素组成
分辨率
分辨率被细分为以下两种
屏幕分辨率
指一个屏幕具体由多少个像素点组成, 比如 iPhone SE 分辨率为 1136 x 640,垂直方向有 1136 个像素点, 水平方向有 640 个像素点
图片分辨率
图片含有的像素数, 原理同上
PPI
概念:每英寸包含的像素数。所以PPI越高,屏幕越清晰
比如下面的图片展示 iPhone XS Max 的信息
计算PPI:
sqrt(2688^2, 1242^2) / 6.5 = 455.54 (ppi)
Retina Display(视网膜屏幕)
解决在相同物理大小的手机,不同分辨率的情况下,所显示的内容大小相同,清晰度不同
把 2x2 个像素当 1 个像素使用
设备独立像素(一般为UX设计稿)
统一不同分辨率手机的单位,告诉其界面显示元素的大小,这个单位就是设备独立像素
在Chrome中显示的 iPhone X:375x812 就是指设备独立像素
设备像素比(dpr)
即 物理像素/设备独立像素
苹果提出Retina Display(视网膜屏幕)后,才有"dpr"的概念,在这之前移动设备是直接使用物理像素来进行展示
视口
布局视口
此时网页宽度就是布局视口
视觉视口
此时通过屏幕看到的区域就是视觉视口(智能手机出现的早期状况)
理想视口
用户不需要进行左右移动或放大缩小就可以浏览网页内容(移动端发展到现在)
px、dp、pt
px (pixel)
- px 是屏幕上用来显示内容的最基本的点
- px 是一个绝对单位( 纠正底部链接中文章存在的错误:developer.mozilla.org/zh-CN/docs/… )
- 屏幕横向、纵向分布的px数量,叫做“分辨率”, 比如 1920 * 1080 的含义: 纵向分布 1920 个px, 纵向分布 1080 个px
pt(point)
ios中最小开发长度单位, 在163ppi时, 1pt = 1px
总结: 1pt = ppi / 163 (px)
解决方案
解决的方案大致细分为如下几种
px转vw
UI给的设计稿一般来说都是px作为单位,所以我们设计的元素也是跟着px走的,但是不同手机的设备独立像素是不同的,比如下图的iPhone6/7/8在Chrome显示的设备独立像素是 375x667
而iPhone12 Pro 在Chrome显示的设备独立像素是 375x844
这样一来的话,同样是 187.5px(375px / 2 == 187.5px) 的页面元素,在iPhone6/7/8中占屏幕宽度的50%,而在iPhone6/7/8 Plus中的高度则只占屏幕的45% (187.5px / 414px) 左右,这样就不符合UI设计中该元素占屏幕宽度一半的想法,更何况还有像 iPad Mini 768x1024等诸多不同设备独立像素的手机或平板
在iPhone6/7/8中:
在iPhone6/7/8 Plus中:
在iPad Mini中:
所以,我们需要将设计稿中的px转为vw,因为vw指的是某一页面元素在宽度上占屏幕的百分之几,与之对应的还有vh,指的是某一页面元素在高度上占屏幕的百分之几;这样的修改就可以使得同一个元素在不同屏幕中按照自身在标准设计稿(iPhone6/7/8)中的占比进行缩放
这里我们采用插件postcss-px-to-viewport,可自动将页面中的px转为vw
改动效果如下:
使用vh重写元素高度
虽然使用了postcss-px-to-viewport将页面中所有的px转为vw或者vh,但是该依赖包不能一部分转为vx,一部分转为vh
所以面对部分应用需求时,比如在之前的基础上,希望表单组件的布局是跟着高度的比例进行变化,那么此时就不能将px转为vw,而是要转为vh进行布局计算
那就需要按照UX设计图将表单中元素改为vh来适配屏幕在垂直方向上的比例
Vscode可安装插件
px2xx, 该插件可将px转为vw、vh、rem, 这样就不用自己手动计算px转vh的过程, 提高效率
刘海屏的安全区域
随着iPhone手机的刘海屏出现,也伴随着顶部刘海屏的适配问题,为了不让刘海屏遮挡住原来的页面布局,我们只能将页面按照设备给出的刘海屏高度进行压缩
分为两步:
- 设置
viewport-fit为cover,这样网页内容可以完全覆盖可视窗口(这一步是必须的!)
<meta name="viewport" content="viewport-fit=cover">
- CSS中同时设置
env函数(iOS < 11.2)和constant函数(iOS >= 11.2),并填入安全区域常量
body {
// 刘海安全距离
padding-top: env(safe-area-inset-top);
padding-top: constant(safe-area-inset-top);
// 底部栏安全距离
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
媒体查询@media
@media screen and (max-width: 450px) {
/* 小屏对应样式 */
}
@media screen and (min-width: 450px) {
/* 更大屏幕的对应样式 */
}
组件开发适配
在不同屏幕下,组件本身的样式(宽度、高度、间距)也会受到手机变化的影响,所以我们需要按照UX设计稿进行等比变换
例如UX给的标准设计稿为iPhone6/7/8 Plus(414 x 736)的情况下,某组件的外部宽度为90px, 所以在iPhone SE的机型中,应该在组件内部(比如Vue2的mounted函数)中转换为(90/414)*375 px,也就是约等于81.52px
图片的伸缩配置
一般来说,UX会根据不同屏幕大小,制作多张图片,来适配不同的机型。
但是假如UX没有针对不同机型给予我们不同的图片,那就需要我们对img标签进行设置,这里就要提到object-fit属性,设置不同的属性值
- contain:保持原有尺寸比例。内容被缩放
- cover:保持原有尺寸比例。但部分内容可能被剪切,显示核心部分
- fill: 默认,不保证保持原有的比例,内容拉伸填充整个内容容器
- scale-down: 保持原有尺寸比例。内容的尺寸与 none 或 contain 中的一个相同,取决于它们两个之间谁得到的对象尺寸会更小一些
- none:保留原有元素内容的长度和宽度,也就是说内容不会被重置
扩展优化
less变量与函数
less变量
使用less变量本质上无法对是配做出影响,但是我们可以将需要复用的高度、宽度、间距等存储在变量中,以便后期对UX进行修改时,不必再次手动计算,而是调整变量统一修改
calc函数
使用calc函数,可以自由的计算vw、vh、px等单位, 也可以达到减少计算的目的, 利于后期的修改和维护
结合Less中Maps映射
结合Maps映射,可以将每一个div整体进行描述。这样可以提高可读性,同时利于后期维护.
这样设计的好处:
- 提高代码可读性, 将每个元素对象化, 用属性描述各个部分
- 提高程序维护性, 如果后期UX对某一个关联样式宽高进行修改,不采用这样的方式是很难在几百行、几千行的CSS代码中去寻找px的关联加减, 从而导致漏改或错改
<template>
<div>
<div class="container">
<div class="header">头部栏</div>
<div class="content">
<div class="item">内容条一</div>
<div class="item">内容条二</div>
<div class="item">内容条三</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "divExpMaps",
};
</script>
<style lang="less" scoped>
@outerWidthVh: 100vh;
@outerHeightVh: 50vh;
@headerHeightVh: 5vh;
@contentItemMarginPX: 15px;
#divMaps() {
outerWidth: @outerWidthVh;
outerHeight: @outerHeightVh;
headerHeight: @headerHeightVh;
contentHeight: calc(@outerHeightVh - @headerHeightVh);
contentItemHeight: calc((@outerHeightVh - @headerHeightVh) / 3 - @contentItemMarginPX);
}
.container {
width: #divMaps[outerWidth];
height: #divMaps[outerHeight];
background: gray;
.header {
height: #divMaps[headerHeight];
background: yellowgreen;
}
.content {
height: #divMaps[contentHeight];
display: flex;
flex-direction: column;
justify-content: space-between;
.item {
width: 100%;
height: #divMaps[contentItemHeight];
}
.item:nth-child(1) {
background: violet;
}
.item:nth-child(2) {
background: orange;
}
.item:nth-child(3) {
background: orangered;
}
}
}
</style>
横屏与竖屏的不同显示
除了适配不同的屏幕以外,有时还需要适配用户翻转手机所带来的布局变换
在 JavaScript 中监听手机翻转
window.addEventListener("resize", () => {
if (window.orientation === 180 || window.orientation === 0) {
// 正常方向或屏幕旋转180度
console.log("竖屏");
}
if (window.orientation === 90 || window.orientation === -90) {
// 屏幕顺时钟旋转90度或屏幕逆时针旋转90度
console.log("横屏");
}
});
在 Less 中设置不同翻转样式
.box {
width: 100vw;
height: 100vh;
@media screen and (orientation: portrait) {
/*竖屏...*/
background: pink;
}
@media screen and (orientation: landscape) {
/*横屏...*/
background: orangered;
}
}
图片模糊问题
通过设置image-set
.avatar {
background-image: -webkit-image-set( "conardLi_1x.png" 1x, "conardLi_2x.png" 2x );
}
media查询
通过媒体查询"-webkit-min-device-pixel-ratio: 2 / 3"分情况设置不同的图片background-image: url(xxxx);
设置secret
<img src="xxx1.png" srcset="xxx2.png 2x, xxx3.png 3x">
使用SVG图片
相比于位图(png、jpg),svg图片描述图片形状,体积小,不失真
总结
参考: