如果您曾经看过老科幻电影,您就会知道循环有多么强大。 给您的机器人死敌一个无限循环,然后 _砰_。 机器人灰尘。
预处理器循环不会在太空中造成剧烈的爆炸(我希望),但它们对编写 DRY CSS 很有用。 虽然每个人都在谈论模式库和模块化设计,但大部分重点都放在 CSS 选择器上。 无论您使用什么缩写来驱动您的选择器(BEM、OOCSS、SMACSS 等),循环都可以帮助保持您的模式更具可读性和可维护性,并将它们直接烘焙到您的代码中。
我们将看看循环可以做什么,以及如何在主要的 CSS 预处理器中使用它们:Sass、Less 和 Stylus。 每种语言都提供独特的语法,但它们都能完成工作。 有不止一种方法可以 循环猫。
查看 CodePen 上的
带音乐循环的走动循环 by CSS-Tricks (@css-tricks)
在 CodePen 上。
(动画 by Rachel Nabors)
PostCSS 也很流行,但它没有提供自己的语法。 虽然它有时被称为后处理器,但我称它为 _元预处理器_。 PostCSS 允许您编写和共享自己的预处理器语法。 如果您愿意,您可以在 PostCSS _内部_ 重新编写 Sass 或 Less,但 其他人已经捷足先登了。
循环条件
星际迷航并不完全错误。 如果不小心,无限循环会导致编译器速度变慢或崩溃。 虽然这不是消灭邪恶机器人的好方法,但它会惹恼任何使用您的代码的人。 这就是为什么循环应该始终服务于有限的目的 - 通常由一定数量的增量重复或一组对象定义。
在编程术语中
- **While** 循环是通用的,并且会在满足任何条件时继续循环 **while**。 小心! 这是最有可能出现无限循环的地方。
- **For** 循环是增量的,**for** 特定数量的重复次数运行。
- **For-Each** 循环遍历集合或列表,一次考虑 **each** 项。
每种类型的循环都比前一种更具针对性。 一个 for-each
循环只是一种 for
循环,而 for
循环只是一种 while
循环。 但是,您的大部分用例都将属于更具体的类别。 我很难在野外找到真正的 while
循环 - 大多数示例本可以使用 for
或 for-each
更好地处理。 这可能是为什么 Stylus 只提供后者的语法的原因。 Sass 为所有这三种类型提供了独特的语法,而 Less 从技术上来说根本没有循环语法 - 但这不会阻止我们! 让我们深入了解一下。
for-each
循环
集合 当您有一组(列表或数组)要循环的项目时,预处理器循环最为有用 - 例如社交媒体图标和颜色的数组,或状态修饰符列表(success
、warning
、error
等)。 由于 for-each
循环与一组已知项目相关联,因此它们往往是最具体且易于理解的循环。
让我们首先循环遍历一个简单的颜色列表,看看它是如何工作的。
在 **Sass** 中,我们将使用 @each
指令 (@each $item in $list
) 来获取颜色
查看 CodePen 上的 Sass ForEach List by Miriam Suzanne (@mirisuzanne) on CodePen.
在 **Stylus** 中,for
语法 (for item in list
) 处理集合
查看 CodePen 上的 Stylus ForEach List by Miriam Suzanne (@mirisuzanne) on CodePen.
**Less** 不提供循环语法,但我们可以用_递归_来伪造它。 递归是指从自身内部调用函数或 mixin 时发生的事情。 在 Less 中,我们可以使用 mixin 进行递归
.recursion() {
/* an infinite recursive loop! */
.recursion();
}
现在,我们将向 mixin 添加一个 when
“guard”,以防止它无限循环。
.recursion() when (@conditions) {
/* a conditional recursive "while" loop! */
.recursion();
}
我们可以通过添加一个计数器 (@i
) 来使其成为 **for** 循环,该计数器从 1
开始,并在每次重复 (@i + 1
) 时增加,只要我们的条件 (@i <= length(@list)
) 满足 - 其中 length(@list)
将我们的循环迭代次数限制为与我们的集合相同的长度。 如果我们在每次传递时提取下一个列表项,我们将有一个手工制作的 **for-each** 循环
查看 CodePen 上的 Less ForEach List by Miriam Suzanne (@mirisuzanne) on CodePen.
在 Less 中,你必须以最困难的方式做所有事情。 这可以培养你的性格。
社交媒体按钮
循环遍历列表很有用,但您更常希望循环遍历对象。 一个常见的例子是将不同的颜色和图标分配给您的社交媒体按钮。 对于列表中的每个项目,我们需要网站名称和该社交网络的品牌颜色
$social: (
'facebook': #3b5999,
'twitter': #55acee,
'linkedin': #0077B5,
'google': #dd4b39,
);
使用 **Sass**,我们可以使用语法 @each $key, $value in $array
访问每对的键(网络名称)和值(品牌颜色)。 这是完整的循环
查看 CodePen 上的 Sass Social Media Loop by Miriam Suzanne (@mirisuzanne) on CodePen.
**Stylus** 有类似的语法:for key, value in array
查看 CodePen 上的 Stylus Social Media Loop by Miriam Suzanne (@mirisuzanne) on CodePen.
在 **Less** 中,我们必须手动提取每对的每一侧
查看 CodePen 上的 LESS Social Media Loop by Miriam Suzanne (@mirisuzanne) on CodePen.
for
循环
增量 **For** 循环可以运行任何数量的重复次数,而不仅仅是对象的长度。 您可能会使用它来创建网格布局 (for columns from 1 through 12
),循环遍历色轮 (for hue from 1 through 360
),或用 nth-child
和生成的 content 对您的 div 进行编号。
让我们首先循环遍历 36 个 div
元素,使用 :nth-child
为每个元素提供一个数字和背景颜色。
**Sass** 提供了一个特殊的 for-loop 语法:@for $count from $start through $finish
,其中 $start
和 $finish
都是整数。 如果起始值更大,Sass 将向下计数而不是向上计数。
查看 CodePen 上的 Sass “for” loop by Miriam Suzanne (@mirisuzanne) on CodePen.
through
关键字表示我们的循环将包括数字 36
。 您也可以使用 to
关键字,它不包括最终计数器:@for $i from 1 to 36
仅循环 35
次。
**Stylus** 对递增具有类似的语法,但 to
和 through
分别被 ...
和 ..
替换
查看 CodePen 上的 Stylus “for” loop by Miriam Suzanne (@mirisuzanne) on CodePen.
Stylus 还提供了一个 range()
函数,它允许您更改一次递增的大小。 使用 for hue in range(0, 360, 10)
将在每次重复时将计数增加 10。
**Less** 将不得不再次使用递归 mixin。 我们可以为迭代次数创建一个参数 (@i
),用条件 when (@i > 0)
保护它,并在每次迭代时减去一 - 使其像一个递减的 for-loop
查看 CodePen 上的 Less “for” loop by Miriam Suzanne (@mirisuzanne) on CodePen.
值得注意的是,**CSS** 也可以在没有预处理器的情况下为我们提供 nth-child
编号。 虽然 CSS 没有循环结构,但它确实提供了一个 counter()
,您可以根据任何数量的 DOM 相关条件递增它,并在生成的 content 中使用。 遗憾的是,它还不能在 content
属性之外使用(还没有),因此我们的背景颜色没有被应用
查看 CodePen 上的 CSS counter by Miriam Suzanne (@mirisuzanne) on CodePen.
网格系统
我在抽象的 Sass 工具包中偶尔会使用增量循环,但在实际的样式表中几乎从不使用。一个常见的例外是生成编号的选择器,无论是使用 `nth-child`(就像我们在上面做的那样)还是在自动生成的类中(通常用于网格系统)。让我们构建一个简单的流体网格系统,不使用任何间距,使数学变得困难
查看 CodePen 上 Miriam Suzanne (@mirisuzanne) 的 Sass For-Loop 网格 笔记。
每个网格跨度都是一个百分比,使用数学公式 `span / context * 100%` — 所有网格系统都必须进行的基本计算。以下是 Stylus 和 Less 中的代码
查看 CodePen 上 Miriam Suzanne (@mirisuzanne) 的 Stylus For-Loop 网格 笔记。
查看 CodePen 上 Miriam Suzanne (@mirisuzanne) 的 LESS For-Loop 网格 笔记。
独特的头像
在 OddBird,我们最近设计了一个应用程序,使用默认的用户头像 — 但我们希望这些默认头像尽可能独特。最终,我们只设计了九个独特的图标,并使用循环将它们转换为 1296 个不同的头像,因此大多数用户永远不会看到重复的头像。
每个头像都有五个属性
<svg class="avatar" data-dark="1" data-light="2" data-reverse="true" data-rotation="3">
<use xlink:href="#icon-avatar-1" xmlns:xlink="http://www.w3.org/1999/xlink"></use>
</svg>
- 起始图标形状(9 个选项)
- 旋转 `0`、`90`、`180` 或 `270` 度(4 个选项)
- 用于填充的深色(6 个选项)
- 用于背景的浅色(6 个选项)
- 一个 `true`/`false` 属性,用于反转颜色(2 个选项)
代码有六种颜色,三个循环
@for $i from 0 through 3
为我们提供四个旋转@for $i from 1 through length($colors)
允许我们循环遍历颜色列表(`$colors`),并为每种颜色分配一个数字(`$i`)。通常我会使用 `@each` 循环来遍历颜色集合,但当我也需要每个项目的数字时,`@for` 更简单。- 嵌套的
@each $reverse in (true, false)
为我们提供了为每种颜色组合翻转前景和背景的选项。
以下是 Sass 中的最终结果
查看 CodePen 上 Miriam Suzanne (@mirisuzanne) 的 使用多个循环创建 1296 个头像 笔记。
将它转换为 Less 和 Stylus 可以作为你的作业。我已经厌倦了看它。
通用 `while` 循环
真正的 `while` 循环很少见,但我偶尔会使用它们。我发现它们在我遵循一条路径以查看其走向时最为有用。我不想循环遍历整个集合或特定数量的迭代 — 我想要一直循环,直到找到我想要的东西。这是我在抽象工具包中使用的东西,但这不是你在日常样式中经常需要的东西。
我构建了一个工具包来帮助我在 Sass 中存储和操作颜色。在变量中存储颜色可能是任何预处理器最常见的用例。大多数人会这样做
$pink: #E2127A;
$brand-primary: $pink;
$site-background: $brand-primary;
我知道 `pink` 可能不是你网站上唯一的颜色,但现在它是我们唯一需要的颜色。我给它起了多个名称,因为它有助于建立抽象层 — 从简单的颜色(`pink`)到更广泛的模式(`brand-primary`)以及具体的用例(`site-background`)。我还想将该列表中的单个颜色转换为我的预处理器可以理解的调色板。我需要一种方法来说明*这些值都是相关的,并且属于一种模式*。为了做到这一点,我将所有主题颜色存储在一个 Sass 映射中,其中包含键值对
$colors: (
'pink': #E2127A,
'brand-primary': 'pink',
'site-background': 'brand-primary',
);
为什么要这样做?我这样做是因为我可以将我的样式指南生成器指向一个变量,并自动创建一个保持最新状态的调色板。但是存在权衡,它并非适合所有人。该映射不允许我像在变量中那样在对之间进行直接赋值。我需要一个 `while` 循环来跟踪键名的面包屑路径,以便找到每种颜色的值
查看 CodePen 上 Miriam Suzanne (@mirisuzanne) 的 Sass “while” 循环 笔记。
我一直都在这样做,但如果你在代码中搜索 Sass 的 `@while`,你将找不到它。这是因为你可以使用递归函数实现相同的效果,从而使其可重用
查看 CodePen 上 Miriam Suzanne (@mirisuzanne) 的 Sass “while” 递归函数 笔记。
现在,我们可以在代码中的任何位置调用 `color()` 函数。
Stylus 没有 `while` 循环的语法,但它也允许使用数组变量和递归函数
查看 CodePen 上 Miriam Suzanne (@mirisuzanne) 的 Stylus “while” 循环 笔记。
Less 没有内置的数组变量,但我们可以通过创建一对列表来模拟相同的效果,就像我们为社交媒体颜色所做的那样
@colors:
'pink' #E2127A,
'brand-primary' 'pink',
'site-background' 'brand-primary'
;
我们将不得不创建自己的 `@array-get` 混合器来使用键名从数组中检索值,然后创建我们的递归 while 循环来跟踪路径
查看 CodePen 上 Miriam Suzanne (@mirisuzanne) 的 Less “while” 列表循环 笔记。
这对于演示目的有效,但可能在 Less 中有更好的方法,因为你可以为变量创建别名和命名空间,而无需使用数组(不像 Sass 或 Stylus)
查看 CodePen 上 Miriam Suzanne (@mirisuzanne) 的 Less 命名空间变量 笔记。
现在我的颜色已成功存储在一个变量中,我可以使用另一个循环来生成我的调色板。以下是在 Sass 中的快速示例
查看 CodePen 上 Miriam Suzanne (@mirisuzanne) 的 Sass 调色板 笔记。
我相信你可以做得比我更漂亮。
循环起来!
如果你不确定何时在代码中使用循环,请注意重复。你是否有多个选择器遵循类似的模式,或者你是否重复进行计算?以下是如何判断哪个循环最适合
- 如果你可以列出并命名循环中的项目,请使用 `for-each` 循环遍历它们。
- 如果重复次数比任何源项目集更重要,或者如果你需要对项目进行编号,请使用 `for` 循环。
- 如果你需要使用不同的输入访问同一个循环,请尝试使用递归函数。
- 对于其他任何情况(几乎从不出现),请使用 `while` 循环。
- 如果你使用的是 Less... 祝你好运!
祝你循环愉快!
太酷了
哇。刚刚意识到从现在开始模式制作会变得多么容易。或者,绘制几只猫,循环它们,看看互联网的一半会变得多么寂静。
有一个 PostCSS 插件 postcss-functions 允许你在 JavaScript 中编写函数,这些函数可以进行递归等操作。
真聪明,谢谢!
但我最喜欢 Sass 的地方是,我可以在 Sass 中编写我的函数。当我编写复杂的函数时,任何 Sass 用户都可以访问和阅读该代码,而无需学习另一种语言。
我真是笑出声了。
Miriam,这太棒了。非常感谢!我发现自己主要在 Sass 中使用循环来处理 transition-delay 或 animation-delay。还没怎么研究过 Stylus,但语法看起来很酷。
发布一个链接!我很想看看你是如何将它与动画结合使用的!
这并不公平。Less 是一种声明式语言,就像 CSS 本身一样,而不是像 Sass 那样是命令式语言。循环是一种命令式概念,通常无法很好地转换为非命令式语言。函数式语言也存在同样的问题,也具有同样的解决方案:递归和递减循环不变式。
Less 中更好的循环模式是使用内部混合器,并利用 Less 混合器的多播行为来明确拆分循环体和迭代情况。
你可以在 Less 中编写自己的 JS 函数,这些函数允许你通过 `@plugin` 指令添加键值映射支持。
例如
但这有点恶心。
如果 Less v3.x 的支持自定义 at-规则的计划得以实现,那么它将使所有这些变得更加美观。这意味着你可以构建自己的 `@each` 结构和/或自己的 `@map` 结构。
这是一个很棒的小贴士,我会牢记在心,从现在开始我会注意我的 scss 代码中是否有重复。