前置知识
关于像素
- 逻辑像素 :也可以称为 css 像素 ,是我们在 css 代码中设置的像素,比如
width=20px
- 设备像素 :也可以称为 物理像素 ,指的是设备能控制显示的最小物理单位。即显示器上一个个小点。是在出厂的时候就固定不变的。
- 位图像素:指的是栅格图像(png,jpg,gif等)中最小的数据单元,每个位图像素都存储着显示信息(比如颜色、透明度等)。
设备像素比
设备像素比 DPR= 设备像素/逻辑像素
,用于衡量设备屏幕上的物理像素与逻辑像素之间的关系。
一般情况下在普通的电脑浏览器上,1个CSS像素通常对应1个物理像素,因此设备像素比为1。但在高分辨率的设备(如Retina显示屏、高DPI手机屏幕等)上,1个CSS像素可能对应多个物理像素,这时设备像素比会大于1。
例如,对于具有2倍像素密度的Retina显示屏,设备像素比通常为2,这意味着在这样的屏幕上,1个CSS像素将对应4个物理像素(一个CSS像素在水平和垂直方向上各对应2个物理像素,总共为4个物理像素)。
在代码中可以通过 var dpr = window.devicePixelRatio || 1;
获取设备像素比。
分辨率
分辨率=水平像素*垂直像素
。
分辨率(Resolution)是指显示设备(如显示器、手机屏幕等)上可以显示的像素数量,通常用水平像素数和垂直像素数来描述。分辨率决定了显示设备的清晰度和细节程度。
rem
众所周知,rem 是相对于 html 的 font-size 而言的。所以可以通过动态设置 html 的 font-size 从而实现响应式的布局。
文本字号为什么不建议使用 rem
比如在阅读小说的时候,用户使用更大的屏幕是为了获取更多的文本,而不是更大的字体。同理用户也不会希望在很小的屏幕上字体变得很小,那对于阅读是存在障碍的。
viewport 的 meta
标签
<meta>
标签有很多种,而这里要着重说的是viewport的meta
标签,其主要用来告诉浏览器如何规范的渲染Web页面,而你则需要告诉它视窗有多大。在开发移动端页面,我们需要设置meta
标签如下:
js
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
代码以显示网页的屏幕宽度定义了视窗宽度。网页的比例和最大比例被设置为100%。
移动端布局实现方案
- flexible.js:淘宝移动端适配方案,开箱即用。
- vw 布局方案:相对于
- rem + media
- vw
- %:不推荐,因为不同情况下的参照物不同
flexible.js
flexible.js 是淘宝移动端适配方案。
该库解决移动端网页开发中的屏幕适配问题而设计的,其主要实现原理是通过设置 <meta>
标签的 viewport 属性来动态调整页面的缩放比例,从而实现不同尺寸设备上页面的适配。
原理
1.获取设备的像素密度(DPR) :
js
var dpr = window.devicePixelRatio || 1;
2.计算页面的根元素字体大小:
计师给的视觉稿一般是 640px
或者是 750px
(这里指的是宽度),我们会选定一种尺寸的稿子作为参考去布局。
假设我们选择 750px
去布局,
-
第一件事情就是要设置根元素
html
的font-size
,那我们把750px
分成10
份,$rem = 750px / 10 = 75px
,也就是,1rem = 75px
,10rem = 750px
。也就相当于将页面的宽度等分成10个$rem
。 -
视觉稿上的单位是
px
,我们需要转成rem
,因为前面说了1rem = 75px
,1px = 1/75rem
,所以用视觉稿上的px
值除以75
就可以了,比如,100px = 100/75rem = 1.33333rem
。这个过程可以通过
sass
去实现,避免自己手动计算:scss/* 基准 font-size,可设置成其他值 */ $UIWidth:750px; $rem: $UIWidth/10; @mixin px2rem($name, $px){ #{$name}: $px / $rem * 1rem; } /* 使用示例:*/ .container { @include px2rem(height, 240); } /* Scss 编译后:*/ .container { height: 2.4rem; }
为了方便运算,有些老司机会把 750
分成 7.5
份,即 $rem = 750px / 7.5 = 100px
,所以,用视觉稿上的 px
直接除以 100
就好了。
3.设置 <meta>
标签的 viewport 属性
将页面的 viewport 设置为一个固定的宽度,使得页面的宽度与设备的物理宽度之间建立起一个固定的关系。这样可以保证页面在不同设备上以相同的比例进行缩放,从而实现适配。
js
// 获取设备像素密度
var dpr = window.devicePixelRatio || 1;
// 计算页面缩放比例
var scale = 1 / dpr;
// 构建 viewport meta 标签
var metaEl = document.createElement('meta');
metaEl.setAttribute('name', 'viewport');
metaEl.setAttribute('content', 'width=device-width, initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
// 插入 meta 标签到文档头部
document.head.appendChild(metaEl);
4.动态调整根元素字体大小
通过监听窗口的 resize 事件,在窗口大小发生变化时,动态调整根元素的字体大小,以保持页面的布局比例不变。
事实上他做了这几样事情:
- 动态改写
<meta>
标签 - 给
<html>
元素添加data-dpr
属性,并且动态改写data-dpr
的值 - 给
<html>
元素添加font-size
属性,并且动态改写font-size
的值
使用注意事项
px2rem
需要在编写样式的时候将px
转化成rem
。可以使用scss
实现(这个上文已经介绍过了)。
也可以使用postcss-px2rem,会自动对代码中的px进行转换。
文本处理
文本字号不建议使用 rem
因为我们希望文本能够流动,使得在大屏手机能看到更多的文本,而不是文本因为 rem 等比缩放使得大字号显得突兀;同样也不希望在小屏手机字体因为等比缩放显得太小
所以需要根据不同的 dpr 动态设置固定字号的字体:
css
div {
width: 1rem;
height: 0.4rem;
font-size: 12px; // 默认写上 dpr 为 1 的 fontSize
}
[data-dpr="2"] div {
font-size: 24px;
}
[data-dpr="3"] div {
font-size: 36px;
}
为了能更好的利于开发,在实际开发中,我们可以定制一个 font-dpr()
这样的 Sass
混合:
scss
@mixin font-dpr($font-size){
font-size: $font-size;
[data-dpr="2"] & {
font-size: $font-size * 2;
}
[data-dpr="3"] & {
font-size: $font-size * 3;
}
}
最大屏幕控制
如果不设置最大宽度,font-size 将随着布局视口的增大而增大,所有尺寸等比例放大,如果在 PC 端打开网页,巨大的按钮将不堪入目。所以设置一个宽度断点,超过断点将使用同一宽度。
js
function refreshRem(){
var width = docEl.getBoundingClientRect().width;
// 最大宽度控制
if (width / dpr > 540) {
width = 540 * dpr;
}
var rem = width / 7.5;
docEl.style.fontSize = rem + 'px';
flexible.rem = win.rem = rem;
}
或者也可以通过媒体查询:
css
/* 当屏幕宽度小于等于 768px 时,应用这些样式 */
@media (max-width: 768px) {
/* 添加你需要应用的样式 */
}
/* 当屏幕宽度大于 768px 时,应用这些样式 */
@media (min-width: 769px) {
/* 添加你需要应用的样式 */
}
图像的高清显示
先来看看位图在 Retina
屏是怎样显示的
位图像素: 一个位图像素是栅格图像 (如:png, jpg, gif 等)最小的数据单元。每一个位图像素都包含着一些自身的显示信息(如:显示位置,颜色值,透明度等)
对于 dpr=2
的 retina
屏幕而言,1 个位图像素对应于 4 个物理像素,由于单个位图像素不可以再进一步分割,所以只能就近取色,从而导致图片模糊(注意上述的几个颜色值)
为了使这张图高清显示,需要提供(@2x)两倍图,比如,200×300(css pixel) img 标签,就需要提供 400×600 的图片,使得位图像素跟物理像素 1:1 对应,图片就自然清晰了
但是对于普通屏幕,就没有必要加载(@2x)两倍图了,一个是因为会造成资源浪费,另一个是会让图片失去了一些锐利度(或色差)
对于图片高清,最好的解决方案是,不同的 dpr
,加载不同的图片。
vw 布局方案
vw
布局方案中,所有元素采用 vw
布局,对于 750
设计稿,750px = 100vw ,即 1px = 100/750vw。
可以借助sass:
scss
@mixin px2vw($name, $px){
#{$name}: $px * (100/750) * 1vw;
}
/* 使用示例:*/
.container {
@include px2vw(height, 750);
}
/* Scss 编译后:*/
.container {
height: 100vw;
}
无法做最大屏幕控制
纯 vw 布局,没有办法控制最大宽度,因为 vw 单位只受视觉视口影响。
rem单位+动态html的font-size
rem 单位是相对于 html 元素的 font-size 来设置的,通过在不同屏幕尺寸下,动态的修改 html 元素的 font-size 以此来达到适配效果。
-
可以通过媒体查询去根据屏幕尺寸设置 html 元素的 font-size。
缺点:
- 需要针对不同的屏幕编写大量的媒体查询
- 如果动态改变尺寸,不会实时更新,只是一个个区间
-
通过监听屏幕尺寸的变化来动态修改 html 元素的 font-size 大小
js
function setRemUnit() {
// 获取所有的 html 元素
const htmlEl = document.documentElement
// 375 -> 16px
// 320px -> 12px
// 我们需要动态更改字体大小,因此需要获取网页的宽度
// 拿到客户端宽度
const htmlWidth = htmlEl.clientWidth
// 将宽度分成10份
const htmlFontSize = htmlWidth / 10
console.log('htmlFontSize', htmlFontSize);
// 将值给到html的font-size
htmlEl.style.fontSize = htmlFontSize + 'px'
}
setRemUnit()
// 给 window 添加监听事件
window.addEventListener('resize', setRemUnit)