1,h5 页面的自适应
rem、vw、vh 之间的反复横跳
rem 单位是相对于根节点的字体大小的,所以通过设置根节点的字体大小可以动态的改变 rem 的大小,即 html 的 font-size(默认是 16px ) === 1rem 。
但是,所有屏幕的 1rem 都等于 16px 的话,那岂不是没有自适应的功能。所以,需要一个对照物(即:某个特定像素的屏幕)作为标准,然后其它屏幕按照参照物对一个单位的 rem 所对应的 px 值进行相应改变。
那么,我们该选一个怎样的屏幕作为基准呢?这里以 iPhone X 为准,使用 iPhone X 的原因是:其手机像素为 750 x 1624 ,我们通常会拿宽度去做计算,而 750 这个数字恰巧是整数,便于计算。同理,iPhone 6 亦是如此。
传统方法设置 html 的 font-size 值
# 基准大小
const baseSize = 32
# 设置 rem 函数
function setRem () {
# 当前页面宽度相对于 750 宽的缩放比例,可根据自己需要修改。
const scale = document.documentElement.clientWidth / 750
# 设置页面根节点字体大小
document.documentElement.style.fontSize = (baseSize * Math.min(scale, 2)) + 'px'
}
# 初始化
setRem()
# 改变窗口大小时重新设置 rem
window.onresize = function () {
setRem()
}
其中的缺点:
需要手动去通过 px 去计算对应的 rem 。
要是屏幕宽度太大或太小,那么后续的视图也会越来越大,这也是不合理的。(需要有一个最大以及最小的宽度,这个好办)
有没有一个更好的方法,即不用手动计算 rem 值,又可以直接使用设计师放在 蓝湖 上设计稿的值。(前提是你的设计师用的也是和你一样的基准屏幕,不然也是白搭)
使用 postcss-pxtorem 将 px 转换为对应的 rem 值
关于 PX 要不要用/_ prettier-ignore _/注释 防止被格式化成小写
PS:postcss-pxtorem 在 2021 年安装时的最高版本时为 6.0.0 与 vue-cli 的 post-css 版本产生冲突,需要降级到 5.1.1 即可正常使用
css: {
loaderOptions: {
postcss: {
plugins: [
require('postcss-pxtorem')({
rootValue: 16,
minPixelValue: 2,
propList: ['*'],
})
]
}
}
}
2,一些概念的东西
参考自:
像素、分辨率、PPI、DPI、DP、DIP、DPR 分别是什么?
inch 英寸,简写为 in
PPI(Pixel Per Inch):每英寸包括的像素数。PPI 可以用于描述屏幕的清晰度以及一张图片的质量。使用 PPI 描述图片时,PPI 越高,图片质量越高,使用 PPI 描述屏幕时,PPI 越高,屏幕越清晰。
DPI(Dot Per Inch):即每英寸包括的点数。这里的点是一个抽象的单位,它可以是屏幕像素点、图片像素点也可以是打印机的墨点。
独立像素:
一般的像素都是指 物理像素,即设备上真实的物理单元。但自从 iPhone4 的面世,首次提出了 Retina Display(视网膜屏幕)的概念,即:把 2x2 个像素当 1 个像素使用(指独立像素,在这里就要用 2x2 个物理像素),这样让屏幕看起来更精致,但是元素的大小却不会改变。
注意:Retina 屏幕只是苹果提出的一个营销术语:在普通的使用距离下(一般比较近),人的肉眼无法分辨单个的像素点。这是表达视觉的效果。
这时候需要有一种单位去告诉不同类型的屏幕该显示元素的大小是多少,这个单位就是设备独立像素(Device Independent Pixels)简称 DIP 或 DP。
ps:打开 chrome 的开发者工具,我们可以模拟各个手机型号的显示情况,每种型号上面会显示一个尺寸,比如 iPhone X 显示的尺寸是 375x812,实际 iPhone X 的分辨率会比这高很多,这里显示的就是设备独立像素。
DPR(device pixel ratio)设备像素比: 即物理像素和设备独立像素的比值。在 web 中,浏览器为我们提供了 window.devicePixelRatio 来帮助我们获取 dpr。在 css 中,可以使用媒体查询 min-device-pixel-ratio,区分 dpr。
关于屏幕:
我们经常见到用 K 和 P 这个单位来形容屏幕:
P 代表的就是屏幕纵向的像素个数,1080P 即纵向有 1080 个像素,分辨率为 1920X1080 的屏幕就属于 1080P 屏幕。
我们平时所说的高清屏其实就是屏幕的物理分辨率达到或超过 1920X1080 的屏幕。
K 代表屏幕横向有几个 1024 个像素,一般来讲横向像素超过 2048 就属于 2K 屏,横向像素超过 4096 就属于 4K 屏。
视窗:
视口(viewport)代表当前可见的计算机图形区域。在 Web 浏览器术语中,通常与浏览器窗口相同,但不包括浏览器的 UI, 菜单栏等——即指你正在浏览的文档的那一部分。
常见 meta 设置
<meta name="viewport" content="width=device-width; initial-scale=1; maximum-scale=1; minimum-scale=1; user-scalable=no;">
解释
变量 | 可选值 | 描述 |
---|---|---|
width | 正整数或 device-width | 以 pixels(像素)为单位, 定义布局视口的宽度。 |
height | 正整数或 device-height | 以 pixels(像素)为单位, 定义布局视口的高度。 |
initial-scale | 0.0 - 10.0 | 定义页面初始缩放比率。 |
minimum-scale | 0.0 - 10.0 | 定义缩放的最小值;必须小于或等于 maximum-scale 的值。 |
maximum-scale | 0.0 - 10.0 | 定义缩放的最大值;必须大于或等于 minimum-scale 的值。 |
user-scalable | 一个布尔值(yes 或者 no) | 如果设置为 no,用户将不能放大或缩小网页。默认值为 yes。 |
1px 的问题:
原因是在设备像素比大于 1 的屏幕上,我们写的 1px 实际上是被多个物理像素渲染,这就会出现 1px 在有些屏幕上看起来很粗的现象。
解决方案:(只介绍比较好的解决方案)
1,伪类 + transform
.border_1px:before{
content: '';
position: absolute;
top: 0;
height: 1px;
width: 100%;
background-color: #000;
transform-origin: 50% 0%;
}
@media only screen and (-webkit-min-device-pixel-ratio:2){
.border_1px:before{
transform: scaleY(0.5);
}
}
@media only screen and (-webkit-min-device-pixel-ratio:3){
.border_1px:before{
transform: scaleY(0.33);
}
}
评价:这种方式可以满足各种场景,如果需要满足圆角,只需要给伪类也加上 border-radius 即可。
2,使用 svg
border-image 和 background-image 都可以模拟 1px 边框,但是使用的都是位图,还需要外部引入。
借助 PostCSS 的 postcss-write-svg 我们能直接使用 border-image 和 background-image 创建 svg 的 1px 边框:
@svg border_1px {
height: 2px;
@rect {
fill: var(--color, black);
width: 100%;
height: 50%;
}
}
.example { border: 1px solid transparent; border-image: svg(border_1px param(--color #00b1ff)) 2 2 stretch; }
评价:上面的方案是大漠在他的文章中推荐使用的,基本可以满足所有场景,而且不需要外部引入,这是我个人比较喜欢的一种方案。
移动端适配方案
1,flexible 方案(即:px 转 rem)
rem 是相对于 html 节点的 font-size 来做计算的。通过设置 document.documentElement.style.fontSize 就可以统一整个页面的布局标准。
背景:由于早前 vw、vh 兼容性较差,故使用 rem 去代替 vw、vh 的以过渡。
实现手段:将 html 节点的 font-size 设置为页面 clientWidth(布局视口)的 1/10,即 1rem 就等于页面布局视口的 1/10,这就意味着我们后面使用的 rem 都是按照页面比例来计算的。
如代码:
// set 1rem = viewWidth / 10
function setRemUnit () {
var rem = docEl.clientWidth / 10
docEl.style.fontSize = rem + 'px'
}
// 在页面大小调整时就修改一次 html 的 font-size 大小
// reset rem unit on page resize
window.addEventListener('resize', setRemUnit)window.addEventListener('pageshow', function (e) {
if (e.persisted) {
setRemUnit()
}
})
postcss-px2rem 配置
// postcss.config.js 中
module.exports = {
plugins: {
"postcss-pxtorem": {
// 一般,我常用 iPhone6 作为设计稿的基准。其分辨率(独立像素,其方便在各种 dpr 屏幕中缩放)为 375 x 667 . 取 1/10 作为 1rem 的数值(即:37.5px )
rootValue: 37.5,
minPixelValue: 2,
propList: ["*"]
}
}
};
2,vh、vw 方案
背景:rem 方案的过渡后,vh、vw 单位兼容性足够高。可以大胆用了,也不需要计算 font-size 值。
实际上直接使用 PostCSS 的 postcss-px-to-viewport 插件即可。
缺点:
1,px 转换成 vw 不一定能完全整除,因此有一定的像素差。(rem 方案也如此)
2,比如当容器使用 vw,margin 采用 px 时,很容易造成整体宽度超过 100vw,从而影响布局效果。当然我们也是可以避免的,例如使用 padding 代替 margin,结合 calc()函数使用等等
图片为什么会变模糊?
产生原因:
我们平时使用的图片大多数都属于位图(png、jpg…),位图由一个个像素点构成的,每个像素都具有特定的位置和颜色值。理论上,位图的每个像素对应在屏幕上使用一个物理像素来渲染,才能达到最佳的显示效果。
而在 dpr > 1 的屏幕上,位图的一个像素可能由多个物理像素来渲染,然而这些物理像素点并不能被准确的分配上对应位图像素的颜色,只能取近似值(原因在下面),所以相同的图片在 dpr > 1 的屏幕上就会模糊:
如图:
为什么要就近取色,而不是直接通过多个物理像素显示同一颜色呢?是因为用数倍物理像素显示一张图片时,如果只是简单粗暴的用 N 个物理像素直接显示 1 个图片像素,在移动端的高素质屏幕下锯齿感会很明显,为了解决这个问题则采用了平滑处理技术(就近取色属于其中一个操作)。假设有一张显示 0 的图片,则下面是处理过程:
如图:
从上图可以看出,dpr2 在图像的细腻程度上能比 dpr1 处理得更好,所以才不是简单地用 4 个物理像素代替 1 个物理像素。最终结论是不是不能直接取色,而是就近取色更好。
解决方案:
为了保证图片质量,我们应该尽可能让一个屏幕像素来渲染一个图片像素,所以,针对不同DPR的屏幕,我们需要展示不同分辨率的图片。
如:在dpr=2的屏幕上展示两倍图(@2x),在dpr=3的屏幕上展示三倍图(@3x)
1,srcset:(通过配置断点去修改不同分辨率的图片)
<img src="conardLi_1x.png"
srcset=" conardLi_2x.png 2x, conardLi_3x.png 3x">
2,通过 js 修改 img 的 src 值
const dpr = window.devicePixelRatio;
const images = document.querySelectorAll('img');
images.forEach((img)=>{
img.src.replace(".", `@${dpr}x.`);
})
3,vue 中的鸡贼做法
<img :src="require(`xx/${dpr}.jpg`)">
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!