1 前置知识
1.1 屏幕尺寸
屏幕尺寸指的是以屏幕对角线长度来计算的,单位是英寸,1英寸 = 2.54厘米。如:
iphone6 Plus
屏幕尺寸5.5英寸(2.7*4.8)
1.2 像素pixel
从计算机技术的角度来解释,像素是硬件和软件所能控制的最小单位.
它指显示屏的画面上表示出来的最小单位,不是图画上的最小单位。一幅图像通常包含成千上万个像素,每个像素都有自己的颜色信息,它们紧密地组合在一起。
一个像素,就是一个点,或者说是一个很小的正方形。
像素单位有设备像素、逻辑像素、CSS
像素 3 种。
1.3 屏幕分辨率
屏幕分辨率指一个屏幕具体由多少个像素点组成,单位是
px
。
1.4 物理像素(设备像素)
设备像素(
device pixels
)也叫物理像素,指的是显示器上的真实像素,每个像素的大小是屏幕固有的属性,屏幕出厂以后就不会再改变。
1.5 逻辑像素(设备独立像素)
设备独立像素(
device independent pixels
)是操作系统定义的一种像素单位,应用程序将设备独立像素告诉操作系统,操作系统再将设备独立像素转化为设备像素,从而控制屏幕上真正的物理像素点。
1.6 每英寸像素点ppi
ppi(pixel per inch) 表示每英寸所包含的像素点数目,数值越高,说明屏幕能以更高密度显示图像。
它的计算公式为:PPI=√(X^2+Y^2)/ Z
(X:长度像素数;Y:宽度像素数;Z:屏幕大小)
ppi
在120-160之间的手机被归为低密度手机,160-240被归为中密度,240-320被归为高密度,320以上被归为超高密度
如:iPhone
6p为401ppi
,iphone6
为326ppi
1.7 设备dpr
dpr
(device pixel ratio) 表示设备像素比,设备像素/设备独立像素,代表设备独立像素到设备像素的转换关系,在JS中可以通过window.devicePixelRatio
获取
计算公式为:DPR
= 物理像素/逻辑像素
css像素
在
CSS
中使用的px
都是指css
像素,比如width: 128px
。css
像素的大小是很容易变化的,当我们缩放页面的时候,元素的css
像素数量不会改变,改变的只是每个css
像素的大小。
1.8 概念关系图
屏幕尺寸、屏幕分辨率–>对角线分辨率/屏幕尺寸–>屏幕像素密度PPI | 设备像素比
dpr
= 物理像素 / 设备独立像素dip(dp)
|viewport: scale
|CSS
像素px
1.3 视口viewport
viewport
表示浏览器的可视区域,也就是浏览器中用来显示网页的那部分区域。存在三种 viewport
分别为 layout viewport
、visual viewport
以及 ideal viewport
-
layout viewport
:布局视口 -
visual viewport
:视觉视口 -
ideal viewport
:理想视口
2 移动端适配方案
2.1 rem适配
rem
(font size of the root element)是CSS3
新增的一个相对单位,是指相对于根元素的字体大小的单位。
使用rem
模拟vw
特性适配多种屏幕尺寸
// toRem.js
export default function() {
const root = document.documentElement;
/** 以iPhone6为例:布局视口为375px,我们把它分成10份,则1rem = 37.5px,
* 这时UI给定一个元素的宽为375px(设备独立像素),
* 我们只需要将它设置为375 / 37.5 = 10rem。
*/
const scale = root.clientWidth / 10
root.style.fontSize = scale + 'px'
}
// main.js
import Vue from 'vue'
import App from './App.vue'
import toRem from "./utils/toRem" //
toRem()
window.addEventListener('resize', toRem)
Vue.config.productionTip = false
new Vue({
render: h => h(App),
}).$mount('#app')
由于实际开发中计算rem
比较麻烦,可以借助less
文件计算,然后将@rem
配置成less
全局变量
/*rem.less*/
@device-width: 375; /*设备布局视口*/
@rem: (@device-width/10rem);
viewport
单位得到众多浏览器兼容,lib-flexible
这个过渡方案已经可以放弃使用。
2.2 vw\vh适配
vw(Viewport Width)
、vh(Viewport Height)
是基于视图窗口的单位,是css3中提出来的,基于视图窗口的单位。
vh
、vw
方案即将视觉视口宽度window.innerWidth
和视觉视口高度window.innerHeight
等分为 100 份
上面的flexible方案就是模仿这种方案,因为早些时候vw还没有得到很好的兼容。
vw(Viewport's width)
:1vw
等于视觉视口的1%
vh(Viewport's height)
:1vh
为视觉视口高度的1%
vmin
:vw
和vh
中的较小值vmax
: 选取vw
和vh
中的较大值
设置meta标签
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
px自动转换vw
- 安装插件 +
webpack
配置
npm install postcss-px-to-viewport --save-dev
module.exports = {
plugins: {
// ...
'postcss-px-to-viewport': {
// options
unitToConvert: 'px', // 需要转换的单位,默认为"px"
viewportWidth: 750, // 设计稿的视窗宽度
unitPrecision: 5, // 单位转换后保留的精度
propList: ['*', '!font-size'], // 能转化为 vw 的属性列表
viewportUnit: 'vw', // 希望使用的视窗单位
fontViewportUnit: 'vw', // 字体使用的视窗单位
selectorBlackList: [], // 需要忽略的 CSS 选择器,不会转为视窗单位,使用原有的 px 等单位
minPixelValue: 1, // 设置最小的转换数值,如果为 1 的话,只有大于 1 的值会被转换
mediaQuery: false, // 媒体查询里的单位是否需要转换单位
replace: true, // 是否直接更换属性值,而不添加备用属性
exclude: undefined, // 忽略某些文件夹下的文件或特定文件,例如 'node_modules' 下的文件
include: /\/src\//, // 如果设置了include,那将只有匹配到的文件才会被转换
landscape: false, // 是否添加根据 landscapeWidth 生成的媒体查询条件
landscapeUnit: 'vw', // 横屏时使用的单位
landscapeWidth: 1125, // 横屏时使用的视窗宽度
},
},
};
less
文件配置
同样算的过程交给less
,直接按照设计稿去开发
// 还是rem.less 我们加一个@vw变量
@device-width: 375;
@rem: (@device-width/10rem);
@vw: (100vw/@device-width);
2.3 viewport + px
这个方案可以不用关注屏幕尺寸差异,直接按照设计稿上的标注进行开发,也无需单位换算,直接用px
在 HTML 的 head 标签里加入
<meta name="viewport" content="width={设计稿宽度}, initial-scale={屏幕逻辑像素宽度/设计稿宽度}" >
。
export function initViewport() {
const width = 375; // 设计稿宽度
const scale = window.innerWidth / width
let meta = document.querySelector('meta[name=viewport]')
let content = `width=${width}, init-scale=${scale}, user-scalable=no`
if(!meta) {
meta = document.createElement('meta')
meta.setAttribute('name', 'viewport')
document.head.appendChild(meta)
}
meta.setAttribute('content', content)
}
3 开发案例分析
3.1 项目适配方案(含折叠屏适配)
采用flexible
+改造方案
-
适配方案
浏览器如何简单模拟nex
机型
设置模拟机型,宽度为639,高度为616,dpr
为3 -
拉伸处理方案
修改项目flexible.js
文件;
原理:通过对屏幕宽高比来判断,设置rootFontSize
-
单独处理
在具体需要处理的元素添加媒体查询来单独处理
@media screen and (min-device-aspect-ratio: ~"4/5")
(function(win, document) {
var documentEl = document.documentElement
var maxwidth = 540
var dpr = devicePixelRatio === 4 ? 1 : devicePixelRatio
var tid = null
documentEl.dataset.dpr = dpr
var designScale = (documentEl.dataset.width || 1080) / 100
var rootFontSize = parseFloat(getComputedStyle(documentEl).fontSize)
var screenWidth = win.screen.width
var screenHeight = win.screen.height
var isVivoBrowser = /vivoBrowser/i.test(window.navigator.userAgent)
if (isVivoBrowser) {
rootFontSize = 16 // vivo浏览器会影响字体大小,必须设置为16
}
var refreshRem = function() {
var width = documentEl.clientWidth
var screenWidth = win.screen.width
var screenHeight = win.screen.height
if (width / dpr > maxwidth) {
width = maxwidth * 1
}
if (screenWidth / screenHeight < 0.8) {
rootFontSize = 16 // 直屏
}
if (screenHeight / screenWidth < 0.8) {
rootFontSize = 42 // 直屏横评
}
if (screenWidth / screenHeight >= 0.8 && screenWidth / screenHeight < 1) {
rootFontSize = 32 // 折叠屏
}
if (screenHeight / screenWidth >= 0.8 && screenHeight / screenWidth < 1) {
rootFontSize = 42 // 折叠屏横评
}
documentEl.style.fontSize = (width / designScale / rootFontSize) * 100 + '%'
}
refreshRem()
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
)
win.addEventListener(
'DOMContentLoaded',
function(e) {
var body = document.getElementsByTagName('body')[0]
body.style.margin = '0 auto'
},
false
)
})(window, document);
3.2 pad横屏适配
1、设计点检,给出优化设计稿
2、通过媒体查询
@media only screen and (orientation: landscape)
总结
rem方案
适配原理稍复杂
需要使用 JS
设计稿标注的 px
换算到 css
的 rem
计算简单
方案灵活,既能实现整体缩放,又能实现局部不缩放
vw 方案
适配原理简单
不需要 JS
即可适配
设计稿标注的 px
换算到 css
的 vw
计算复杂
方案灵活,既能实现整体缩放,又能实现局部不缩放
viewport+px方案
适配原理简单
需要使用 JS
直接使用设计稿标注无需换算
方案死板,只能实现页面级别肢体缩放