2

本文摘自: please call me HR

双飞翼又叫做圣杯布局。简单的说,双飞翼是中国叫法,圣杯是老外叫的。这是由: Matthew Levine 提出的。简而言之有几点注意事项:

  • 中间一栏最先渲染

  • 允许任意一栏放最上面

  • 只需一个额外 div 标签

  • 少用 HACK

实际上,强制要求就是前 3 个。他给的实现方式已经有了,我就简单的讲解一下他的实现吧。

原始双飞翼实现

完整demo,可以参考: 双飞翼布局

他的基本 HTML 为:

<div id="header">#header</div>
<div id="container">
        <div id="center" class="column">#center</div>
        <div id="left" class="column">#left</div>
        <div id="right" class="column">#right</div>
</div>
<div id="footer">#footer</div>

没啥特殊的。这里就满足了任意一栏放上面的要求。因为 container 里面包括了圣杯布局的基本实现。使用 div 来包裹他,防止影响到其他元素。
主要还是 CSS (因为这里根本就没用 JS)
header 和 footer 随便大家怎么放了,就是用到了 clear:both 清楚浮动。我们来观摩 container 里面的内容。

 #container {
        padding-left: 200px;
        padding-right: 150px;
    }
    
    #container .column {
        height: 200px;
        position: relative;
        float: left;
    }
    
    #center {
        background-color: #000;
        width: 100%;
    }
    
    #left {
        background-color: red;
        width: 200px;
        right: 200px;
        margin-left: -100%;
    }
    
    #right {
        background-color: blue;
        width: 150px;
        margin-right: -150px;
    }
    // IE6 的 HACK 可以不用管
    * html #left {  // 定位 右边 container 的盒模型位置
        left: 150px;
    }

最最重要的是前面两个。

 #container {
        padding-left: 200px;  // 定义 #left 的宽度
        padding-right: 150px;  // 定义 #right 的宽度
    }
    
    #container .column {
        height: 200px;
        position: relative;  // 设置相对布局,便于调整位置
        float: left; // 让包裹元素脱离文档流
    }

ok, 也就是说,#container 里面的属性,应该是至关整个页面布局的属性。这里,可以根据单一职责原则,再进行优化,将清除浮动这个 trick 让他本身自己实现。即:

#container::after{
        display:block;
        content:'';
        clear:both;
    }

后面,我就挑 left 来进行简单说明(这篇想说的是 flexbox)。

    #left {
        background-color: red; 
        width: 200px;
        right: 200px;
        margin-left: -100%; 
    }

主要属性就是 margin-left:-100%;,用来将自己吃掉。那么此时,看起来他的宽度就为0,这就意味着,他可以上移了。对应于 right 就是:

#right {
        background-color: blue;
        width: 150px;
        margin-right: -150px;
    }

但,这有一个问题,right 怎么会浮动到最右边呢?感觉他应该是紧挨着 left。我们现在讲 margin-right 给忽略,会得到:

holy_grail

关键的秘密在于,使用 margin-left 是不会当前层的文档流,更直观的就是,他们是在一个 Layer 上。

layer

right 跟着上移时,会根据上面那一行进行布局。so,他就会到右边去了。补充一下:
margin 为负值的 problem

  • 没有设置 width: 使用 margin 负值,会向指定方向拖动

  • 设置 width: 会让元素内部塌陷,原来的样子不变。也就是说,其他元素会覆盖到它。

基本内容就是这些,MDZZ,一个双飞翼能扯出这么多。。。怪不得很多公司都喜欢问。。。主要的考点就是,margin 负值,position,left,清除浮动。
不过,我们这里,不扯这些,我们就用 flexbox。纯原生,快速实现。

flexbox 实现

这里,再说一下要求吧:

  • 中间一列自适应

  • 左右永远固定

对比与 flex 实现方式来说,这简直 so easy。我直接上代码吧:

// HTML
<div id="header">#header</div>
<div id="container">
    <div id="left" class="column">#left</div>
    <div id="center" class="column">#center</div>
    <div id="right" class="column">#right</div>
</div>
<div id="footer">#footer</div>

// CSS
body {
        min-width: 550px;
    }
    #container{
        display: flex;
        justify-content: center;
        align-items: flex-start;
    }
    .column{
        height: 200px;
        color:white;
    }
    #center{
        flex-grow: 1;
        background-color: black;
    }
    #left{
        flex-basis: 200px;
        background-color: red;
    }
    #right{
        flex-basis: 200px;
        background-color: blue;
    }

里面主要用到的就是 justify-content 和 align-items 来进行布局设置。
具体可以参考: holy grail
那两者有没有什么差异呢? 查看了 timeLine 发现, flexbox 渲染的层数也是一层:

flexbox

ok... 这当然说明不了什么,我们具体来看一下渲染完成时间就 ok:

  • flexbox: ~80ms

  • 原始 css: ~120ms

ok, 这下你知道该用哪一个了吧。

当然 flex 实际上就为了各种各样比较 HACK 的布局方式实现的。比如,侧边栏布局,多列布局等等。

aside_layout

侧边栏布局,我就不多说了,关键是掌握 flexbox 的基本属性。直接看源码就 ok。
当然,flexbox 最适合做的就是多列布局,而且你多列的顺序可以随意切换,只要简单修改 order 就 ok。

flexbox 兼容处理

当然,说了这么多,兼容性肯定是个大问题,因为从第一版的 box 到现在 W3C 标准的 flex。中间经历了很多让人费解的阶段,那使用 flex 的时候,非要让你手动加上每一个时代的 flexbox,这估计就疯了。所以,编译 flex 的插件就出来了。我经常使用的就是 flexboxfixer。简单的说,他就是将以前的 flexbox 的tip 给加上。它源码里也很清楚的写明了 flexbox 怎样做映射。

var mappings = {
            'display': {
            valueMap: {
                'box': 'flex',
                'flexbox': 'flex',
                'inline-box': 'inline-flex',
                'inline-flexbox': 'inline-flex'
            }
},
    'box-align': {
    newName: 'align-items',
    valueMap: {
        'start': 'flex-start',
        'end': 'flex-end'
    }
},
    'flex-direction': {
    valueMap: {
        'lr': 'row',
        'rl': 'row-reverse',
        'tb': 'column',
        'bt': 'column-reverse'
    }
},
    'box-pack': {
        newName: 'justify-content',
        valueMap: {
            'start': 'flex-start',
            'end': 'flex-end',
            'justify': 'space-between'
        }
    },
    'box-ordinal-group': {
    newName: 'order',
    valueMap: {}
},
    'box-flex': {
    newName: 'flex',
    valueMap: {}
}
};

这里就不过多赘述了。用的话也很简单,直接 postcss([flexboxfixer]) 即可。放一个我常用的 gulp 配置:

gulp.task('css', () => {
    var processors = [ //这里就是中间件
        autoprefix({
            browsers: ['last 3 Safari versions', "last 2 Explorer versions", 'last 2 Explorer versions', "iOS 5"],
            cascade: true,
            remove: true
        }),
        flexpost
    ];
    return gulp.src(["xx/*.css"]) // 引入CSS
        .pipe(postcss(processors)) // 处理相关 css 
        .pipe(gulp.dest('dev/styles'));
});

villainhr
7.8k 声望2.2k 粉丝

引用和评论

0 条评论