CSS 如何实现交叉布局

1,182 阅读8分钟

随着 Web技术不断的革新,以往很多依赖于图片展示的布局能较轻易地通过代码来实现,而且现在的布局也不仅仅局限于平面的布局。实际上也有一些伪3D,甚至是3D的布局效果。这样的布局主要目的是为了增强一个立体的空间感。比如我们常见的层叠的效果,飘带等效果,如下图所示:

还有一些效果类似于下图的效果:

这样的交叉效果就是我们今天要深入聊的一个效果。

简单地分析一下

就上图这样的一个效果,看上去是一张图片穿叉在一个边框中,但一般穿叉的效果是图片在边框的下面或图片在边框的上面。但要实现上图的效果,还着实不是一件易事。那么问题了,如果你的业务中要实现上图这样的一个效果,你会怎么来做呢?

拿到这样的一个效果,一眼看上去就是两个层:

如果用两个层来处理的话,要实现我们想要的效果还是有一定的难度的。按照两层的思路来做,实现的效果是红框在图片的上面或者红框在图片的下面:

如果用代码来写的话,会很简单:

<div class="design">
    <img src="http://i.pravatar.cc/500?img=2" alt="" />
    <div class="design__border"></div>
</div>
.design {
    position: relative;
    height: 300px;
    width: 300px;
}

.design > * {
    position: absolute;
    height: 100%;
    width: 100%;
}

.design__border {
    box-sizing: border-box;
    border: 15px #eb311f solid;
    transform: rotate(45deg);
    box-shadow: 0 0 10px #eb311f, inset 0 0 20px #eb311f;
}

效果会如下所示:

Demo 地址:codepen.io/airen/full/…

这离我们想要的效果还是非常的遥远。但要实现最终的效果还是有一定的方案的。

将旋转的边框拆分成多分

就效果而言,两条边在图片上面,另外两条边在图片下面。按照这样的思路,我们可以将design__border元素的border拆分出来。可以借助::before::after来做。比如:

<div class="design">
    <img src="http://i.pravatar.cc/500?img=2" alt="">
</div>
.design {
    position: relative;
    height: 300px;
    width: 300px;
}

.design > * {
    position: absolute;
    height: 100%;
    width: 100%;
}

.design {
    &::before,
    &::after {
        content: '';
        transform: rotate(45deg);
        width: 100%;
        height: 100%;
        box-sizing: border-box;
        border: 15px solid #eb311f;
        position: absolute;
        left: 0;
        top: 0;
        filter: drop-shadow(0 0 5px #eb311f);
    }
    &::before {
        border-color: transparent #eb311f;
        z-index: 1;
    }
    img {
        z-index: 2;
    }
    &::after {
        border-color:#eb311f transparent ;
        z-index: 3;
    }
}

效果如下:

Demo 地址:codepen.io/airen/full/…

来简单地看一下其合成过程:

Demo 地址:codepen.io/airen/full/…

注意,这个效果中我们使用了filter的drop-shadow()来实现阴影效果,就该示例而言,如果继续使用box-shadow不易于控制红色边框的阴影效果

使用 CSS Masking特性

就该示例上下层穿插效果而言,用另一种话来描述的话就是部分红色边框不可见(因为他被图片给遮盖住了)。纵观CSS 中要让一个元素中部分不可见可以借助于CSS 的Masking和Clipping相关的特性。其中Masking是指CSS的mask属性,而Clipping是掉CSS的clip-path属性。

接下来我们来看看怎么借助CSS Masking中的特性实现我们想要的效果。

如果你对maskclip-path有所了解的话,就应该知道接下来要怎么做了。大概的步骤如下:

通过前三步得到我们想要的合层图的效果,然后和图片结合在一起,并做一定的旋转,就可以得到我们最终想要的一个效果。这里最为关键的是蒙层图(即mask-image),对应上图中的2,大致如下:

整个效果所用的代码并不多:

.design {
    position: relative;
    height: 300px;
    width: 300px;
}

.design > * {
    position: absolute;
    height: 100%;
    width: 100%;
}

.design__border {
    height: 300px;
    width: 300px;
    box-sizing: border-box;
    border: 15px #eb311f solid;
    transform: rotate(-45deg);
    mask: url('masking3.png') no-repeat 0 0;
}

最终的效果如下:

Demo 地址:codepen.io/airen/full/…

使用Masking较为麻烦的是mask-image制作麻烦。另外,要是你足够仔细的话,你可以看到就算代码中加入了box-shadow(或者filter:drop-shadow())也会被mask裁剪掉,如果你想让阴影能够显示出来,需要对mask-image做扩大处理。

如果你不想使用mask来做处理的话,那么你可以尝试着使用clip-path来做,要是你感兴趣的话,不仿一试。

CSS Grid和混合模式的结合

接下来这种方式是一个全新的思路。将CSS Grid布局CSS混合模式结合在一起实现。咱们先不看CSS怎么写,先来看其结果。该方案所用结构要比前面的复杂一些。整个结构分成了四个部分:

<div id=design>
    <!-- 原图 -->
    <img src=photo-1511692277506-3be3a7ab1686>

    <!-- 旋转的红色边框 -->
    <div id=rotatedBorder></div>

    <!-- 网格布局层 -->
    <div class=grid>
        <div data-white></div>
        <div></div>
        <div></div>
        <div data-white></div>
    </div>
    <div id=blend>
        <img src=photo-1511692277506-3be3a7ab1686>
        <div class=grid>
            <div></div>
            <div data-white></div>
            <div data-white></div>
            <div></div>
        </div>
    </div>
</div>

是不是复杂多了,整不明白为什么要这么写,不要紧,随着往下阅读你就能整明白。咱们先拆分一下整个效果:

其合成的过程如下图所示:

浏览器渲染的过程:

  • 最底层是img(即示例中那张鸟图),对应下图中最左侧的那个灰色部分

  • 然后旋转一个红色边框,对应下图中最左侧的那个红色边框

  • 在它们的上层盖着一个网格(即div.grid),其中左上角和右下角是单元格带有白色背景

  • 最后整了类似前面的一个副本,带有原图和另一个网格,只不过该网格右上角和左上角单元格带有白色背景,该层设置混合模式(正片叠底)

整个过程如下图所示:

下面我们分别用代码来完成这三个部分:

<!-- Step01 -->
<div class="design">
    <img src="bird-photo.jpg">
    <div class="rotated-border"></div>
</div>
.design {
    position: relative;
    height: 300px;
    width: 300px;
}

.design > * {
    position: absolute;
    height: 100%;
    width: 100%;
}

.rotated-border {
    box-sizing: border-box;
    border: 15px #eb311f solid;
    transform: rotate(45deg);
    box-shadow: 0 0 10px #eb311f, inset 0 0 20px #eb311f;
}

在前面的基础上增加第二层:

<!-- Step02 -->
<div class="design">
    <img src="bird-photo.jpg">
    <div class="rotated-border"></div>

    <!-- Grid Level -->
    <div class=grid>
        <div data-white></div>
        <div></div>
        <div></div>
        <div data-white></div>
    </div>
</div>
.grid {
    display: grid;
    grid: repeat(2, 1fr) / repeat(2, 1fr); 
}

[data-white]{
    background-color: white; 
}

在这里用到了CSS的grid相关的知识,为了节省篇幅就不做过多阐述,感兴趣的话可以点击这里了解CSS Grid相关的知识

接着是第三层,在第三层中,我们有原图和一个网格,其中不同的是网格填充白色的格子不同。代码如下:

<!-- Step03 -->
<div class="design">
    <img src="bird-photo.jpg">
    <div class="rotated-border"></div>

    <!-- Grid Level -->
    <div class=grid>
        <div data-white></div>
        <div></div>
        <div></div>
        <div data-white></div>
    </div>

    <!-- CSS Blend -->
    <div class=blend>
        <img src="https://images.unsplash.com/photo-1511692277506-3be3a7ab1686" alt="鸟图" />
        <div class=grid>
            <div></div>
            <div data-white></div>
            <div data-white></div>
            <div></div>
        </div>
    </div>
</div>
.blend > * {
    position: absolute;
    height: 100%;
    width: 100%;
}

这个时候在混合层上添加CSS混合模式,采用正片叠底的效果:

.blend {
    mix-blend-mode: multiply; 
}

现在我们只需要把三个层合在一起,即可。先来将第一步和第二步两层合起来:

接下来是混合层要起的作用了,怎么让第二层网格左上角和右下角能显示图片。还记得在混合层也用了一张图?这也是关键所在,先将第三层合在一起,但暂时将混合样式禁用一下:

开启混合模式样式:

.blend {
    mix-blend-mode: multiply; 
}

效果就出来了:

Demo 地址:codepen.io/airen/full/…

该方法相比以前面几种方案而言,更为灵活,只是HTML结构更为复杂。对于CSS混合模式的特性之强大之处,有兴趣的话建议阅读一些这方面的文章。你甚至还可以将 CSS maskCSS 自定义属性相结合,实现其他的交叉布局效果:

Demo 地址:codepen.io/airen/detai…

如果你对这个示例效果感兴趣的话,可以移步阅读《SVG 遮罩》!

另外,CSS Grid在布局方面特别的灵活,只要发挥你的创意,他都能非常灵活的帮助你实现比如@Andy Barefoot的示例,实现一个3D的布局效果:

Demo 地址:codepen.io/andybarefoo…

小结

这篇文章主要使用不同的方式来实现穿叉布局效果。同样的一个效果虽然可以使用不同的方式实现,但不管是哪种方式都有相应的局限性。要实现一个效果并不难,难是创新。好比最后一种方案,采用最新的CSS Grid布局和CSS混合模式的结合,让我们实现了这种带边框的穿叉效果。

当然,你可能还有别的方案更具创意,如果有兴趣的话不妨发挥发挥,用自己的方式实现同样的一个效果。同时欢迎您能在下面的评论中与我们一起分享。


damo.gif

如果你觉得该教程对你有所帮助,请给我点个赞。要是你喜欢 CSS ,或者想进一步了解和掌握 CSS 相关的知识,请关注我的专栏,或者移步阅读下面这些系列教程: