在CSS中构建六边形网格

2020-06-20 04:15:41

我一直在追求使用(滥用?)的想法。CSS网格,构建一个互连的六边形网格,其中每个六边形无缝连接在一起。这方面的一个例子是很多桌面战争游戏,一些棋盘游戏(例如,卡坦的定居者)和一些电脑游戏(我曾经玩过韦斯诺斯战役,它使用的是这样的系统)。

必须互连。这意味着各个项目有一定程度的重叠,因为我们重新将一个六边形安装到一个方孔中。

必须是灵活的。我需要能够把一件新的东西扔进去,然后让它正常工作。

一定是有反应的。在较大的屏幕上,从5英寸开始的网格应该在较小的屏幕上降到1到2英寸。

我将一步一步地分析这是如何工作的,但这是我能想出的最终结果。这个网站的文章列表实际上也使用了这个版本。

让我们分析一下这段代码的html是什么样子,然后我们将了解网格是如何设置的。其中一个要求是灵活性,部分要求是确保不涉及太多复杂的标记。另外,CSS网格要求我们有相当平面的标记,因为现在我们所有的网格项都必须是兄弟项。子网格将使这不是一个要求,但它还没有完全在那里,这被证明不是一个问题。

如你所见,我们这里有一个非常标准的清单。我们确实需要一个额外的内容<;div>;才能实现我们想要的外观;这是因为需要强制这些六边形的纵横比,当我们进入CSS时,您将会看到这一点。(=。如果你对CSS中的强制比例很好奇,这是一篇不错的文章。

好了,让我们来了解一下这个疯狂的网格系统是如何工作的。我在这方面的第一次尝试是试图找出一个自动放置方案,但很快就成了死胡同,因为我意识到我的一个要求,即相互连接的六边形,将阻止任何形式的自动放置,至少从我对网格的理解来看是这样。CSS网格非常有趣,因为它们真的不在乎是否有一个元素已经占用了您告诉它们去的空间,所以如果您告诉多个元素占用同一个网格单元格,它们会很乐意这样做,但是您必须明确地告诉网格您需要重叠哪些元素,所以自动放置是不可行的。一旦我弄清楚了这一点,我就开始弄清楚我的网格需要实际看起来是什么样子。事实证明,这比我预期的更具挑战性。或者,也许我只是数学不好。我想可能就是这样了。

首先,我们需要将列表转换为网格。:)另外,由于我的标记是列表,我还将删除默认的列表样式。

弄清楚我的行需要看起来是什么样子实际上是相当容易的。如果你看这个六边形,你会发现顶部和底部都是平的:

这一点,再加上六边形在y轴上是镜像的,这意味着我们不必做任何特殊的事情来设置我们的行;当我们开始将单独的物品放入正确的空间时,所有这些都会自动处理。

柱子更难弄清楚。再看一下六边形,我最初的想法是,我应该可以用一个网格,每个单独的项目跨越3列。中心是一列,每边也将占据一列:

方便的是,六边形的边正好是中心宽度的一半,这意味着当我们为这个网格设置列时,我们的网格列应该看起来像1 2 1。我们也将使用fr单元,这样我们就可以缩放容器,并使网格弹性与之匹配。表示这一点的一种非常简单的方法是只写出我需要的列,在本例中,我每行有5个六边形:

.hex-grid__list{显示:网格;列表样式-类型:无;页边距:0;填充:0;网格模板-列:1FR 2fr 1FR 2fr 1FR 2fr 1FR 2fr 1FR 2fr 1FR;}。

我想重写这段代码以使用Repeat()函数,但其中一个有趣的问题是,由于我们的单个网格项需要占用奇数的列(3),因此我们的网格作为一个整体也需要是一个奇数。要做到这一点,我要建立一个重复,只是重复1FR的2FR部分,然后附加一个1FR的末端。“。我还将设置一个CSS变量,该变量将控制此网格每行的六边形数量。这是很好的测试,而且当我们开始响应时也会派上用场。

.hex-grid__list{--数量:5;显示:网格;列表样式类型:无;页边距:0;填充:0;网格模板列:重复(var(--数量),1fr,2fr)1fr;}

这是我们到目前为止得到的,我添加了一些颜色,这样我们就可以直观地看到我们到目前为止所处的位置。

好的,现在我们需要开始将物品放到这个网格上。我们的单个项目需要跨越3列和2行,因此我们将从添加Grid-Column:1/span 3开始。这将定位每个项目,因此它从第一列开始,跨越3列。然后添加GRID-ROW:1/SPAN 2,这使项目跨度为2行。现在,我们有了横跨所需的行数和列数的各个项目,我继续并开始添加一些我们还需要的附加视觉样式,为列表项引入比率调整,然后将内容定位在该列表项中。

.hex-grid__item{位置:相对;网格列:1/span 3;网格行:1/span 2;高度:0;填充底部:100%;}.十六进制网格__内容{位置:绝对;左侧:0;顶部:0;高度:100%;宽度:100%;背景颜色:白色;}。

这很酷,但绝对不是网格。现在还不行。:)我们将使用一些疯狂的:nth-of-type选择器来构建我们的行和列规则。问题的根源在于,我们需要更新网格列和网格行规则的起始位置。我们将从行开始,因为这样更容易解释,而且列在顶部的层次也很好。我们将向列表中添加一个计数器变量,然后在每次移动到新行时递增该变量。然后,我们将更新网格行规则,将行位置设置为计数器的两倍,因为网格项所在的每个行实际上是两个网格行。

这就是我遇到第一期的地方。没有一种很好的自动方法来跟踪我们在哪一行,我可以弄清楚。我最初的想法是可以设置一个:nth-of-type规则,比如:nth-of-type(5n+1),它递增我的计数器变量,但据我所知,使用自身重新赋值变量并不起作用,或者可能是:nth-of-type没有正确更新变量的问题。不管怎样,那都过时了。我还研究了使用CSS计数器,但我们需要将这些数字作为实际数字,并将计数器输出作为字符串。我最终放弃了,重新使用了一系列:nth-of-type(n+<;mount>;)。不幸的是,这意味着它不像我希望的那样灵活,因为您必须设置固定数量的行,之后网格将停止正常运行。

这就是说,带着一点粗鲁的循环,我可以去了,我已经设置了我的生成20行,因为我可能永远不会使用它的任何更多的东西,如果是这样,它是一个快速的调整。我已经设置了我的循环生成20行,因为我可能永远不会使用这个更多的东西,如果是这样,它是一个快速的调整。

这就是我们目前的情况。我还包括了一些原始的CSS片段,这样您就可以看到sass循环编译成什么了。

$Amount:5;.hex-grid__list{--金额:5;--计数器:1;显示:网格;列表样式类型:无;页边距:0;填充:0;网格模板列:Repeat(var(--mount),1fr,2fr)1FR;}.hex-grid__item{位置:相对;网格列:1/span 3;GRID-ROW:Calc(var(--Counter)+var(--Counter))/span 2;高度:0;填充-底部:100%;@$i从1到20{&;:第n个类型(n+#{$i*$Amount+1}){--计数器:#{$i+1};}.hex-grid__content{position:Absolute;Left:0;top:0;高度:100%;宽度:100%;背景颜色:白色;}

.hex-grid__item:第n个类型(n+6){--计数器:2;}.hex-grid__item:第n个类型(n+11){--计数器:3;}.hex-grid__item:第n个类型(n+16){--计数器:4;}/*.等*/。

快到了,然后我们就可以开始把它弄漂亮了!定位这一点的最后一步是设置列的规则。我们将再次使用:nth-of-type来完成此操作,但这一次我们需要为每个项目列设置一个规则(每个列都是3个网格列,但它们会相互联锁。?是的,这很复杂)。我们要找的大概是:nth-of-type(<;Amount>;n+<;column>;),因此我们需要:nth-of-type(5N+1)到:nth-of-type(5N+5)。我选择再次将其放入sass循环中,这样我们就不会编写太多CSS,给出以下内容:

$Amount:5;.hex-grid__list{--金额:5;--计数器:1;显示:网格;列表样式类型:无;页边距:0;填充:0;网格模板列:Repeat(var(--mount),1fr,2fr)1FR;}.hex-grid__item{位置:相对;网格列:1/span 3;GRID-ROW:Calc(var(--Counter)+var(--Counter))/span 2;高度:0;填充-底部:100%;//$i的列@,从1到$Amount{&;:第n个类型(#{$Amount}n+#{$i}){Grid-Column:#{$i+$i-1}/span 3;@if$i%2==0{grid-row:calc(var(--counter)+var(--counter)-1)/span 2;}//$i的行@,从1到20{&;:第n个类型(n+#{$i*$Amount+1}){--计数器:#{$i+1};}.hex-grid_content{位置:绝对;左:0;顶部:0;高度:100%;宽度:100%;背景色:白色;}。

您将注意到的最后一件事是,在我的COLUMN循环中,我正在修改偶数列以将它们的行号偏移-1。通过将每隔一列向上移动到前一行的中间,这为我们提供了我们想要的联锁模式。

现在,为了让它看起来像一个六边形,让我们继续在我们的内容中添加一个剪辑路径。我从多边形开始计算出剪辑路径,然后玩弄这些值,直到得到我想要的结果。我还添加了一些填充,以保持我们的内容流畅。我尝试了一下CSS形状,希望能让内容留在蒙版内部,但无法让形状与外部的任何东西一起工作。我非常肯定如果/当内部形状存在时,这将是完美的。填充效果相当好,所以我并不太担心。我还略微调低了比例,因为这个六边形实际上不是一个完美的正方形,它实际上是9:10的比例。

.hex-grid__item{/*截取*/高度:0;ppad-Bottom:90%;}.hex-grid__content{位置:绝对;左侧:0;顶部:0;高度:100%;宽度:100%;背景色:白色;剪辑路径:多边形(75%0,100%50%,75%100%,25%100%,050%,25%0);填充:2rem 25%;}。

添加檐槽就像向栅格添加栅格间隙一样简单。您实际上会希望将行间距设置为您想要的间距,然后将列间距设置为双倍。我不完全确定为什么会这样,但我想这是因为数学是如何计算出倾斜的间隙的吧?谁在乎呢,它管用,对吧?:)。

.hex-grid__list{--数量:5;--计数器:1;显示:网格;列表样式类型:无;页边距:0;填充:0;网格模板列:重复(var(--数量),1fr,2fr)1FR;网格间距:1rem 2rem;}

差不多就是这样了!使其具有响应性与设置媒体查询一样简单,这样屏幕宽度越小,每行的列数就越少。我把这项工作所需的部分变成了一种sass混合,并用它来构建我的媒体查询。完整的代码包含在下面!

$block:';.hex-grid';;@Mixin网格项($Amount){@表示$i从1到$Amount{&;:第n个类型(#{$Amount}n+#{$i}){网格列:#{$i+$i-1}/span 3;@if$I%2==0{网格行:Calc(var(-计数器)+var(--计数器)-1)/span 2;}@for$i,从1到20{&;:第n个类型(n+#{$i*$Amount+1}){--计数器:#{$i+1};}#{$block}{display:flex;JUSIGN-CONTENT:CENTER;&;__LIST{--Amount:5;位置:相对;填充:0;边距:0;列表样式类型:无;显示:网格;网格模板列:REPEAT(var(--Amount),1FR 2fr)1FR;网格间距:2.5rem 5rem;}&;__item{位置:相对;网格列:1/span 3;网格行:Calc(var(--计数器)+var(--计数器))/span 2;过滤器:投射阴影(0 10px RGBA(#444,0.08));高度:0;填充底部:90%。}&;__Content{位置:绝对;高度:100%;宽度:100%;字体大小:1.125rem;颜色:#111111;背景颜色:白色;剪辑路径:多边形(75%0,100%50%,75%100%,25%100%,050%,25%0);显示:弯曲;弯曲方向:列;对齐内容:居中;对齐项目:居中;填充:2rem 25%0;文本装饰:无;文本对齐:居中;过渡:转换.24s缓出;}}@媒体屏幕和(最小宽度:1440px){#{$block}{&;__list{--金额:5;--计数器:1;}&;__Item{@include Grid-Item(5);}@媒体屏幕和(最小宽度:1120px)和(最大宽度:1439px){#{$block}{&;__list{--金额:4;--计数器:1;}&;__Item{@include Grid-Item(4);}@媒体屏幕和(最小宽度:840px)和(最大宽度:1119px){#{$block}{&;__list{--数量:3;--计数器:1;网格间距:1.5rem 3rem;}&;__Item{@include Grid-Item(3);}@媒体屏幕和(最小宽度:480px)和(最大宽度:839px){#{$block}{&;__list{--数量:2;--计数器:1;栅格间隙:1.5rem 3rem;}&;__item{@include grid-item(2);}@媒体屏幕和(最大宽度:479px){#{$block}{&;__list{--数量:1;网格间距:1.5rem 3rem;}