使用 Flexbox 实现围绕枢轴点平衡

Avatar of Julian Merkenich
Julian Merkenich 发布

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费额度!

让我向您展示我最近发现的一种方法,它可以将一堆元素围绕我称之为 **枢轴点** 的点进行居中。我向您保证,这种方法无需使用奇特的 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 元素。

就是这样!去告诉你的朋友,你是周围最伟大的杂技演员。