草庐IT

那些你不知道的 CSS 自定义形状网格布局

南城FE 2023-03-28 原文
本文翻译自 CSS Grid and Custom Shapes, Part 1,略有删改

在正常的开发中,我们会遇到很多元素块排列对齐的需求,如九宫格抽奖,多张图片上传后等分布局预览,微信朋友圈多张图片展示等。这都是正常的正方形很规整的布局。

如下所示,如果图像不是完全正方形,而是形状像六边形或菱形怎么办?我们怎么做呢。事实上,我们将结合我们已经研究过的 CSS 网格技术,并加入一些 CSS clip-pathmask魔法,为您可以想象的任何形状创建精美的图像网格!

相同的HTML

我们将要研究的大多数布局乍一看似乎很容易实现,但具有挑战性的部分是使用相同的 HTML 标记来实现它们。我们可以使用很多包装器、divs 等等,但这篇文章的目标是使用相同且最少的 HTML 代码,并且仍然可以实现我们想要的所有不同风格的网格。

这就是说,让我们从以下的HTML开始:

<div class="gallery"> <img src="..." alt="..."> <img src="..." alt="..."> <img src="..." alt="..."> <img src="..." alt="..."> <!-- as many times as we want --> </div> 一个带有图像的容器就是我们在这里所需要的。足已!

六边形 CSS 网格

这个形状有时也称为蜂窝网格。

首先,我们使用clip-path在图像上使用来创建六边形形状,并将它们全部放在同一个网格区域中,以便它们重叠。

.gallery { --s: 150px; /* controls the size */ display: grid; } .gallery > img { grid-area: 1/1; width: var(--s); aspect-ratio: 1.15; object-fit: cover; clip-path: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0 50%); }

此时所有的图像都是六边形并且重叠在一起。所以看起来我们只有一个六边形的图像元素,但实际上有七个。下一步将把图像平移到它们正确放置的网格上。

保留其中一张图像在中心位置。其余图像使用 CSS translate 平移在它周围。这是我为网格中的每个图像提出的模拟公式:

translate((height + gap)*sin(0deg), (height + gap)*cos(0)) translate((height + gap)*sin(60deg), (height + gap)*cos(60deg)) translate((height + gap)*sin(120deg), (height + gap)*cos(120deg)) translate((height + gap)*sin(180deg), (height + gap)*cos(180deg)) translate((height + gap)*sin(240deg), (height + gap)*cos(240deg)) translate((height + gap)*sin(300deg), (height + gap)*cos(300deg)) 经过一些计算和优化后,我们得到以下最终 CSS

.gallery { --s: 150px; /* control the size */ --g: 10px; /* control the gap */ display: grid; } .gallery > img { grid-area: 1/1; width: var(--s); aspect-ratio: 1.15; object-fit: cover; clip-path: polygon(25% 0%, 75% 0%, 100% 50% ,75% 100%, 25% 100%, 0 50%); transform: translate(var(--_x,0), var(--_y,0)); } .gallery > img:nth-child(1) { --_y: calc(-100% - var(--g)); } .gallery > img:nth-child(7) { --_y: calc( 100% + var(--g)); } .gallery > img:nth-child(3), .gallery > img:nth-child(5) { --_x: calc(-75% - .87*var(--g)); } .gallery > img:nth-child(4), .gallery > img:nth-child(6) { --_x: calc( 75% + .87*var(--g)); } .gallery > img:nth-child(3), .gallery > img:nth-child(4) { --_y: calc(-50% - .5*var(--g)); } .gallery > img:nth-child(5), .gallery > img:nth-child(6) { --_y: calc( 50% + .5*var(--g)); } 每个图像都由基于这些公式的--_x和变量转换。--_y只有第二张图片 ( nth-child(2)) 在任何选择器中未定义,因为它位于中心。如果您决定使用不同的顺序,它可以是任何图像。这是我使用的顺序:

只需几行代码,我们就得到了一个很酷的图像网格。为此,我在图像上添加了悬停的效果,让交互效果更上一层楼,代码在线预览如下:

https://code.juejin.cn/pen/7132450107156332552

CSS 菱形网格

菱形是将一个正方形旋转45度。

还是相同的 HTML,我们首先在 CSS 中定义一个 2×2 的图像网格:

.gallery { --s: 150px; /* controls the size */ display: grid; gap: 10px; grid: auto-flow var(--s) / repeat(2, var(--s)); place-items: center; } .gallery > img { width: 100%; aspect-ratio: 1; object-fit: cover; } 然后设置旋转,请注意我如何将它们都旋转45deg,但方向相反。

.gallery { transform: rotate(45deg); } .gallery > img { transform: rotate(-45deg); } 向负方向旋转图像可防止它们与网格一起旋转,因此它们保持笔直。现在,我们应用 clip-path 从它们中剪出菱形。

此时的图像并没有按我们的预期的间距排列,我们需要纠正图像的大小以使它们适合在一起。否则,它们的间距会很远,以至于看起来不像图像网格。

图像在绿色圆圈的边界内,即放置图像的网格区域的内切圆。我们想要的是将图像放大以适合红色圆圈,即网格区域的外接圆。

.gallery > img { width: 141%; /* 100%*sqrt(2) = 141% */ aspect-ratio: 1; object-fit: cover; transform: rotate(-45deg); clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%); } 最后,还是给图像增加悬停的效果,在线代码如下: https://code.juejin.cn/pen/7132464081167974434

三角形的 CSS 网格

你现在可能知道,最大的窍门是找出clip-path我们想要的形状。对于这个网格,每个元素都有自己的clip-path值,而最后两个网格使用一致的形状。所以,这一次,就像我们正在处理几个不同的三角形形状,它们组合在一起形成一个矩形的图像网格。

我们使用以下 CSS 将它们放置在 3×2 网格中:

.gallery { display: grid; gap: 10px; grid-template-columns: auto auto auto; /* 3 columns */ place-items: center; } .gallery > img { width: 200px; /* controls the size */ aspect-ratio: 1; object-fit: cover; } /* the clip-path values */ .gallery > img:nth-child(1) { clip-path: polygon(0 0, 50% 0, 100% 100% ,0 100%); } .gallery > img:nth-child(2) { clip-path: polygon(0 0, 100% 0, 50% 100%); } .gallery > img:nth-child(3) { clip-path: polygon(50% 0, 100% 0, 100% 100%, 0 100%); } .gallery > img:nth-child(4) { clip-path: polygon(0 0, 100% 0, 50% 100%, 0 100%); } .gallery > img:nth-child(5) { clip-path: polygon(50% 0, 100% 100%, 0% 100%); } .gallery > img:nth-child(6) { clip-path: polygon(0 0, 100% 0 ,100% 100%, 50% 100%); } } 最终得到的效果如下图所示:

最后一点是使中间列的宽度等于0消除图像之间的空间。我们在菱形网格中遇到了同样的间距问题,但对我们使用的形状采用了不同的方法:

grid-template-columns: auto 0 auto; 最终的在线代码如下:

https://code.juejin.cn/pen/7132468930584510502

比萨形状的 CSS 网格

基于上面的三角形网格通过添加简单的border-radius和overflow就可以实现另一个很酷的网格,比萨形状的 CSS 网格。

拼图风格的 CSS 网格

这次我们将使用 CSS mask 属性来使图像看起来像拼图。

现在设置网格应该是小菜一碟,所以让我们把注意力集中在mask上。我们需要两个渐变来创建最终的拼图形状。一个渐变创建一个圆形(绿色部分),另一个渐变创建红色区域并填充半圆白色区域。

--g: 6px; /* controls the gap */ --r: 42px; /* control the circular shapes */ background: radial-gradient(var(--r) at left 50% bottom var(--r), green 95%, #0000), radial-gradient(calc(var(--r) + var(--g)) at calc(100% + var(--g)) 50%, #0000 95%, red) top/100% calc(100% - var(--r)) no-repeat; 两个变量控制形状。--g变量控制网格间隙,相对不是最重要的。重要的是考虑间隙之间如何正确放置我们的圆圈,以便在组装整个拼图时它们完美重叠。该--r变量则控制拼图形状的圆形部分的大小。

然后我们使用相同的 CSS 值并针对不同的位置稍加调整来创建其他三个形状:

此时整体拼图形状好了,但没有按我们的预期重叠在一起。因为每个图像都被限制在它所在的网格单元中,所以现在形状有点混乱是对的: 我们需要通过增加图像的高度/宽度来创建溢出。从上图中,我们必须增加第一个和第四个图像的高度,同时增加第二个和第三个图像的宽度。您可能已经猜到我们需要使用--r变量来增加它们。

.gallery > img:is(:nth-child(1),:nth-child(4)) { width: 100%; height: calc(100% + var(--r)); } .gallery > img:is(:nth-child(2),:nth-child(3)) { height: 100%; width: calc(100% + var(--r)); } 此时左边两张图片按预期展示了,但默认情况下,我们的图像要么在右侧(如果我们增加宽度)重叠,要么在底部(如果我们增加高度)重叠。但这不是我们想要的第二张和第四张图片。解决方法是在这两个图像上使用place-self: end,最后我们的完整代码如下:

https://code.juejin.cn/pen/7132472038211452942

最后来一个不一样的,为保障gif图加载速度,我将图片替换成纯色图像,这次我们使用clip-path,因为它是我们可以动画的属性,我们只需更新控制形状的自定义属性即可获得很酷的悬停效果。我们设置一个自定义变量控制默认的三角形的角度,在鼠标悬停时设置该变量为0则回到正常的正方形,效果图如下:

在线代码如下: https://code.juejin.cn/pen/7133195476844675103

最后

本文通过将我们已经了解的有关 CSS Grid 的知识与一些附加clip-path的mask魔法相结合,我们能够制作不同形状的网格布局。而且我们每次都使用相同的 HTML 代码!代码本身只不过是一个包含少量图像元素的容器!看完是不是觉得很简单很神奇呢,有兴趣的同学可以自己试试看,兴许你能创造出更多有趣的网格图形。

看到最后如果觉得有用,记得点个赞收藏起来吧,说不定哪天就用上啦。

专注前端开发,分享前端相关技术干货,公众号:南城大前端(ID: nanchengfe)

有关那些你不知道的 CSS 自定义形状网格布局的更多相关文章

  1. ruby - Facter::Util::Uptime:Module 的未定义方法 get_uptime (NoMethodError) - 2

    我正在尝试设置一个puppet节点,但ruby​​gems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由ruby​​gems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby

  2. ruby - capybara field.has_css?匹配器 - 2

    我在MiniTest::Spec和Capybara中使用以下规范:find_field('Email').must_have_css('[autofocus]')检查名为“电子邮件”的字段是否具有autofocus属性。doc说如下:has_css?(path,options={})ChecksifagivenCSSselectorisonthepageorcurrentnode.据我了解,字段“Email”是一个节点,因此调用must_have_css绝对有效!我做错了什么? 最佳答案 通过JonasNicklas得到了答案:No

  3. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

    我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

  4. ruby-on-rails - form_for 中不在模型中的自定义字段 - 2

    我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢

  5. ruby - 主要 :Object when running build from sublime 的未定义方法 `require_relative' - 2

    我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby​​1.9+ 关于ruby-主要:Objectwhenrun

  6. ruby - 在 Ruby 中有条件地定义函数 - 2

    我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin

  7. ruby - 定义方法参数的条件 - 2

    我有一个只接受一个参数的方法:defmy_method(number)end如果使用number调用方法,我该如何引发错误??通常,我如何定义方法参数的条件?比如我想在调用的时候报错:my_method(1) 最佳答案 您可以添加guard在函数的开头,如果参数无效则引发异常。例如:defmy_method(number)failArgumentError,"Inputshouldbegreaterthanorequalto2"ifnumbereputse.messageend#=>Inputshouldbegreaterthano

  8. ruby - 如何在 Grape 中定义哈希数组? - 2

    我使用Ember作为我的前端和GrapeAPI来为我的API提供服务。前端发送类似:{"service"=>{"name"=>"Name","duration"=>"30","user"=>nil,"organization"=>"org","category"=>nil,"description"=>"description","disabled"=>true,"color"=>nil,"availabilities"=>[{"day"=>"Saturday","enabled"=>false,"timeSlots"=>[{"startAt"=>"09:00AM","endAt"=>

  9. ruby - 获取模块中定义的所有常量的值 - 2

    我想获取模块中定义的所有常量的值:moduleLettersA='apple'.freezeB='boy'.freezeendconstants给了我常量的名字:Letters.constants(false)#=>[:A,:B]如何获取它们的值的数组,即["apple","boy"]? 最佳答案 为了做到这一点,请使用mapLetters.constants(false).map&Letters.method(:const_get)这将返回["a","b"]第二种方式:Letters.constants(false).map{|c

  10. ruby - 这两个 Ruby 类初始化定义有什么区别? - 2

    我正在阅读一本关于Ruby的书,作者在编写类初始化定义时使用的形式与他在本书前几节中使用的形式略有不同。它看起来像这样:classTicketattr_accessor:venue,:datedefinitialize(venue,date)self.venue=venueself.date=dateendend在本书的前几节中,它的定义如下:classTicketattr_accessor:venue,:datedefinitialize(venue,date)@venue=venue@date=dateendend在第一个示例中使用setter方法与在第二个示例中使用实例变量之间是

随机推荐