【更新前言】很欣慰在看以前写的文章时能发现错误,说明自己进步了,此次更新主要是重构了文章,删去了错误的部分,更新了最近开发时遇到的问题和心得,希望以后还能有新的想法注入。如果看官有想法和建议,欢迎评论哦~
【18-01-12 更新】
* 适配问题的深坑 - 逻辑像素被改变
下文从重构时提到过一些,某耀手机具有切换“显示模式”的功能,它会主动改变手机的逻辑像素。当逻辑像素被改变时,由 rem
转换出来的 逻辑像素单位在页面上展示的效果就会发生变化。
例如:原本设置 .btn { width: 28rem; }
,大约占用手机屏幕90%的宽度效果,当显示模式设置为‘大’,即手机逻辑像素改变后,28rem
显示出占据手机屏幕约110%的现象,严重影响视觉。
【处理】在css中,有两个不常见又很好用的单位,vw
和 vh
;它表示占用视口宽度/高度的百分比,1vw表示1%视口宽度,1vh表示1%视口高度。
有人会问:为什么不直接用百分比?很好的问题,回答这个问题前,要先了解一个概念:视口.
什么是「视口」? 即移动前端经常会提到的一个概念:viewport; 指的是浏览器显示页面内容的屏幕区域。针对移动端开发非常方便。
移动端视口宽度和视口高度,就是说手机屏幕上显示内容的区域大小,根据视口大小比例设置宽高,能先保证在竖屏下按钮永远显示一种效果。对于横屏,引入media query
即可,也可以用js动态修改,会比较麻烦点。
那为什么不用流式布局的百分比? 原因很简单,百分比是相对单位,相对的是父元素的大小,如果父元素不是body
,就会受父元素的大小影响,通常情况下,我们大部分都采用的是rem设置大小,如果父元素是rem设置的单位,百分比就只能按照父元素的这个rem来计算长度,依旧不能解决逻辑像素被改变时的显示问题。
--------------孤独的根号3-----------------
* 什么是 rem,为什么用 rem
REM(font size of the root element),是指相对于根元素的字体大小的单位,是个相对单位,如果不设置根元素的字体大小,则会默认以根元素的16px的大小计算值。
说到rem,百分九十的面试官会问,rem和em区别是什么?EM(font size of the element),是指相对于父元素的字体大小单位,也是个相对单位。他们之间很类似,但为什么 em 就是火不起来?一段好的程序,讲究的是易于维护,找到元素的大小之后还要去找父元素大小才能理解真正大小,不太方便。
REM 是css3提出的,它实现了 类似 less / sass 中变量的功能,大大提高了可维护性,实现UI稿百分百的还原效果
* 基础案例
搞移动端开发的小伙伴,最头疼的事情就是适配问题, 但又不得不硬着头皮去解,改了一版又一版。其实能rem帮我们解决很多适配问题。 基础的用法:
javascript代码
<button class="btn">Rem等比例缩放</button>
css代码
html { font-size: 16px; }
.btn {
width: 8rem;
/* height: 2rem; */
line-height: 2rem;
border-radius: 0.25rem;
background-color: #0a90d3;
border-color: #0a90d3;
color: white;
}
效果展示
理解分析
根元素设置字体大小为16px; 那么,根据rem规则,width就为 8 * 16 = 128px;圆角就为0.25 * 16 = 4px;
当我们修改如下时
html { font-size: 20px; }
可以得到以下效果,整个按钮被等比例放大了:
到这里你就基本理解rem怎么使用了,但这么用,还远远不够,我们继续深入探索,如何深度还原UI稿上的效果?我们知道,UI稿的字体大小和边距等参数都是是在1080 * 1920上拟定出来的, 那我们如何计算成我们要的大小呢?
解决这个问题之前,我们要先理解 逻辑像素 和 物理像素
* 物理像素[设备像素] & 逻辑像素[CSS像素]
物理像素
物理像素,又称 设备像素,在同一个设备上,他的物理像素是固定的,设备厂商出厂是就设置好了,即,一个设备的分辨率是固定的。
逻辑像素
又称css像素,viewport中的一个小方格。我们在写css代码的时候,用的就是css像素。
像素比
物理像素和逻辑像素的比例。当比例为 1:1 时,表示1个物理像素显示一个css像素;当比例为 2:1 时,表示使用4个物理像素显示1个逻辑像素。
逻辑像素和物理像素差异:(1px逻辑像素
!= 1px物理像素
)
移动端Web App开发的小伙伴肯定有类似的遭遇,UI设计师给我们的UI稿上,字号动不动就是36px
;甚至是60px
;这要真是写到代码中,页面直接爆炸,别说页面美感了,连显示都是问题。为什么会有这问题呢?
看了上面的定义,或许你就明白了。没错,UI稿上的36px,60px,
其实是物理像素,是相对于设备分辨率下的字号,而我们要在css中填写的字号,是css像素,是相对于逻辑像素下的字号,不是同一个概念。
在Iphone 4中,物理像素是640px * 960px
;而逻辑像素是 320px * 480px
;因此,这里大概需要4个物理像素来显示一个css像素的内容。只看宽度就是两个物理像素。这也是为什么很多初级前端开发工程师直接使用 UI稿/2法 也能实现基本效果的原因。
点题: UI稿上要求设置边框为1px
,但是我们设置border: 1px solid #eee;
了之后,UI又说我们边款太粗了。有没有很想一巴掌扇死他的冲动?其实,我们设置的1px是css像素,而UI稿上指的是设备像素,原来真的比要求的粗。
* 自动化检测屏幕并设置 [根元素]
理解完 逻辑像素和物理像素后,我们可以如下实现动态检测并监听屏幕变化并实现自动化适配:
// rem布局的核心代码。 此例默认UI稿按 1080 * 1920 提供
(function(win, doc){
var docEl = doc.documentElement,
resizeEvt = 'oritationchange' in window ? 'oritationchange' : 'resize',
recalc = function(){
var clientWidth = docEl.clientWidth;
if (!clientWidth) return;
if (clientWidth < 550) {
docEl.style.fontSize = 100 * (clientWidth / 1080) + 'px';
} else {
docEl.style.fontSize = 100 * (clientWidth / 1920) + 'px';
}
if (!doc.addEventListener) return;
win.addEventListener(resizeEvt, recalc, false);
doc.addEventListener('DomContentLoaded', recalc, false);
})(window, document)
【分析】doc.addEventListener('DomContentLoaded', recalc, false);
触发在网页被载入时,计算一次根元素的长度大小,win.addEventListener(resizeEvt, recalc, false);
触发在浏览器窗口被改变时如拉动,横竖屏切换等操作时,重新计算一次根元素的长度大小。
为什么设置 clientWidth < 550
?用来甄别手机处于横竖屏什么状态。 以前我设置的是640,因为我看过一篇文章提到640px(逻辑像素)是最大安全宽度。但其实也有个问题,这是这个值宽度保证不会超过了没问题,但是当横竖屏切换时,高度就变成了宽度,有些手机的高度比640小,因此误判成手机处在竖屏状态,那么适当缩小改成550还是比较合适的。我出问题的手机是最新某耀手机,能设置 "显示模式" 为‘大’,竖屏逻辑像素只有605。
* 适配问题的处理 - 媒体查询
在基础案例中,我们已经能实现根据根元素做字号、宽高的自适应,但是这种情况也并不一定能完全符合预期。
把手机做横竖屏切换后,rem能根据手机横竖屏做出动态响应是没错,但是又有一个问题来了,如果我们想在横屏时实现不同的效果呢?比如,竖屏时底部按钮占满宽度,横屏时底部按钮只占一般宽度?
这时,我们需要引入媒体查询@media
,如下案例:
@media screen and (min-width: 550px){
btn {
width: 16rem;
height: 3rem;
}
}
注意: 设置@media
的时候,最好将其放置在css的最底部,用来覆盖其他样式
到这里,我们基本就能实现绝大部分设备的适配问题。
【发现】关于利用 rem 实现适配的典型案例,大家可以看看手机某宝的代码,你可能不会相信,手机淘宝的首页 不是用Native App实现的,而是用Web App实现的,应该是考虑到方便升级更新等问题吧。
# 简单建议:
1、meta:viewport,设置initial-scale为 1;
2、选择合适的rem比例,要方便转换,也要有实际意义
3、页面整体布局采用 REM布局方式,图片采用流体布局并设置max-width
,特殊效果采用响应式布局媒体查询。特殊效果采用弹性布局flex实现简易一些。
# 后语
本文是作者在开发时遇到问题的心得体会,如果有不对的地方,欢迎你的指正,我们一起成长。