前端开发者学堂 - fedev.cn

CSS如何实现交叉布局

发布于 大漠

随着Web技术不断的革新,以往很多依赖于图片展示的布局能较轻易地通过代码来实现,而且现在的布局也不仅仅局限于平面的布局。实际上也有一些伪3D,甚至是3D的布局效果。前两天看到@Preethi分享的一个效果就是我们平时难于用代码实现的一个效果。就这样的效果放到今天来说,我们通过CSS可以实现,而且这样的一个效果是一个典型的效果,所以今天和大家一起来探讨这样的一个布局效果。

刚才提到借助于Web的一些特性,我们可以实现伪3D的效果(看上去像上一个3D的布局)。这样的布局主要目的是为了增强一个立体的空间感。比如我们常见的层叠的效果,飘带等效果,如下图所示:

还有一些效果类似于@Preethi提到的效果:

这样的交叉效果就是我们今天要深入聊的一个效果。也就是说,我们今天的目标是要和大家一起实现@Preethi在《Weaving One Element Over and Under Another Element》一文中提到的效果:

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

简单地分析一下

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

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

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

<!-- HTML -->
<div class="design">
    <img src="https://static.fedev.cn/sites/default/files/blogs/2019/1910/photo-1511692277506-3be3a7ab1686.jpeg" alt="">
    <div class="design__border"></div>
</div>

// CSS
.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;
}

效果会如下所示:

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

将旋转的边框拆分成多分

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

<!-- HTML -->
<div class="design">
    <img src="https://static.fedev.cn/sites/default/files/blogs/2019/1910/photo-1511692277506-3be3a7ab1686.jpeg" alt="">
</div>

// CSS
.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;
    }
}

效果如下:

来简单地看一下其合层过程

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

如果你对filter相关的知识感兴趣的话,可以点击这里进行了解或者阅读@ChokCoco的《你所不知道的 CSS 阴影技巧与细节》一文了解阴影实现的一些细节。

使用CSS Masking特性

就该示例上下层穿叉效果而言,用另一种话来描述的话就是部分红色边框不可见(因为他被图片给遮盖住了)。纵观CSS中要让一个元素中部分不可见可以借助于CSS Masking Module Level 1规范中的MaskingClipping相关的特性。其中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('https://static.fedev.cn/sites/default/files/blogs/2019/1910/masking3.png') no-repeat 0 0;
}

最终的效果如下:

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

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

CSS Grid和混合模式的结合

接下来这种方式是@Preethi在《Weaving One Element Over and Under Another Element》一文中所介绍的方法。该方法是一个全新的思路。

CSS Grid布局CSS混合模式结合在一起实现。

先直接上效果:

咱们先不看CSS怎么写,先来看其结果。该方案所用结构要比前面的复杂一些:

整个结构分成了四个部分

<!-- HTML -->
<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>

    <!-- CSS混合模式层 -->
    <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>

// CSS

.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>

// CSS
.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>

// CSS
.blend > * {
    position: absolute;
    height: 100%;
    width: 100%;
}

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

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

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

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

开启混合模式样式:

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

效果就出来了:

其中奥秘不做过多阐述,对于最后一种使用Grid和混合模式实现上例效果的详细介绍可以阅读@Preethi的《Weaving One Element Over and Under Another Element》一文

该方法相比以前面几种方案而言,更为灵活,只是HTML结构更为复杂。对于CSS混合模式的特性之强大之处,有兴趣的话建议阅读一些这方面的文章。这里提一下,CSS Grid在布局方面特别的灵活,只要发挥你的创意,他都能非常灵活的帮助你实现。比如@Andy Barefoot的示例,实现一个3D的布局效果:

小结

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

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