首页 > web前端 > css教程 > 用tan(atan2())在CSS中的打字和视口转换

用tan(atan2())在CSS中的打字和视口转换

Joseph Gordon-Levitt
发布: 2025-03-07 16:41:09
原创
735 人浏览过

Typecasting and Viewport Transitions in CSS With tan(atan2())

CSS 已经能够获取视口长度了,时间可以追溯到……(查看笔记)……2013年!令人惊讶的是,那已经是十多年前的事了。如今,获取视口宽度就像编写 100vw 一样简单,但这在像素中又意味着什么呢?其他属性呢,比如那些采用百分比、角度或整数的属性?

考虑一下根据屏幕大小更改元素的不透明度、旋转它或设置动画进度。首先需要视口作为整数——这在 CSS 中目前是不可能的,对吧?

我接下来要说的并非什么突破性的发现,Jane Oriin 在 2023 年就精彩地描述过。简而言之,我们可以使用一个涉及 tan()atan2() 三角函数的奇特技巧(或特性)来将长度(例如视口)类型转换为整数。这开启了许多新的布局可能性,但我第一次体验是在编写 Almanac 条目时,我只是想让图像的不透明度具有响应性。

调整 CodePen 大小,随着屏幕尺寸变小,图像会变得更透明,当然会有一些边界,因此它不会变得不可见:

这是我们能做的最简单的方法,但还有更多。例如,我尝试组合许多与视口相关的效果所做的这个演示。调整演示大小,页面就会充满活力:对象移动、背景变化,文本平滑地换行到位。

我认为这真的很酷,但我不是设计师,所以这是我大脑所能想到的最好的方法。不过,对于这个类型转换技巧的介绍来说,这可能有点太多了,因此,作为中间地带,我将只关注标题转换以展示其所有工作原理:

设置

其背后的想法是使用 atan2()100vw 转换为弧度(一种编写角度的方式),然后使用 tan() 返回其原始值,其优点是作为整数出现。应该像这样实现:

<code>:root {
  --int-width: tan(atan2(100vw, 1px));
}</code>
登录后复制
登录后复制
登录后复制

但是!浏览器不太支持这种方法,因此需要更多包装才能使其在所有浏览器中都能工作。以下内容可能看起来像魔法(或无稽之谈),所以我建议阅读 Jane 的文章以更好地理解它,但这样它就能在所有浏览器中工作:

<code>@property --100vw {
  syntax: "<length>";
  initial-value: 0px;
  inherits: false;
}

:root {
  --100vw: 100vw;
  --int-width: calc(10000 * tan(atan2(var(--100vw), 10000px)));
}</length></code>
登录后复制
登录后复制
登录后复制

别太担心。重要的是我们宝贵的 --int-width 变量,它保存视口大小作为整数!

宽度:一个数字统治所有

现在我们有了视口作为整数,但这只是第一步。这个整数本身并不是非常有用。我们应该接下来将其转换为其他内容,因为:

  • 不同的属性具有不同的单位,并且
  • 我们希望每个属性都从起始值变为结束值。

考虑一下图像的不透明度从 0 变为 1,对象从 0deg 旋转到 360deg,或者元素的 offset-distance 从 0% 变为 100%。我们希望随着 --int-width 变大而在这些值之间进行插值,但现在它只是一个通常在 0 到 1600 之间变化的整数,这是不灵活的,并且不能轻松转换为任何结束值。

最好的解决方案是将 --int-width 转换为一个从 0 到 1 的数字。因此,随着屏幕变大,我们可以将其乘以所需的结束值。由于缺乏更好的名称,我称这个“0 到 1”的值为 --wideness。如果我们有 --wideness,所有最后的示例都成为可能:

<code>:root {
  --int-width: tan(atan2(100vw, 1px));
}</code>
登录后复制
登录后复制
登录后复制

因此,--wideness 是一个介于 0 到 1 之间的值,表示屏幕的宽度:0 表示屏幕狭窄时,1 表示屏幕宽阔时。但我们仍然必须设置这些值在视口中的含义。例如,我们可能希望 0 为 400px,1 为 1200px,我们的视口转换将在这些值之间运行。低于和高于的值分别被钳位到 0 和 1。

在 CSS 中,我们可以这样编写:

<code>@property --100vw {
  syntax: "<length>";
  initial-value: 0px;
  inherits: false;
}

:root {
  --100vw: 100vw;
  --int-width: calc(10000 * tan(atan2(var(--100vw), 10000px)));
}</length></code>
登录后复制
登录后复制
登录后复制

除了简单的转换之外,--wideness 变量还可以让我们定义转换应该运行的下限和上限。更好的是,我们可以将转换区域设置在中间位置,以便用户可以充分欣赏它。否则,屏幕需要为 0px,以便 --wideness 达到 0,而谁知道要达到 1 需要多宽。

我们得到了 --wideness。接下来是什么?

首先,标题的标记被分成 span,因为没有 CSS 方法可以选择句子中的特定单词:

<code>/* 如果 `--wideness` 为 0.5 */

.element {
  opacity: var(--wideness); /* 为 0.5 */
  translate: rotate(calc(wideness(400px, 1200px) * 360deg)); /* 为 180deg */
  offset-distance: calc(var(--wideness) * 100%); /* 为 50% */
}</code>
登录后复制
登录后复制

由于我们将自己进行换行,因此取消设置一些默认值非常重要:

<code>:root {
  /* 两个边界都是无单位的 */
  --lower-bound: 400;
  --upper-bound: 1200;

  --wideness: calc(
    (clamp(var(--lower-bound), var(--int-width), var(--upper-bound)) - var(--lower-bound)) / (var(--upper-bound) - var(--lower-bound))
  );
}</code>
登录后复制
登录后复制

转换应该可以在没有基本样式的情况下工作,但它看起来太普通了。如果您想将它们复制到您的样式表中,它们如下所示:

最后,我们的当前技巧如下所示:

<code><h1>Resize and enjoy!</h1></code>
登录后复制

好了,设置就到这里。现在是时候使用我们的新值并进行视口转换了。我们首先必须确定标题应该如何重新排列以适应较小的屏幕:正如您在初始演示中看到的,第一个 span 向上和向右移动,而第二个 span 向相反的方向移动,向下和向左移动。因此,两个 span 的最终位置转换为以下值:

<code>h1 {
  position: absolute; /* 保持文本居中 */
  white-space: nowrap; /* 禁用换行 */
}</code>
登录后复制

在继续之前,这两个公式基本上相同,但符号不同。我们可以通过引入一个新变量 --direction 来一次重写它们。它将是 1 或 -1,并定义运行转换的方向:

<code>:root {
  --int-width: tan(atan2(100vw, 1px));
}</code>
登录后复制
登录后复制
登录后复制

下一步是将 --wideness 引入公式中,以便随着屏幕大小调整而更改值。但是,我们不能只将所有内容乘以 --wideness。为什么?让我们看看如果我们这样做会发生什么:

<code>@property --100vw {
  syntax: "<length>";
  initial-value: 0px;
  inherits: false;
}

:root {
  --100vw: 100vw;
  --int-width: calc(10000 * tan(atan2(var(--100vw), 10000px)));
}</length></code>
登录后复制
登录后复制
登录后复制

正如您将看到的,一切都反过来了!当屏幕太宽时,单词会换行,当屏幕太窄时,单词会取消换行:

与我们的第一个示例不同,在第一个示例中,转换随着 --wideness 从 0 增加到 1 而结束,我们希望随着 --wideness 从 1 减少到 0 而完成转换,即随着屏幕变小,属性需要达到其最终值。这没什么大不了的,因为我们可以将我们的公式重写为减法,其中减数随着 --wideness 的增加而增加:

<code>/* 如果 `--wideness` 为 0.5 */

.element {
  opacity: var(--wideness); /* 为 0.5 */
  translate: rotate(calc(wideness(400px, 1200px) * 360deg)); /* 为 180deg */
  offset-distance: calc(var(--wideness) * 100%); /* 为 50% */
}</code>
登录后复制
登录后复制

现在,调整屏幕大小时,所有内容都将朝正确的方向移动!

但是,您会注意到单词如何直线移动,以及调整大小时某些单词如何重叠。我们不允许这样做,因为具有特定屏幕尺寸的用户可能会在转换中的这一点上卡住。视口转换很酷,但不以破坏某些屏幕尺寸的体验为代价。

单词不应直线移动,而应曲线移动,以便它们绕过中心单词。别担心,在这里制作曲线比看起来容易:只需在 x 轴上使 span 的移动速度是 y 轴的两倍即可。这可以通过将 --wideness 乘以 2 来实现,尽管我们必须将其限制为 1,这样它就不会超过最终值。

<code>:root {
  /* 两个边界都是无单位的 */
  --lower-bound: 400;
  --upper-bound: 1200;

  --wideness: calc(
    (clamp(var(--lower-bound), var(--int-width), var(--upper-bound)) - var(--lower-bound)) / (var(--upper-bound) - var(--lower-bound))
  );
}</code>
登录后复制
登录后复制

看看那美丽的曲线,正好避开了中心文本:

这仅仅是个开始!

令人惊讶的是,将视口作为整数可以有多强大,更疯狂的是,最后一个示例是您可以使用此类型转换技巧进行的最基本转换之一。一旦您完成了初始设置,我可以想象还有更多可能的转换,而 --wideness 非常有用,就像现在拥有一个新的 CSS 功能一样。

我希望将来能看到更多关于“视口转换”的内容,因为它们确实使网站比自适应网站感觉更“生动”。

以上是用tan(atan2())在CSS中的打字和视口转换的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
作者最新文章
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板