首页 >web前端 >css教程 >流体其他一切

流体其他一切

Christopher Nolan
Christopher Nolan原创
2025-03-07 16:59:08206浏览

Fluid Everything Else

响应式设计,我们都懂,对吧?我们用媒体查询。不,现在我们用容器查询了,不是吗?有时我们会巧妙地运用flexbox或自动流式网格。如果我们想更大胆一些,还可以尝试流体排版。

我有点不习惯响应式设计经常被分割成离散的块,例如“布局A到这个尺寸,然后是布局B,直到有足够的空间容纳布局C”。这没问题,它有效,并且适合于将屏幕设计为PhotoFigVa中静态布局的工作流程(免责声明,我编造了这个软件名称)。但这个过程在我看来是一种妥协。我一直相信,响应式设计应该对用户几乎不可见。当他们在排队购买K-Pop门票时,在移动设备上访问我的网站,他们不应该注意到它与一小时前坐在他们说服老板需要的巨大曲面游戏显示器上的体验有什么不同。

考虑一下这个简单的英雄横幅及其移动端等效版本。抱歉设计比较粗糙。图片是AI生成的,但这是这篇文章中唯一AI生成的部分。

狐獴和文本的位置和大小都不同。实现这一点的传统方法是使用<del>媒体</del>(抱歉,容器)查询选择两种布局。每种布局可能具有一定的灵活性,例如居中内容,以及字体大小上的少量流体排版,但我们将选择一个点,在此点上我们将布局切换进出堆叠版本。因此,在断点附近的宽度处,布局可能看起来有点空或有点拥挤。

还有其他方法吗?

事实证明,。我们可以将流体排版的概念应用于几乎任何东西。这样,我们可以拥有一个随其父容器大小而流畅变化的布局。很少有用户会看到过渡,但他们都会欣赏结果。说实话,他们会的。

开始设计样式

第一步,让我们分别为布局设置样式,有点像我们使用宽度查询和断点时那样。事实上,让我们一起使用容器查询和断点,以便我们可以轻松地看到需要更改哪些属性。

这是我们英雄横幅的标记,它不会改变:

<div>
  <div>
    <h1>LookOut</h1>
    <p>Eagle Defense System</p>
  </div>
</div>

这是宽版本相关的CSS:

#hero {
  container-type: inline-size;
  max-width: 1200px;
  min-width: 360px;

  .details {
    position: absolute;
    z-index: 2;

    top: 220px;
    left: 565px;

    h1 { font-size: 5rem; }

    p { font-size: 2.5rem; }
  }

  &::before {
    content: '';
    position: absolute;
    z-index: 1;

    top: 0; left: 0; right: 0; bottom: 0;

    background-image: url(../meerkat.jpg);
    background-origin: content-box;
    background-repeat: no-repeat;

    background-position-x: 0;
    background-position-y: 0;
    background-size: auto 589px;
  }
}

我将背景图像附加到一个::before伪元素上,以便我可以在其上使用容器查询(因为容器不能查询自身)。我们稍后会保留这一点,以便我们可以使用内联容器查询 (cqi) 单位。现在,这是仅显示我们将要使之流畅的值的容器查询:

@container (max-width: 800px) {
  #hero {
    .details {
      top: 50px;
      left: 20px;

      h1 { font-size: 3.5rem; }

      p { font-size: 2rem; }
    }

    &::before {
      background-position-x: -310px;
      background-position-y: -25px;
      background-size: auto 710px;
    }
  }
}

您可以在实时演示中看到代码运行——它完全是静态的,以显示典型方法的局限性。

让我们变得流畅

现在我们可以获取文本和背景的大小和位置的起始点和结束点,并使其流畅。文本大小以您已经熟悉的方式使用流体排版。这是结果——在您查看代码后,我将解释这些表达式。

首先是文本位置和大小的更改:

<div>
  <div>
    <h1>LookOut</h1>
    <p>Eagle Defense System</p>
  </div>
</div>

这是狐獴图像的背景位置和大小:

#hero {
  container-type: inline-size;
  max-width: 1200px;
  min-width: 360px;

  .details {
    position: absolute;
    z-index: 2;

    top: 220px;
    left: 565px;

    h1 { font-size: 5rem; }

    p { font-size: 2.5rem; }
  }

  &::before {
    content: '';
    position: absolute;
    z-index: 1;

    top: 0; left: 0; right: 0; bottom: 0;

    background-image: url(../meerkat.jpg);
    background-origin: content-box;
    background-repeat: no-repeat;

    background-position-x: 0;
    background-position-y: 0;
    background-size: auto 589px;
  }
}

现在我们可以完全删除容器查询了。

让我们解释一下这些clamp()表达式。我们将从top属性的表达式开始。

@container (max-width: 800px) {
  #hero {
    .details {
      top: 50px;
      left: 20px;

      h1 { font-size: 3.5rem; }

      p { font-size: 2rem; }
    }

    &::before {
      background-position-x: -310px;
      background-position-y: -25px;
      background-size: auto 710px;
    }
  }
}

您会注意到那里有一个注释。这些表达式很好地说明了为什么幻数是不好的。但我们在这里无法避免它们,因为它们是一些联立方程的解——CSS无法做到这一点!

传递给clamp() 的上限和下限足够清楚,但中间的表达式来自这些联立方程:

/* 行更改
 * -12,27 +12,32
 */

.details {
  /* ... 行 14-16 未更改 */
  /* 对 360px 宽的容器计算结果为 50px,对 1200px 宽的容器计算结果为 220px */
  top: clamp(50px, 20.238cqi - 22.857px, 220px);

  /* 对 360px 宽的容器计算结果为 20px,对 1200px 宽的容器计算结果为 565px */
  left: clamp(20px, 64.881cqi - 213.571px, 565px);

  /* ... 行 20-25 未更改 */
  h1 {
    /* 对 360px 宽的容器计算结果为 3.5rem,对 1200px 宽的容器计算结果为 5rem */
    font-size: clamp(3.5rem, 2.857rem + 2.857cqi, 5rem);
    /* ... 字体粗细未更改 */

  }

  p {
    /* 对 360px 宽的容器计算结果为 2rem,对 1200px 宽的容器计算结果为 2.5rem */
    font-size: clamp(2rem, 1.786rem + 0.952cqi, 2.5rem);
  }
}

……其中f是固定大小长度单位(即px)的数量,v是可变大小单位 (cqi)。在第一个方程中,我们说当1cqi等于12px时,我们希望表达式计算结果为220px。在第二个方程中,我们说当1cqi为3.6px时,我们想要50px,这解为:

/* 行更改
 * -50,3 +55,8
 */

/* 对 360px 宽的容器计算结果为 -310px,对 1200px 宽的容器计算结果为 0px */
background-position-x: clamp(-310px, 36.905cqi - 442.857px, 0px);
/* 对 360px 宽的容器计算结果为 -25px,对 1200px 宽的容器计算结果为 0px */
background-position-y: clamp(-25px, 2.976cqi);
/* 对 360px 宽的容器计算结果为 710px,对 1200px 宽的容器计算结果为 589px */
background-size: auto clamp(589px, 761.857px - 14.405cqi, 710px);

……这整理成一个计算友好的表达式:20.238cqi – 22.857px

当固定单位不同时,我们必须相应地更改可变单位的大小。因此,对于<h1></h1>元素的字体大小,我们有;

/* 对 360px 宽的容器计算结果为 50px,对 1200px 宽的容器计算结果为 220px */
top: clamp(50px, 20.238cqi - 22.857px, 220px);

======================================================================================================================================================================

这是在解决这些方程,因为在容器宽度为 1200px 时,1cqi 与 0.75rem 相同(我的 rem 相对于默认 UA 样式表,16px),而在 360px 宽时,1cqi 为 0.225rem。

<code>f + 12v = 220
f + 3.6v = 50</code>

需要注意的是:根据您目标的单位,方程是不同的。

老实说,每次都做这个枯燥的数学运算很无聊,所以我做了一个你可以使用的计算器。它不仅可以为你解决方程(精确到小数点后三位,以保持你的CSS整洁),还可以提供有用的注释与表达式一起使用,这样你就可以看到它们的来源并避免幻数。随意使用它。是的,那里有很多类似的计算器,但它们专注于排版,因此(正确地)专注于rem单位。如果你使用CSS预处理器,你可能可以移植JavaScript。

clamp()函数此时并非严格必要。在每种情况下,clamp() 的边界都设置为容器宽度为 360px 或 1200px 时的值。由于容器本身被限制在这些限制内——通过设置min-widthmax-width值——clamp()表达式永远不应该调用任何边界。但是,如果我们改变主意(我们即将这样做),我更喜欢保留clamp(),因为像这样的隐式边界很难发现和维护。

避免伤害

我们可以认为我们的工作完成了,但我们还没有。布局仍然不太好用。文本直接覆盖在狐獴的头上。虽然我得到保证这不会对狐獴造成伤害,但我不喜欢它的外观。因此,让我们进行一些更改,以使文本避免碰到狐獴。

第一个很简单。我们将狐獴更快地向左移动,以便它让路。最简单的方法是将插值的较低端更改为更宽的容器。我们将设置它,以便狐獴在 450px 处完全向左移动,而不是下降到 360px。没有理由我们所有流体表达式的起始点和结束点需要与相同的宽度对齐,因此我们可以将其他表达式流畅地降低到 360px。

使用我可靠的计算器,我们只需要更改背景位置属性的clamp()表达式:

<div>
  <div>
    <h1>LookOut</h1>
    <p>Eagle Defense System</p>
  </div>
</div>

这改善了情况,但并非完全改善。我不想更快地移动它,所以接下来我们将看看文本的路径。目前它沿直线移动,如下所示:

但是我们可以弯曲它吗?是的,我们可以。

路径中的弯曲

我们可以做到这一点的一种方法是为顶部坐标定义两个不同的插值,将直线放置在不同的角度,然后选择最小的一个。这样,它允许更陡峭的直线在更大的容器宽度处“获胜”,而较浅的直线在容器宽度小于约 780px 时成为获胜的值。结果是一条带有弯曲的直线,它错过了狐獴。

我们只更改top值,但我们必须首先计算两个中间值:

#hero {
  container-type: inline-size;
  max-width: 1200px;
  min-width: 360px;

  .details {
    position: absolute;
    z-index: 2;

    top: 220px;
    left: 565px;

    h1 { font-size: 5rem; }

    p { font-size: 2.5rem; }
  }

  &::before {
    content: '';
    position: absolute;
    z-index: 1;

    top: 0; left: 0; right: 0; bottom: 0;

    background-image: url(../meerkat.jpg);
    background-origin: content-box;
    background-repeat: no-repeat;

    background-position-x: 0;
    background-position-y: 0;
    background-size: auto 589px;
  }
}

对于这些值,我并没有使用精心选择的中间点正式计算它们,而是尝试了端点,直到得到我想要的结果。实验与计算一样有效,可以达到您需要的效果。在这种情况下,我从自定义变量中重复插值开始。我本可以使用容器查询将路径拆分为显式部分,但这不会减少数学开销,并且使用min()函数在我的眼中更简洁。此外,这篇文章并非严格关于容器查询,对吧?

现在文本沿此路径移动。打开实时演示以查看其运行情况。

CSS 无法做到一切

最后关于计算的一点说明是,就我们能做什么和不能做什么而言,存在限制。第一个,我们已经稍微减轻了,那就是这些插值是线性的。这意味着不可能进行淡入淡出或其他复杂的行为。

另一个主要限制是CSS只能以这种方式生成长度值,因此无法在纯CSS中应用例如基于容器或视口大小的流体不透明度或旋转角度。预处理器也无法在这里帮助我们,因为限制在于calc()在浏览器中的工作方式。

如果您准备依赖少量JavaScript,则可以解除这两个限制。几行代码来观察容器的宽度并设置一个无单位的CSS自定义属性就足够了。我将用它来使文本遵循二次贝塞尔曲线,如下所示:

这里代码太多,无法列出,数学解释贝塞尔曲线也太多,但请在实时演示中查看其运行情况。

如果像calc(1vw / 1px)这样的表达式在CSS中没有失败,我们甚至不需要JavaScript。它们没有失败的理由,因为它们表示两个长度之间的比率。就像1英寸中有2.54厘米一样,当视口宽度为800px时,1vw中有8px,因此calc(1vw / 1px)应该计算为无单位的8值。

但它们确实失败了,所以我们所能做的就是陈述我们的观点然后继续前进。

流体一切并不能解决所有布局

当然,总有一些布局需要大小查询;有些设计只需要在固定的断点处进行更改。如果合适,没有理由避免这样做。也没有理由避免混合两者,例如,通过流畅地调整背景大小和位置,同时使用查询在文本放置的网格定义之间切换。我的狐獴示例故意设计得很简单,是为了演示的目的。

我要补充一点的是,我对使用新的锚点定位API进行流体定位的可能性感到非常兴奋。有可能使用锚点定位来定义两个元素如何一起在屏幕上流动,但这留待以后再讨论。

以上是流体其他一切的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn