让我向您展示我最近发现的一种方法,它可以将一堆元素围绕我称之为 **枢轴点** 的点进行居中。我向您保证,这种方法无需使用奇特的 HTML,也不需要了解任何前沿的 CSS 即可完成。
我非常喜欢文字游戏,所以最近我重新设计了我的网站主菜单,以致敬纵横填字游戏,我的名字作为垂直单词,网站的主要部分横向排列。
以下是使用一些颜色的名称进行的设计效果

这是一个驱动此拼图的 HTML 示例
<div class="puzzle">
<div class="word">
<span class="letter">i</span>
<span class="letter">n</span>
<span class="letter">d</span>
<span class="letter">i</span>
<span class="letter pivot">g</span>
<span class="letter">o</span>
</div>
<!-- MORE WORDS -->
</div>
在此示例中,字母 g
是枢轴点。请注意它不在中间位置?这就是这个挑战的妙处。
我们可以使用硬编码 CSS 或内联自定义属性为每个单词应用偏移量,然后离开。这当然获得了解决问题的最显而易见方法的奖项,但也有一个缺点——除了 .pivot
类之外,我们还必须为每个单词指定一个偏移量。我脑海中的声音告诉我,这增加了不必要的冗余,灵活性较差,并且每次添加或更改单词时都需要额外的负担,而我们并不需要。
让我们退一步,看看没有平衡的情况下拼图是什么样子

想象一下,我们使用 display: none
隐藏枢轴点之前的所有字母;现在我们只能看到枢轴点及其后面的所有内容

无需进一步更改,我们的枢轴点就已经对齐了。但是我们丢失了单词的开头,当我们重新引入隐藏的部分时,每个单词都会向右推,所有内容都乱了套。
如果我们改为隐藏后面的字母,枢轴点仍然不对齐

所有这些来回似乎有点毫无意义,但它揭示了我的问题中的对称性。如果我们使用从右到左 (RTL) 的阅读方案,我们将遇到相反的问题——我们将能够解决右侧,但左侧将完全错误。
如果有一种方法可以同时让两侧对齐,那不是很好吗?
事实上,确实有。
鉴于我们已经有一半的解决方案,让我们借鉴算法中的一个概念,称为 *分而治之*。其基本思想是,我们可以将问题分解成几个部分,并且通过找到部分的解决方案,我们将找到整个问题的解决方案。
在这种情况下,让我们将问题分解成两个部分的位置。首先是“头部”或枢轴点之前的所有内容。

接下来是“尾部”,它是枢轴点加上其后的所有内容。

flex
显示类型将在这里帮助我们;如果您不熟悉它,flex
是一个 用于在一维空间中定位元素的框架。这里的技巧是利用容器的左右两端来强制对齐。为了使其工作,我们将使用比头部更小的 order
属性值来交换头部和尾部部分。order 属性由 flex 用于确定灵活布局中元素的顺序。较小的数字放置在流程的前面。
为了在没有任何额外 HTML 的情况下区分头部和尾部元素,我们可以将样式应用于头部部分的所有字母,然后利用 CSS 的级联特性,使用后续同级选择器 .pivot ~ .letter
覆盖枢轴点及其后的所有内容。
以下是目前的效果

好的,现在头部紧贴着尾部的末端。等等,不要为此大惊小怪!我们可以通过将 margin: auto
应用于尾部最后一个元素的右侧来解决此问题。碰巧的是,这也恰好是单词的最后一个字母,现在位于中间某个位置。添加自动边距的作用是将头部从尾部推开,一直推到容器的右侧。
现在我们得到如下效果

剩下的唯一事情就是以正确的顺序将我们的片段拼接在一起。如果我们对所有字母应用 position: relative
,然后在尾部加上 left: 50%
,在头部项目上加上 right: 50%
,这很容易做到。

这是我们刚刚使用的代码的通用版本。如您所见,它只有 15 行简单的 CSS 代码
.container {
display: flex;
}
.item:last-child {
margin-right: auto;
}
.item {
order: 2;
position: relative;
right: 50%;
}
.pivot, .pivot ~ .item {
order: 1;
left: 50%;
}
也可以通过将 flex-direction
设置为 column
值来使用此方法进行垂直布局。还需要说明的是,可以通过将头部和尾部元素粘贴到它们自己的包装器中来实现相同的效果——但这需要更多标记和冗长的 CSS,同时灵活性也大大降低。例如,如果我们的后端已经生成了一个没有包装的元素列表,其中包含动态生成的类,会怎样?
相当偶然的是,此解决方案也与屏幕阅读器配合良好。虽然我们反向排序了这两个部分,但随后我们通过相对定位将它们移回原位,因此元素的最终排序与我们的标记匹配,尽管很好地居中。
以下是 CodePen 上的最终示例
结论
开发人员比杂技演员更擅长平衡。不相信我?仔细想想:我们面临的许多常见挑战都需要在相互竞争的需求之间找到一个平衡点,这些需求包括性能和可读性、样式和功能,甚至可扩展性和简单性。毫无疑问,这是一场平衡游戏。
但我们找到平衡点的时刻并不总是在某件事和另一件事的中间。平衡通常存在于两者之间某个难以解释的点;或者,正如我们刚刚看到的,围绕一个任意的 HTML 元素。
就是这样!去告诉你的朋友,你是周围最伟大的杂技演员。

你太棒了!
这太棒了!
这真是太令人印象深刻了。我只能想象人们会使用多少行 JS 来实现这一点。
太棒了!
稍微有点不足:如果某些字母没有放置在包含的
.puzzle
块元素内,那就完美了。特别是对于水平枢轴,这可能是一个问题:https://codepen.io/ccprog/pen/XWKJpxj您认为如何解决这个问题?
谢谢!是的,在某些情况下,内容会泄漏到容器之外。我使用拼图的两种方式都没有遇到这种效果
也就是说,有几种方法可以减轻这种情况。最简单的方法是确保顶级容器的宽度(或高度,在您的情况下)永远不会低于某个值。 这是您的笔的副本,字体大小已缩小,并且具有固定高度。
哇!简单、优雅、明智:精彩。
哇,Julian!这里有非常巧妙的横向思维。
非常规的方法!特别是使用“order”和“margin”进行对齐。
超级创新且非常规!
哈,不错,我之前也做过类似的实验,用来围绕 logo 平衡导航项目
干得好!:D
哇,我以为会很复杂。结果却令人震惊。Julian,真的很棒的方法!
感谢您撰写本文!让我思考网格是否更容易,因此 在此笔上进行了尝试。
我以为可以不用 span,使用等宽字体、字母间距、填充等作弊,但我意识到我仍然无法在网格上定位特定的字母(第 n 个字母选择器在哪里!!),所以我又回到了 span 所有内容。
然后做了 第二个笔。
尝试定位区域而不是字母,这样你就可以对悬停等做一些更有趣的事情,而且结果更简洁有趣。再次感谢!
哇,很棒的方法。真是令人惊叹。我正在尝试弄清楚如何将我的支点对齐到左侧而不是页面中心。也许你有什么提示?
谢谢!
据我了解,您仍然希望支点对齐,但您希望它们靠在容器的左侧?在这种情况下,您可以像这样设置头部和尾部的定位
请注意,原始容器仍将保持其原始大小,并且支点左侧的项目将位于其外部,因此您可能需要通过添加边距来进行补偿。
希望这有帮助!
感谢您撰写本文!!
尝试用 Grid 做了一些有趣的事情
[尝试 1](https://codepen.io/jupago/pen/MWeKoaV)
[尝试 2](https://codepen.io/jupago/pen/YzWwQRG)
对于第一次尝试,我试图过于聪明,没有使用任何 span,但由于我们仍然没有 nth-letter (╯°□°)╯︵ ┻━┻ 我无法使其工作。
对于第二次尝试,我能够利用 Grid 并定位单元格而不是字母,这次肯定更有趣。(悬停!)
(*几天前写了一个类似的回复,但看起来没有通过,所以这是一个重做,希望我没有重复回复!)
不错!我喜欢第二个的悬停效果!真的很高兴你也能玩得开心!! :)
“我们找到平衡点的点并不总是介于一件事和另一件事之间。平衡通常是在两者之间某个难以解释的点上找到的”
这在很多方面都很美妙。