使用rem之前,先得弄清楚rem、em和px之间的关系,特别是每一个单位的使用跟代码块的继承之间的关系:
通过对比会发现:只是单位使用不一样但效果却是截然不同的。rem和em都是相对单位,px则不是
在CSS样式表中,单位em是作为字体高度的单位来使用的,但实际字体大小的高度显示是用户对DPI的定义来决定的。为了改善这种样式规则,单位rem则是直接取决于文档根元素字体默认大小,也可以理解为root em,跟em有所不同的是使用rem单位的字体大小在整个文档中都是恒定不变的
rem 的官方定义:The font size of the root element,即以根节点的字体大小作为基准值进行长度计算。
rem的兼容性:
通过上图可见:
先不谈安卓,就以iphone各种手机的尺寸来说,iPhone4,5 宽度320px,iPhone6 375px,iPhone6 plus 414px. iPad 768px。如果一个按钮,固定一个75x25的尺寸,那么就必然会导致在不同尺寸下的相对大小不一样。这带来的问题就在于会直接影响到设计的美观,可能在iPhone6下,一个完美的设计图,到了iPhone5,就变得low很多。为了让页面元素的尺寸能够在设备宽度变化的时候也跟着变化,rem就出现了。
使用rem适配的原理就是我们只需要在设备宽度大小变化的时候,调整html的字体大小,那么页面上所有使用rem单位的元素都会相应的变化。 这也是rem与px最大的区别。
在讲述rem布局之前得先搞明白的就是移动设备上的viewport,只有明白了viewport的概念以及弄清楚了跟viewport有关的meta标签的使用,才能更好地让我们的网页适配或响应各种不同分辨率的移动设备。
通俗的讲,移动设备上的viewport就是设备的屏幕上能用来显示我们的网页的那一块区域,即浏览器上(也可能是一个app中的webview)用来显示网页的那部分区域,但viewport又不局限于浏览器可视区域的大小,它可能比浏览器的可视区域要大,也可能比浏览器的可视区域要小。
下图列出了一些设备上浏览器默认的viewport宽度。
css中的像素只是一个抽象的单位,在不同的设备或不同的环境中,css中的1px所代表的设备物理像素是不同的。
在移动端浏览器中以及某些桌面浏览器中,window对象有一个devicePixelRatio属性,它的官方的定义为:
window.devicePixelRatio is the ratio between physical pixels and device-independent pixels (dips) on the device.
window.devicePixelRatio = physical pixels / dips
设备物理像素和设备独立像素的比例,也就是 devicePixelRatio = 物理像素 / 独立像素。css中的px就可以看做是设备的独立像素,所以通过devicePixelRatio,我们可以知道该设备上一个css像素代表多少个物理像素。
需要注意的是,devicePixelRatio存在兼容性问题,具体可以查看PPK made some research on devicePixelRatio
拓展:PPK的关于三个viewport的理论
ppk把移动设备上的viewport分为layout viewport 、 visual viewport 和 ideal viewport 三类,其中的ideal viewport是最适合移动设备的viewport,ideal viewport的宽度等于移动设备的屏幕宽度,只要在css中把某一元素的宽度设为ideal viewport的宽度(单位用px),那么这个元素的宽度就是设备屏幕的宽度了,也就是宽度为100%的效果。ideal viewport 的意义在于,无论在何种分辨率的屏幕下,那些针对ideal viewport 而设计的网站,不需要用户手动缩放,也不需要出现横向滚动条,都可以完美的呈现给用户。
类别 | 简述 | 说明 | 图例 |
---|---|---|---|
layout viewport | 浏览器默认的viewport | layout viewport 的宽度是大于浏览器可视区域的宽度的 | ![]() |
visual viewport | 浏览器可视区域的大小 | 在Android 2, Oprea mini 和 UC 8中无法正确获取 | ![]() |
ideal viewport | 移动设备的理想viewport | 理想适配指的是:1、不需要用户缩放和横向滚动条就能正常的查看网站的所有内容;2、显示的文字的大小是合适,无论是在何种密度屏幕,何种分辨率下,显示出来的大小都是差不多的。当然,不只是文字,其他元素如图片等也如此 | ![]() ![]() |
文章链接
A tale of two viewports — part one
A tale of two viewports — part two
Meta viewport
在苹果的规范中,meta viewport 有6个指令,[可以同时使用,也可以单独使用或混合使用,多个指令同时使用时用逗号隔开]如下:
指令 | 说明 |
---|---|
width | 设置layout viewport 的宽度,为一个正整数,或字符串”width-device” |
initial-scale | 设置页面的初始缩放值,为一个数字,可以带小数 |
minimum-scale | 允许用户的最小缩放值,为一个数字,可以带小数 |
maximum-scale | 允许用户的最大缩放值,为一个数字,可以带小数 |
height | 设置layout viewport 的高度,这个属性对我们并不重要,很少使用 |
user-scalable | 是否允许用户进行缩放,值为”no”或”yes”, no 代表不允许,yes代表允许 |
要把当前的viewport宽度设为ideal viewport的宽度,既可以设置 width=device-width
,也可以设置 initial-scale=1
,但这两者各有一个小缺陷,就是iphone、ipad以及IE 会横竖屏不分[下图所示],通通以竖屏的ideal viewport宽度为准。所以,最完美的写法应该是,两者都写上去,这样就initial-scale=1
解决了 iphone、ipad的毛病,width=device-width
则解决了IE的毛病:
"viewport" content="width=device-width, initial-scale=1">
//注意:缩放是相对于ideal viewport来缩放的,缩放值越大,当前viewport的宽度就会越小,反之亦然
rem布局就是指为文档的根节点元素设置一个基准字体大小,然后所有的元素尺寸都以rem为单位来写,为了能够在不同尺寸的手机屏幕上自适应,需要用js来判断手机宽度,并动态设置的字体大小,这样基准字体变了,元素的尺寸自然相应变化,达到了自适应的效果。
废话说了辣么多,这里给大家介绍一个移动端布局开发解决方案
使用动态的HTML根字体大小和动态的viewport scale。
遵循视觉一致性原则。在不同大小的屏幕和不同的设备像素密度下,让你的页面看起来是一样的。
不仅便捷了你的布局,同时它使用起来异常简单。
示例名称 | 演示地址 | 贡献者 |
---|---|---|
普通的演示 | http://imochen.github.io/hotcss/example/normal/ | 墨尘 |
duang游戏 | http://imochen.github.io/hotcss/example/duang/ | 阳阳 |
灰太狼 | http://imochen.github.io/hotcss/example/wolf/ | 阳阳 |
viewport
,根据当前环境计算出最适合的viewport
。px2rem
转换方法,CSS布局,零成本转换,原始值不丢失。<script src="/path/to/hotcss.js">script>
hotcss
提供的将px转为rem的方法,可根据您的需要选择使用。
//px2rem.scss
@function px2rem( $px ){
@return $px*320/$designWidth/20 + rem;
}
推荐使用scss来编写css,在scss文件的头部使用import
将px2rem
导入,
@import '/path/to/px2rem.scss';
如果你的项目是单一尺寸设计图,那么你需要去px2rem.scss中定义全局的designWidth
。
@function px2rem( $px ){
@return $px*320/$designWidth/20 + rem;
}
$designWidth : 750; //如设计图是750
如果你的项目是多尺寸设计图,那么就不能定义全局的designWidth
了。需要在你的业务scss
中单独定义。如以下是style.scss
@import '/path/to/px2rem.scss';
$designWidth : 750; //如设计图是750
$designWidth
必须要在使用px2rem
前定义。否则scss编译会出错。
注意:如果使用less,则需要引入less-plugin-functions
,普通的less编译工具无法正常编译。
直接写px肯定是不能适配的,那hotcss.js
会在html上注册data-dpr
属性,这个属性用来标识当前环境dpr值。那么要使用px可以这么写。
//scss写法
#container{
font-size: 12px ;
[data-dpr="2"] &{
font-size: 24px;
}
[data-dpr="3"] &{
font-size: 36px;
}
}
可能你会说 talk is cheap,show me the code
,那我现在列下hotcss整个项目的目录结构。
├── example //所有的示例都在这个目录下
│ ├── duang
│ ├── normal
│ └── wolf
│
└── src //主要文件在这里
├── hotcss.js
├── px2rem.less
├── px2rem.scss
└── px2rem.styl
(function( window , document ){
'use strict';
//给hotcss开辟个命名空间,别问我为什么,我要给你准备你会用到的方法,免得用到的时候还要自己写。
var hotcss = {};
(function() {
//根据devicePixelRatio自定计算scale
//可以有效解决移动端1px这个世纪难题。
var viewportEl = document.querySelector('meta[name="viewport"]'),
hotcssEl = document.querySelector('meta[name="hotcss"]'),
dpr = window.devicePixelRatio || 1,
maxWidth = 540,
designWidth = 0;
dpr = dpr >= 3 ? 3 : ( dpr >=2 ? 2 : 1 );
//允许通过自定义name为hotcss的meta头,通过initial-dpr来强制定义页面缩放
if (hotcssEl) { var hotcssCon = hotcssEl.getAttribute('content'); if (hotcssCon) { var initialDprMatch = hotcssCon.match(/initial\-dpr=([\d\.]+)/); if (initialDprMatch) { dpr = parseFloat(initialDprMatch[1]); } var maxWidthMatch = hotcssCon.match(/max\-width=([\d\.]+)/); if (maxWidthMatch) { maxWidth = parseFloat(maxWidthMatch[1]); } var designWidthMatch = hotcssCon.match(/design\-width=([\d\.]+)/); if (designWidthMatch) { designWidth = parseFloat(designWidthMatch[1]); } } }
document.documentElement.setAttribute('data-dpr', dpr);
hotcss.dpr = dpr;
document.documentElement.setAttribute('max-width', maxWidth);
hotcss.maxWidth = maxWidth;
if( designWidth ){ document.documentElement.setAttribute('design-width', designWidth); hotcss.designWidth = designWidth; }
var scale = 1 / dpr,
content = 'width=device-width, initial-scale=' + scale + ', minimum-scale=' + scale + ', maximum-scale=' + scale + ', user-scalable=no';
if (viewportEl) { viewportEl.setAttribute('content', content); } else { viewportEl = document.createElement('meta'); viewportEl.setAttribute('name', 'viewport'); viewportEl.setAttribute('content', content); document.head.appendChild(viewportEl); }
})();
hotcss.px2rem = function( px , designWidth ){
//预判你将会在JS中用到尺寸,特提供一个方法助你在JS中将px转为rem。就是这么贴心。
if( !designWidth ){
//如果你在JS中大量用到此方法,建议直接定义 hotcss.designWidth 来定义设计图尺寸;
//否则可以在第二个参数告诉我你的设计图是多大。
designWidth = parseInt(hotcss.designWidth , 10);
}
return parseInt(px,10)*320/designWidth/20;
}
hotcss.rem2px = function( rem , designWidth ){
//新增一个rem2px的方法。用法和px2rem一致。
if( !designWidth ){
designWidth = parseInt(hotcss.designWidth , 10);
}
//rem可能为小数,这里不再做处理了
return rem*20*designWidth/320;
}
hotcss.mresize = function(){
//给HTML设置font-size。
var innerWidth = document.documentElement.getBoundingClientRect().width || window.innerWidth;
if( hotcss.maxWidth && (innerWidth/hotcss.dpr > hotcss.maxWidth) ){
innerWidth = hotcss.maxWidth*hotcss.dpr;
}
if( !innerWidth ){ return false;}
document.documentElement.style.fontSize = ( innerWidth*20/320 ) + 'px';
hotcss.callback && hotcss.callback();
};
hotcss.mresize();
//直接调用一次
window.addEventListener( 'resize' , function(){ clearTimeout( hotcss.tid ); hotcss.tid = setTimeout( hotcss.mresize , 33 ); } , false );
//绑定resize的时候调用
window.addEventListener( 'load' , hotcss.mresize , false );
//防止不明原因的bug。load之后再调用一次。
setTimeout(function(){ hotcss.mresize(); //防止某些机型怪异现象,异步再调用一次 },333)
window.hotcss = hotcss;
//命名空间暴露给你,控制权交给你,想怎么调怎么调。
})( window , document );
栗子:
//.html
<html lang="en">
<head>
<meta charset="UTF-8">
<title>这是一个最最普通的演示title>
<link rel="stylesheet" href="./css/style.css">
<script src="../hotcss.js">script>
head>
<body>
<div id="header">普通的演示div>
<div id="container1">
这是一个普通的演示。在head头加载hotcss.js。
优雅的使用scss书写css。单位均使用了px2rem处理
div>
<div id="container2">
这个里面的字体没有使用px2rem。具体写法参看css。
如果你在使用chrome。请打开控制台,尝试模拟不同设备,来查看表现。
div>
body>
html>
//.scss
@import 'px2rem'; //第一步先把px2rem导入
$designWidth : 640; //写scss之前,必须要先定义designWidth。
body{
width: 16rem;
margin: 0 auto;
padding: 0;
}
#header{
width: px2rem(640);
height: px2rem(88);
line-height: px2rem(88);
background-color: #33aa33;
text-align: center;
font-size: px2rem(48);
color: rgba(255,255,255,1);
}
#container1{
margin-top: px2rem(10);
padding: px2rem(30);
background-color: #191919;
color: #f0f0f0;
line-height: 1.7;
max-height: 100%;
font-size: px2rem(32);
}
#container2{
margin-top: px2rem(10);
padding: px2rem(30);
background-color: #191919;
color: #f0f0f0;
line-height: 1.7;
max-height: 100%;
font-size: 12px ;
[data-dpr="2"] &{
font-size: 24px;
}
[data-dpr="3"] &{
font-size: 36px;
}
}
可以通过强制设置dpr。来关闭响应的viewport scale。使得viewport scale始终为固定值。
<meta name="hotcss" content="initial-dpr=1">
<script src="/path/to/hotcss.js">script>
通过设置该值来优化平板/PC访问体验,注意该值默认值为540。设置为0则该功能关闭。
为了配合使用该设置,请给body增加样式width:16rem;margin:0 auto;
。
<meta name="hotcss" content="max-width=640">
<script src="/path/to/hotcss.js">script>
<style>
body{
width: 16rem;
margin: 0 auto;
}
style>
通过对design-width的设置可以在本页运行的JS中直接使用hotcss.px2rem/hotcss.rem2px
方法,无需再传递第二个值。
<meta name="hotcss" content="design-width=750">
<script src="/path/to/hotcss.js">script>
用于重新计算布局,一般不需要你手动调用。
hotcss.mresize();
触发mresize的时候会执行该方法。
hotcss.callback = function(){
//your code here
}
hotcss.px2rem
和 hotcss.rem2px
。你可以预先设定可以在meta中设置hotcss.designWidth
design-width
,则之后使用这两个方法不需要再传递第二个参数。
迭代后仍然支持在js中设置hotcss.designWidth
,推荐使用meta去设置。
/**
* [px2rem px值转换为rem值]
* @param {[number]} px [需要转换的值]
* @param {[number]} designWidth [设计图的宽度尺寸]
* @return {[number]} [返回转换后的结果]
*/
hotcss.px2rem( px , designWidth );
/**
* 同上。
* 注意:因为rem可能为小数,转换后的px值有可能不是整数,需要自己手动处理。
*/
hotcss.rem2px( rem , designWidth );
//你可以在meta中定义design-width,此后使用px2rem/rem2px,就不需要传递designWidth值了。同时也支持旧的设置方式,直接在JS中设置hotcss.designWidth
hotcss.px2rem(200);
hotcss.rem2px(350);
使用rem的大站
- 手机淘宝
- 网易新闻
- 聚划算
使用hotcss的站点
- 熊猫TV
- 美丽说HIGO
- 奇虎360
- 爆米兔
- 一起作业
- TalkingData
- 电兔贷款
- 新浪show
https://github.com/imochen/hotcss
http://www.cnblogs.com/2050/p/3877280.html