阅读过几篇关于 px rem 的文章,感觉 rem 很强大。但是自己接触到的公司项目全部都使用 px,想知道为什么。是我司技术更新落后了吗?

我们当然有在用 vw 和 vh,但是只是在 layout 层级,组件层使用 px。

人数赞同最多的回答

先抛出观点:

  • 本文建议读者不要使用flexible或者其他修改viewport致使viewport width不等于device-width的方案(因为这会导致一些bug)
  • 本文建议读者不要使用以rem或者小程序rpx来实现等比缩放为主的布局手段,而使用面向逻辑像素px为主,面向等比缩放的vx和vxxx(vw/vh/vmax/vmin)为辅助的布局单位,搭配一些flex等布局手段
  • 本文建议读者一般情况遵循:同样观看距离情况下,大屏看的更多而不是大屏看的更大的设计最佳实践来进行布局,并且以这种最佳实践作为理论依据来传递给设计师(当然你觉得等比缩放才是合理的,请看其他回答,该回答不适合你)
  • 本文没有鼓吹读者使用响应式布局,也没有鼓吹读者盲目忽略兼容性使用vx和vxxx,同时也没有反对读者使用rem这个单位本身原有的定义来实现一些布局

吐槽

你们老是鼓吹rem的,醒醒吧,别再看网上大片大片的rem文章

flexible方案已经废坑了,已经废坑了,已经废坑了

对,flexible已经不维护了,原因自己看flexible的github

px没有问题,用rem来实现自适应才有问题(因为它是vx,vxxx单位的备胎),能问这种问题的人,应该认真研究一下viewport。

下面简单介绍下上面的几个知识点:

①:flexible.js

rem是相对于根元素<html>,这样就意味着,我们只需要在根元素确定一个px字号,则可以来算出元素的宽高。1rem=16px(浏览器html的像素,可以设定这个基准值),假如浏览器的html设为64px,则下面的元素则1rem=64px来运算。

阿里团队开源的一个库flexible.js作用就是通过rem和px之间的换算,把设计稿从px转到rem。兼容自适应各种移动端设备。

flexible.js关键代码(通过js来调整html的字体大小,而在页面中的制作稿则统一使用rem这个单位来制作):

;(function(win, lib) {
var doc = win.document;
var docEl = doc.documentElement;
var metaEl = doc.querySelector('meta[name="viewport"]');
var flexibleEl = doc.querySelector('meta[name="flexible"]');
var dpr = 0;
var scale = 0;
var tid;
var flexible = lib.flexible || (lib.flexible = {}); if (metaEl) {
console.warn('将根据已有的meta标签来设置缩放比例');
var match = metaEl.getAttribute('content').match(/initial\-scale=([\d\.]+)/);
if (match) {
scale = parseFloat(match[1]);
dpr = parseInt(1 / scale);
}
} else if (flexibleEl) {
var content = flexibleEl.getAttribute('content');
if (content) {
var initialDpr = content.match(/initial\-dpr=([\d\.]+)/);
var maximumDpr = content.match(/maximum\-dpr=([\d\.]+)/);
if (initialDpr) {
dpr = parseFloat(initialDpr[1]);
scale = parseFloat((1 / dpr).toFixed(2));
}
if (maximumDpr) {
dpr = parseFloat(maximumDpr[1]);
scale = parseFloat((1 / dpr).toFixed(2));
}
}
} if (!dpr && !scale) {
var isAndroid = win.navigator.appVersion.match(/android/gi);
var isIPhone = win.navigator.appVersion.match(/iphone/gi);
var devicePixelRatio = win.devicePixelRatio;
if (isIPhone) {
// iOS下,对于2和3的屏,用2倍的方案,其余的用1倍方案
if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) {
dpr = 3;
} else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)){
dpr = 2;
} else {
dpr = 1;
}
} else {
// 其他设备下,仍旧使用1倍的方案
dpr = 1;
}
scale = 1 / dpr;
} docEl.setAttribute('data-dpr', dpr);
if (!metaEl) {
metaEl = doc.createElement('meta');
metaEl.setAttribute('name', 'viewport');
metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
if (docEl.firstElementChild) {
docEl.firstElementChild.appendChild(metaEl);
} else {
var wrap = doc.createElement('div');
wrap.appendChild(metaEl);
doc.write(wrap.innerHTML);
}
} function refreshRem(){
var width = docEl.getBoundingClientRect().width;
if (width / dpr > 540) {
width = 540 * dpr;
}
var rem = width / 10;
docEl.style.fontSize = rem + 'px';
flexible.rem = win.rem = rem;
} 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); if (doc.readyState === 'complete') {
doc.body.style.fontSize = 12 * dpr + 'px';
} else {
doc.addEventListener('DOMContentLoaded', function(e) {
doc.body.style.fontSize = 12 * dpr + 'px';
}, false);
} refreshRem(); flexible.dpr = win.dpr = dpr;
flexible.refreshRem = refreshRem;
flexible.rem2px = function(d) {
var val = parseFloat(d) * this.rem;
if (typeof d === 'string' && d.match(/rem$/)) {
val += 'px';
}
return val;
}
flexible.px2rem = function(d) {
var val = parseFloat(d) / this.rem;
if (typeof d === 'string' && d.match(/px$/)) {
val += 'rem';
}
return val;
} })(window, window['lib'] || (window['lib'] = {}));

从上面的代码,主要是改变了dpx和document的font-size大小。大小为docEl.getBoundingClientRect().width / 10 + 'px';

详细可参考地址:《flexible.js 布局详解》

css里边px到底是什么

都9102了

都不知道px是viewport像素,

不是物理像素,不是逻辑像素,不是渲染像素

这里简单解释一下viewport像素,物理像素,逻辑像素,渲染像素

首先物理像素,逻辑像素,渲染像素的关系

下图是部分iphone设备的逻辑像素(下图为像素,其实应该翻译为点 Points),渲染像素,物理像素的指标,看了应该清晰这三者之间的关系

物理像素(physical pixel)就是反映显示屏的硬件条件,反映的就是显示屏内部led灯的数量,可以简单理解,一组三色led代表一个物理像素,当然根据屏幕物理属性以及处理led的方法不一样。强调这是物理的,因为这是一个纯硬件指标。比如我把屏幕锯了一半,物理像素就只有一半。
注:LED,发光二极管,是一种固态的半导体器件,它可以直接把电能转化为光能。
渲染像素(render pixel),则是在系统内部对物理像素的分配进行再一次的调整,在pc上,渲染像素其实就是设置里边的分辨率。对于显示设备,系统为显示设备提供渲染尺寸,由显示设备的“缩放引擎”(带存储器阵列的数字视频处理器)处理。这种“缩放引擎”一般内部有一系列的合理分辨率和一个推荐分辨率。一般推荐分辨率就是最大渲染像素,也是设备的物理分辨率(为了最佳表现)。这是一个软硬件(偏硬)结合的缩放方案。由于部分设备不能设置渲染像素,所以下文部分场景为了简化模型,直接跳过渲染像素,直接等同于物理像素
逻辑像素/点(device point / device pixel / point ),是为了调和距离不一样导致的差异,将所有设备根据距离,透视缩放到一个相等水平的观看距离之后得到的尺寸,是一个抽象的概念,这个单位就是ios开发的px,安卓开发的dp。对于pc,包括win(8+) linux,mac,由各自系统的或者对应软件(比如webview内部)提供的图像界面处理引擎处理进行缩放

在win上,可以通过显示设置缩放比例来调整部分应用的逻辑像素。对于linux,可以通过x和wayland的缩放比例来调整

但是,由于这个是一个纯软件的方案,如果部分软件不遵循开发规则,或者使用老旧的api,就会导致逻辑像素不合理,导致缩放问题。例如win10中部分旧的软件在高分屏的设备会导致界面偏小。因为他们实际是使用的是渲染像素而不是逻辑像素

各种设备,手机,平板,笔记本等逻辑像素

手机:逻辑像素在3xx-4xx(短边)之间

平板:10寸平板7xx-8xx(短边)

笔记本:13寸 1280 (长边)

24寸显示屏:1920(长边)

你会发现如果设置width=device-width下,无论是否高分屏,在浏览器得到的screen.width仍然符合上述的尺寸

逻辑像素的引入,简单来说,就是为了消除了不同屏幕观看距离和不同ppi(见下文)之间的差异,衍生出来的一个虚拟的尺寸

ppi(pixel per inch) 每英寸像素,指的是屏幕在每英寸的物理像素,更高的ppi意味着屏幕的清晰度更佳。

所谓高分屏,其实就是指ppi大于同类设备的屏幕。比如对于桌面设备,大于96ppi。对于移动设备,大于160ppi

所谓视网膜屏,其实就是指在该观看距离内超出人类的辨认能力的屏幕。比如对于桌面设备,大于192ppi。对于移动设备大于326ppi

ppi,对于移动设备而言,一般来说ppi以160为一个档次

也就是假设一个ppi160,2寸x3寸的屏幕,物理像素应该是320x480。同理ppi320,同样尺寸的屏幕,物理像素是640x960

由于它们尺寸一致,假设它们观看距离一致,那么消除掉ppi的影响,他们的逻辑像素是一致的

也就是

逻辑像素长度 = 物理像素长度 * 160 / ppi

得出都是 320 x 480

当然,由于生产标准不一致,不可能做到绝对的160ppi作为标准,所以ppi的等级划分是动态的

dpr (device point ratio / device pixel ratio) 渲染像素与逻辑像素的比例。由于渲染像素一般等于逻辑像素,如果ppi是以160为基准的话,那么 dpr = ppi / 160

多少倍屏或者多少x(三倍屏,3x,意思就是3dpr),一般来说就是说的是这个值

viewport像素又是什么,它本质是DIP(Device Independent Pixels),中文意思设备无关像素,是与上述所有像素都无绝对逻辑关系的一个单位。其实是浏览器内部对逻辑像素进行再处理的结果,简单来理解就是调整逻辑像素的缩放来达到适应设备的一个中间层

对于pc,viewport是不生效的,所以在pc上,px其实就是逻辑像素(chrome)。但是逻辑像素是与软件实现有关的,所以会出现一些问题。比如在win上,对于部分国产马甲浏览器,viewport内部没有适配系统的缩放等级,导致渲染的内容过小

如果你像评论区的某些看客一样麻木认为pc的px就是物理像素,那么你可能就不知道怎么解决了,甚至百度半天都找不到答案

比如这个问题

谷歌浏览器的100%显示和搜狗125%显示大小相同?

面向逻辑像素开发的基本开发流程

  • 1. 在head 设置width=device-width的viewport
  • 2. 在css中使用px
  • 3. 在适当的场景使用flex布局,或者配合vw进行自适应
  • 4. 在跨设备类型的时候(pc <-> 手机 <-> 平板)使用媒体查询
  • 5. 在跨设备类型如果交互差异太大的情况,考虑分开项目开发

那么viewport width=device-width是什么意思,其实就是让viewport的尺寸等于逻辑像素的尺寸

关于px的疑问

那有朋友就问,不同设备的物理像素是不一样的呀,我怎么实现不同物理像素的布局,如果设计师给你一张图,怎么将它转为我想要的在css里边的px

首先,你要读懂设计师给你设计图的意图,一般国内的设计师,给出手机版的设计图,一般是750px,注意这里的px,并不是我们的px(逻辑像素),其实是物理像素,因为设计师是根据iphone6或者同等设备来进行设计的,那么要知道iphone6的逻辑像素其实是 375,dpr是2,那么拿到手的设计稿,转换为逻辑像素,就得除以2,我们叫这种设计图,叫两倍图

同理,如果是375 + 375 + 375大小,那么我们就得除以3,叫三倍图

如果设计团队有使用墨刀或者蓝湖,你可以在两者里边设置你的查看尺寸,得到我们需要的逻辑像素

如果设计师不用蓝湖等工具,给你的并不是375的倍数怎么办,我先说办法,原因你们自己琢磨,我不细致分析

最简单的方法,设计师给你的图的物理宽度w,除以一个数x,如果得的出来的商在360 - 414之间,那么你换算的公式为【你在设计图测量出来的物理像素数除以x】,那么dpr就是x,也就是x倍图

那么朋友又会问,不同设备逻辑像素也不一样呀

对,不一样,但问题为什么我们要将它们弄成一样?其实,不一样,才是合理的。我暂且不说原因,原因后面的文章解释

那么不一样的情况,怎么布局?那就靠你们技术手段,flex,流式布局,vw等

总结

  • 设计师给的设计稿都是物理像素(比如750px),我们实际处理的是移动端的屏幕尺寸px是逻辑像素(比如iphone6的逻辑像素是375);由于PC端viewport是不生效的,所以pc端的px就是逻辑像素(chrome)
  • ppi(pixel per inch) 每英寸像素,指的是屏幕在每英寸的物理像素,更高的ppi意味着屏幕的清晰度更佳。对于移动设备而言,一般来说ppi以160为一个档次。
  • dpr (device point ratio / device pixel ratio) 渲染像素与逻辑像素的比例。由于渲染像素一般等于逻辑像素,如果ppi是以160为基准的话,那么 dpr = ppi / 160.
  • 逻辑像素长度 = 物理像素长度 * 160 / ppi,也即是逻辑像素长度 = 物理像素长度 * 1/dpr;

知乎问题地址及详细答案:《为什么很多web项目还是使用 px,而不是 rem?》

知乎问题:为什么很多web项目还是使用 px,而不是 rem?的更多相关文章

  1. 献给那些每次调试时都要启动很多WEB项目的苦逼程序猿

    当一个解决方案包含多个WEB项目的时候,只要按F5调试,其它用不着的WEB项目也会自动添加到托盘里.很多新手都不知道如何解决这个问题,我也是刚知道. 在网上找了很多资料看到有2种解决方法: 1.把WE ...

  2. 小型web项目的模块化(转)

    背景   目前团队中新的 Web 项目基本都采用了 Vue 或 React ,加上 RN,这些都属于比较重量级的框架,然而对于小型 Web 页面,又显得过大.早期的一些项目则使用了较原始的 HTML ...

  3. 在WEB项目中调用QQ通讯组件打开QQ聊天界面

    在很多WEB项目中,需要提供在线服务的功能,加上自己的联系方式,例如:QQ,不用添加QQ好友也可以交谈,那这到底是怎么实现的呢? 对于这个功能,需要提到一个组件,即“QQ通讯组件”.QQ通讯组件是一种 ...

  4. 原创一看便知、Maven创建web项目

    创建maven-项目    如果 pom.xml 文件报错 右击项目-->Maven-->update Project 详细步骤   上图中Next 2.继续Next 3.选maven-a ...

  5. IntelliJ IDEA WEB项目的部署配置

    以下内容是我网上找的比较全面了,其中关于facets配置很多地方都没有说明,其实很重要,我加入了自己的理解.其他来自网络.在导入一个项目有问题时,建议先创建一个正确的web项目,然后对比配置项,一般就 ...

  6. MyEclispe发布web项目-遁地龙卷风

    (-1)写在前面 我用的是MyEclipse8.5. 还记得以前帮助一个女同学解决问题的时候,特意情调了要先启动服务在发布项目,其实单独的时候都是知道的,总和起来后就容易片面的给出结论.因为不会发生问 ...

  7. ASP.NET 5 Web 项目

    在Mac OS X Yosemite 10.10.3 中搭建第一个 ASP.NET 5 Web 项目 终于有时间在 Mac 上安装一下 ASP.NET 5,网上有许多教程,但是多数的时间比较早了,版本 ...

  8. web项目自定义路由_实现静态资源URL控制

    前言: IIS会默认把:图片.JS.HTML.CSS这些文件当成静态资源处理,为了减少服务器压力,默认这些静态资源是不走URL路由规则控制的. 作为小白及初学者,本人对这些了解甚少,补充基础知识吧: ...

  9. java web项目为什么我们要放弃jsp?

    前戏: 以前的项目大多数都是java程序猿又当爹又当妈,又搞前端(ajax/jquery/js/html/css等等),又搞后端(java/mysql/Oracle等等). 随着时代的发展,渐渐的许多 ...

  10. 《Maven实战》笔记-8-构建部署Web项目

      一.Web项目结构 1.显式指定Web项目打包方式为war:   2.默认目录 根据“约定大于配置”的规则,Web项目的类及资源文件默认位置为src/main/java和src/main/reso ...

随机推荐

  1. yolov5+deepsort+slowfast复现

    1.运行环境 ubuntu 18.04.1 Cuda 11.5 Python 3.8.15 torch 1.10.1+cu113 torchvision 0.11.2+cu113 2.安装PyTorc ...

  2. [oeasy]python0122_日韩字符_日文假名_JIS_Shift_韩国谚文

    日文假名和韩国谚文 回忆上次内容 上次回顾了非ascii的拉丁字符编码的进化过程 0-127 是 ascii 的领域   世界各地编码分布 拉丁字符扩展 ascii 共 16 种 由iso组织制定 从 ...

  3. oeasy教您玩转vim - 35 - # 正则表达

    ​ 查找进阶 回忆上节课内容 实时搜索 :set incsearch 大写小写 ignorecase 查找当前单词 * 正向按单词 # 反向按单词 g* 正向不按单词 g# 反向不按单词 继续查找 n ...

  4. Day 4 - 搜索进阶与模拟

    启发式搜索 下面将简要介绍启发式搜索及其用法. 定义 启发式搜索(英文:\(\text{heuristic search}\))是一种在普通搜索算法的基础上引入了启发式函数的搜索算法. 启发式函数的作 ...

  5. ComfyUI进阶:Comfyroll插件 (四)

    ComfyUI进阶:Comfyroll插件 (四) 前言: 学习ComfyUI是一场持久战,而Comfyroll 是一款功能强大的自定义节点集合,专为 ComfyUI 用户打造,旨在提供更加丰富和专业 ...

  6. 领域驱动设计(DDD)分层架构的三种模式

    模式一:四层架构 1.User Interface为用户界面层(或表示层),负责向用户显示信息和解释用户命令.这里指的用户可以是另一个计算机系统,不一定是使用用户界面的人.2.Application为 ...

  7. Django--StreamingHttpResponse下载文件

    from django.shortcuts import render, HttpResponse from django.http import StreamingHttpResponse impo ...

  8. java web 开发框架编

    学习web 框架上开发需要的是安装  mysql 8.0  idea 2022 git  2.2.23  node 16以上 (新版本不好拉有些库了)jdk 最好是17以上 jdk8也是行的,反正不管 ...

  9. hmall | 引入ES实现高效搜索与同步双写

    在gitee.飞书.百度云.B站中,黑马都没有上传该部分资料,以下皆为个人观点,如有纰漏欢迎指正 1.先把item-service中的searchcontroller抽出来,抽到一个模块中并将其设为h ...

  10. python解决urllib发送请求报错:urllib.error.URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:xxxx)>

    在使用urllib.request.Request(url)前,添加代码放到最前面 import ssl ssl._create_default_https_context = ssl._create ...