布局方式 (上) - 总结与对比

页面布局的方式总是随着技术的更新和创意的涌现不断的更新换代。从最开始的 固定布局「Fixed」到因为宽屏设备和移动互联网的普及产生的响应式和自适应,再到「Mobile First」的设计理念,前端们在可用性和适配性上做的贡献越来越多

页面布局是css的一个重点应用

网页布局类型

常见的布局方式大致有以下几种:

  • 固定布局 【Fixed】
  • 流式布局 【Fluid】
  • rem布局 【Rem】
  • 弹性布局 【Flexbox】
  • 响应式布局 【Responsive】

1. 固定布局 [px]

  顾名思义,页面上的所有元素的尺寸一律使用px,pt这种固定单位编写,在<head>里把

<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0">

然后根据设计稿的宽高设定实现稿即可。
这种方式简单易上手,缺点显而易见,不能根据不同的屏幕做出不同的表现。例如直接设定主体宽度,然后居中展示,两边留白。宽屏两边留白多,窄屏两边留白少。

移动端使用固定布局
  1. viewport meta标签上设置content = “width=320”, 页面的各个元素也采用px作为单位。通过用JS动态修改标签的initial-scale使得页面等比例缩放, 从而刚好占满整个屏幕。这种方案类似弹性布局的rem。
  2. viewport meta标签上设置content=“width=640,user-scalable=no”,页面的各个元素采用px作为单位。由于手机浏览器的宽度小于640px(逻辑像素)(iphone plus系列为414px),所以浏览器viewport会自动缩放至全屏大小。经典案例:荔枝FM,人人都是播客的移动站

优势

  • 开发简单: 缩放交给浏览器,开发人员不需要自己操心,完全按照视觉稿切图
  • 还原精准: 绝对等比例缩放,可以精准的还原原始视觉效果(会有清晰度的失真)
  • 测试方便: 可以在pc端实现大部分的测试工作,即开发人员自己就能完成测试的大部分工作,手机端只要修改一些细节。

劣势

  • 像素丢失: 对于一些分辨率较低的手机,可能设备像素还未达到viewport指定的宽度,此时可能出现边框丢失等问题。现代手机基本不会发生这种情况。
  • 缩放失效: 早期android手机不能根据meta中的width来进行缩放,需要配合initial-scale。这种完全依赖浏览器的方式也难免可能发生异常。
  • 文本折行: 会发生在缩放失效的机型中。

2. 流式布局 [%]

 网页中主要使用百分比(%)来划分区域,往往配合max-*,min-* 等属性控制尺寸流动范围以免过大或者过小影响阅读,让不同分辨率屏幕尽可能实现自适应。
例如,设置网页主题宽度为80%,min-width为960;图片也做类似处理(例如 width:100%; max-width:图片原始尺寸,防止拉伸)。这种开发方式在早期的web开发时用来对应不同的PC屏幕(那时不会相差太大),现在也常用在移动端开发上

缺点

  • 宽度使用百分比定义,但高度和文字大小都是用px来固定,所以在大屏幕手机下的显示效果有些元素(例如width=100%)会被拉伸的很长,但是高度,文字大小还是和原来一样(即有些元素无法变得流式,显得很不协调)。

3. rem布局

REM,即font size of the root element,使用 rem 单位进行相对布局,通过根元素进行适配,它好比是一个中介,通过它计算出页面真正要展示的大小, 达到自适应效果。(有时需要配合媒体查询),常用于移动端开发。
rem避免了根据 px 布局在高分辨率下几乎无法辨认的缺点,又相对 % 百分比更加灵活,同时可以支持浏览器的字体大小调整和缩放等的正常显示。一般编写时需要对UI稿做转换,也有编辑器可以安装单位转换插件。例如 ATOMpx2rem 插件
  实现 rem 的核心,就是针对并监听不同尺寸的手机屏幕计算出相应的比例,以下代码通常写在app.js(第一个加载的js文件)最上方:

// 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 < 640) {
                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)

解释一下:

  1. 当浏览器将我们的项目下载完成时,开始解析DOM树,触发doc.addEventListener('DomContentLoaded', recalc, false),执行recalc方法,计算出当时条件下的适配比rem,执行页面渲染。当用户手机屏幕发生横竖屏翻转时,BOM捕捉到事件变化, 触发win.addEventListener(resizeEvt, recalc, false)执行recalc方法,重新计算新的适配比rem,页面响应变化。
  2. if (clientWidth < 640 ) 监控并区分用户手机屏幕是处在横竖屏什么状态下,640px是手机屏幕的最大安全宽度,超过640就认为是横屏状态。
  3. 至于缩放比是要设置 100,10 还是 24(移动端常用大小),你开心就好。 总之,计算规则就是 【 1px * 缩放比 == 1rem 】

这种布局方式编写简单,适配能力强,容易理解,易于维护。

# 但 rem也不是万能的,比如:
  1. 用户修改字体大小,使得某些元素不能展示原有效果出现换行现象。
  2. 用户修改显示模式,触发逻辑像素被改变,rem布局不能感知这种状态,会出现元素偏移的现象
  3. 当用户切换横竖屏需要展示不同效果时,rem无法实现。
    解决办法:问题1 可以配合css位置属性 或局部使用其他的的布局方式(如以下将要介绍的 flex布局)调整恢复;问题2可以使用vw/vh单位局部切换实现样式;问题3 可以使用媒体查询 或通过js动态修改样式。

4. 弹性布局 [flex]

  flex布局是W3C在2009年提出的方案。 传统的布局方式,基于盒模型,依赖于display + position + float 等属性,他们对于特殊布局非常不方便,比如垂直居中。而flex可以为盒模型提供最大的灵活性。
任何一个容器都可以指定为flex布局,包括行内布局。如果是webkit内核,必须加-webkit- 前缀

<div class="box">
    <span class="item"></span>
</div>
.box {
    -webkit-display: flex;
    display: flex
}
span {
    display: inline-flex;
}

注意:设置为Flex布局以后,子元素的float,clearvertical-align属性将会失效。

# flex基本概念: ‘容器’ - ’项目’ - ’轴’
flex布局结构示意图
  • axis】: 水平的主轴 main axis 的起始位置为 main start ,结束的位置为main end; 垂直的交叉轴 cross axis 的起始位置为 cross start,结束位置为 cross end
  • 容器flex container】和 项目flex item】: 项目在容器内部,默认沿着主轴排列,单个项目占据主轴为main size, 占据交叉轴为 cross size;容器可以简单理解为父元素,项目可以简单理解为子元素

项目其实可以理解为 子容器,对于flex,核心点只有两个:容器 和 轴


# 为容器增加属性 -【父元素】

1. flex-direction (元素排列方向),可选值:(默认)水平、水平右到左、垂直、垂直下到上

.box {  flex-direction: row | row-reserve| column | column-reserve  }

2. flex-wrap (元素的换行方式),可选值:(默认)不换行、往下换行、往上换行(第一行在下方)

.box {  flex-wrap: nowrap | wrap | wrap-reserve  }

3. flex-flow (以上两者的简写),默认为: 水平 || 不换行

.box {  flex-flow: flex-direction || flex-wrap  }
// 例
.box {  flex-flow: row nowrap;  }

4. justify-content (主轴对齐方式),可选值:(默认)左对齐、右对齐、居中、充满两边靠边、充满两边留边

.box {  justinfy-content: flex-start | flex-end | center | space-between | space-around  }

5. align-items (交叉轴对齐方式),可选值:上对齐、下对齐、中部对齐、(默认)拉伸至上下占满、首行文字的基线对齐

.box {  align-items: flex-start | flex-end | center | stretch | baseline  }

6. align-content (多根轴线的对齐方式) 可选值:上对齐、下对其、中部对齐、(默认)均匀拉伸至上下占满、充满上下中部均分、充满上下两边留空中部均分

.box {  align-content: flex-start | flex-end | center | stretch | space-between | space-around  }
# 为项目增加属性 -【子元素】

1. order (项目的排列顺序),可选值: 数值(越小越靠前,相同按文档流顺序展示)

,item {  order: <integer>;  }   /* default 0 */

2. flex-grow (项目的放大比例),可选值:数值(0表示按原大小展示;都定义为1表示所有项目均分可用空间;定义某个为2其余都为1,则2的占用1的两倍然后所有项目占满空间)

.item {  flex-grow: <number>;  }   /* default 0 */

3. flex-shrink (项目的缩小比例),可选值:数值(0表示按原大小展示;都定义为1表示空间不足时等比缩小;定义某个为0其余都为1,空间不够时0的不缩小,其余按等比缩小。负值无效)

.item {  flex-shrink: <number>;  }    /* default 1 */

4. flex-basic (项目占用主轴空间), 可选值:(根据flex-direction定义的主轴(水平或垂直)来定义项目本来的大小,跟width/height一样;如果设定跟widthheight一样的值,则项目将占据固定空间)

.item {  flex-basic: <length> | auto;  }    /* default auto */

5. flex (前三者的简写【推荐】),可选值:auto (1 1 auto) 、none (0 0 auto)、自定义值(n, n, length);auto表示空间大了自动放大,空间小了自动缩小;none表示无论空间如何,大小固定不变。推荐优先使用autonone

  /* default  0 1 auto ; 空间大了保持不变,小了自动缩小*/
.item {  flex: auto | none | [<'flex-grow'> <'flex-shrink'>? || <'flex-basic'>]  }
flex

5. align-self (某项目和其他项目不一样的对齐方式,覆盖align-item),可选值:auto(继承父元素align-items)、上对齐、下对齐、中部对齐、首行文字基线对拉伸;要区分align-items是设置在容器上的属性,align-self是设置在项目上的属性。

  /* default auto */
.item {  align-self: auto | flex-start | flex-end | center | baseline | stretch;  }

# flex 总结

flex属性树

5. 响应式布局 [media query]

  响应式布局的设计思路和其他布局思路大同小异,都是为了同一套代码适配不同媒体设备。因为由于越来越多的移动设备加入到互联网大军中,移动端将不能再被忽视,移动终端的分辨率和pc端有较大差异,为了让用户的体验更好,代码就需要兼容不同的屏幕。

  主要分两类: RWD响应式 和 AWD自适应,

  • RWD = (Responsive Web Design):media query + 流体布局 + 自适应图片/视频资源
  • AWD = (Adaptive Web Design):media query + js 操作dom + 在服务端操作dom
    这里主要介绍响应式:
      很粗糙的解释,步骤大致是:
  • 编写非响应式代码(适配主要屏幕的设备代码,遵循 Mobile First
  • 加工成响应式代码 (增加媒体查询语句)
  • 响应式细节处理,代码自测
  • 完成响应式开发
# 实现响应式布局设计具体步骤

1.布局及设置meta标签

<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="HandheldFriendly" content="true">

  主要是为了让代码适应移动端的屏幕。解释一下,viewport是指需要加载meta标签时读取的名字为‘视口’,其中,width等于设备的宽度device-width,是为了告诉浏览器,将布局视口的宽度设置为设备理想的宽度,initial-scale=1(缩放比例)保证加载的时候,要获取设备的宽度,同时要保持原始大小,便于媒体查询。user-scalable=no指定不允许用户缩放屏幕。

2. 通过媒体查询来设置响应式样式
  media query 是响应式布局的核心,他们够和浏览器进行沟通,告诉浏览器页面改如何呈现,假如要适配一个大小为414逻辑像素的屏幕(iphone6 plus),应该这么写:

我喜欢设置范围,也可以设置具体值:(max-width: 414px)
@media screen and (max-width < 500) {
    .my_class {
        font-size: 24px;
        color: #3d77e0;
    }
}

然后我们将该片段放置在样式文件的尾部,以保证覆盖非响应代码的样式。再如:假如我们要兼容iPad和iphone,应该这么设置:

@media screen and (min-width>768px) and (max-width<1024px) { }   // ipad
@media screen and (min-width>320px) and (max-width<767px) { }    // iphone

3. 字体设置-推荐rem
  在css3之前,大部分开发人员使用的都还是px,但px是绝对单位,并不能响应随着屏幕变化而变化的父容器,css3引入了rem,完美解决此问题。注意不要忘记响应变化时重置根元素的大小。

html { font-size: 24px; }
@media ( min-width>640px ) { html {font-size: 28px} body { font-size: 1.2rem } }
@media ( min-width>767px ) { html {font-size: 32px} body { font-size: 1.2rem } }
@media ( min-width>960px ) { html {font-size: 36px} body { font-size: 1.2rem } }
其实这里就是使用的rem布局的知识,不理解的建议回头看看rem布局。

4.需要注意的点
(1):宽度不能固定,可以使用百分比,即流体(fluid grids)

body { width: 100%; }
.content { width: 50%; }

(2):图片处理
  html中的图片,为了最大化让图片保持原始效果,可以如下这么设置:

.wrap img {
    width: auto;
    max-width: 100%;
    height: auto;
}

这么设计是为了:当图片小于给定空间时按原始尺寸显示,当超出时就缩小至容器大小。

  • 除了img标签的图片外我们经常会遇到背景图片,比如logo做背景,可以这么设置:
.box {
    background: url('icon/logo.png') no-repeat;
    background-size: 100% 100%;  // css3
}

background-size 是css3新增的属性,用来设置背景图片的大小,有两个可选值,表示widthheight,如果只指定一个,另一个则为auto。设置100% 100%使图片总是充满元素容器,这有可能使得图片变形。另外
background-size: cover; 表示等比扩充比例用来填满元素,这有可能使得图片内容显示不全,但不会被拉伸或挤压。
background-size: contain; 表示等比缩小比例来适应元素的尺寸,则有可能使得元素容器部分区域空白。

  • 还可以使用::before::after伪元素 + content 属性来动态显示一些内容。

6. 最后 但同样重要

  自习对比几种布局方式可以发现,各大布局并不独立,反而相辅相成,互相渗透,想单独用某种布局来实现一个产品很难,我们也没必要这么做。
  目前采用最多的是 rem布局 + 响应式布局的 media query,基本能实现较为复杂的页面效果,当然还有一些效果如垂直居中,也能引入弹性布局flex来实现;容器、图片、背景图片设置,流式布局的效果相对好一些;如果没有特殊需求,尽量还是少用绝对布局,坑太多不好维护。
  总之,遵循一个原则,结合具体需求,那种适配性高些,哪种后期维护容易些,就使用哪种。

# 最后的最后,但不重要
  我的下一篇文章: 《布局的具体设计与实现》,如果觉得我写的还不错的朋友,可以关注我,我们一起成长。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容