我最近偶然发现了一个有趣的切片圆盘设计。 圆盘有一个对角渐变,并被分成水平切片,从左到右略微偏移。 自然地,我开始思考用 CSS 实现它的最有效方法是什么。

第一个想法是这应该可以用 border-radius
完成,对吧? 嗯,不! border-radius
的问题是它创建了一个椭圆形角,其末端 与它连接的边相切。
我的第二个想法是使用 circle()
剪切路径。 好吧,事实证明这种解决方案非常有效,所以让我们仔细研究一下!
请注意,以下演示在 Edge 中不起作用,因为 Edge 尚未支持 HTML 元素上的 clip-path
。 为了获得跨浏览器支持,所有这些都可以通过使用带有 overflow: hidden
的嵌套元素来模拟,但是,为了简单起见,我们在本文中分析了 clip-path
方法。
将圆盘切成相等的部分
就 HTML 结构而言,我们使用预处理器来生成它以避免重复。 首先,我们确定切片数量 n
。 然后我们将此数字作为自定义属性 --n
传递给 CSS。 最后,我们在循环中生成切片,并将每个切片的索引作为另一个自定义属性 --i
传递给 CSS。
- var n = 8;
style :root { --n: #{n} }
- for(var i = 0; i < n; i++)
.slice(style=`--i: ${i}`)
继续进行 CSS,我们首先确定圆盘的直径 $d
。 这是切片的 width
。 height
是直径除以项目数量 calc(#{$d}/var(--n))
。
为了能够区分它们,我们根据奇偶性为切片提供虚拟背景。
$d: 20em;
.slice {
--parity: 0;
width: $d;
height: calc(#{$d}/var(--n));
background: hsl(36, calc(var(--parity)*100%), calc(80% - var(--parity)*30%));
&:nth-of-type(2n) { --parity: 1 }
}
我们还在其容器(在本例中为 body
)上使用列 flex
布局将切片定位在中间。
请参阅 thebabydino 在 Pen 上的演示 (@thebabydino),并在 CodePen 上查看。
为了获得圆盘形状,我们使用一个 circle()
剪切路径,其半径 $r
等于直径的一半 .5*$d
,中心点位于组装的正中间。 由于我们在切片上设置了此 clip-path
,因此每个切片的中心点位置相对于切片本身。
在水平方向上,它始终位于中间,位于切片的 50%
处。 在垂直方向上,它需要位于组装的中间,因此我们需要使用从预处理器代码传递的 CSS 变量,即总项目数量和项目的索引。
在组装的中间意味着从组装的顶部到组装的高度的一半。 组装的高度的一半是直径的一半 .5*$d
,这等效于半径 $r
。 但此值相对于整个组装,我们需要一个相对于当前切片的相对值。 为了获得它,我们减去当前切片相对于组装的垂直位置,即当前切片的顶部相对于组装的顶部的距离。
第一个切片(索引为 --i: 0
)位于组装的顶部,因此在这种情况下我们减去的量为 0
。
第二个切片(索引为 --i: 1
)位于组装顶部的一个切片高度(第一个切片占用的空间),因此在这种情况下我们减去的量为 1
个切片高度。
第三个切片(索引为 --i: 2
)位于组装顶部两个切片高度(第一个和第二个切片占用的空间),因此在这种情况下我们减去的量为 2
个切片高度。
在一般情况下,我们为每个切片减去的量是切片的索引(--i
)乘以一个切片 height
。
--h: calc(#{d}/var(--n)); /* slice height */
clip-path: circle($r at 50% calc(#{$r} - var(--i)*var(--h))
请参阅 thebabydino 在 Pen 上的演示 (@thebabydino),并在 CodePen 上查看。
完成此操作后,我们可以根据奇偶性偏移切片。
--sign: calc(1 - 2*var(--parity));
transform: translate(calc(var(--sign)*2%))
现在我们有了切片圆盘!
请参阅 thebabydino 在 Pen 上的演示 (@thebabydino),并在 CodePen 上查看。
间隔开切片
第一个想到的方法是使用每个切片的 margin
。
请参阅 thebabydino 在 Pen 上的演示 (@thebabydino),并在 CodePen 上查看。
在某些情况下这可能是一个不错的结果,但是如果我们不想让圆盘变长怎么办?
好吧,我们可以选择将 background
限制在 content-box
中,并添加一个垂直 padding
box-sizing: border-box;
padding: .125em 0;
background: hsl(36, calc(var(--parity)*100%), calc(80% - var(--parity)*30%))
content-box;
当然,在这种情况下,我们需要确保 box-sizing
设置为 border-box
,这样垂直 padding
不会添加到 height
中。
请参阅 thebabydino 在 Pen 上的演示 (@thebabydino),并在 CodePen 上查看。
在这种情况下,唯一一个小问题是它也会切掉第一个切片的顶部和最后一个切片的底部。 在某些情况下这可能不是问题,我们始终可以 重置 :first-of-type
上的 padding-top
和 :last-of-type
上的 padding-bottom
为 0
.slice {
/* other styles */
padding: .125em 0;
&:first-of-type { padding-top: 0 }
&:last-of-type { padding-bottom: 0 }
}
但是,我们也有一个单行解决方案来解决在切片之间创建间隙的问题:在容器上添加一个 mask
!
此 mask
是一个 repeating-linear-gradient()
,它创建间隙 $g
厚度的透明条纹,在切片 height
后重复自身,并水平限制在圆盘直径 $d
内,垂直限制在圆盘直径 $d
减去一个间隙 $g
内(这样我们不会像最初使用 padding
方法那样遮盖掉最顶部和最底部)。
mask: repeating-linear-gradient(red 0, red calc(var(--h) - #{$g}),
transparent 0, transparent var(--h))
50% calc(50% - #{.5*$g})/ #{$d} calc(#{$d} - #{$g})
请注意,在这种情况下,我们需要在容器上设置切片 height
变量 --h
,因为我们在 mask
中使用它。
请参阅 thebabydino 在 Pen 上的演示 (@thebabydino),并在 CodePen 上查看。
background
连续 为了获得连续的渐变 background
,我们需要为该 background
指定与圆盘相同的高度,并将其垂直位置相对于每个切片设置,使其始终从组装的顶部开始……无论该位置相对于切片位于何处。
第一个切片(索引为 --i: 0
)的顶部与组装的顶部重合,因此我们的背景从垂直方向上的 0
开始。
第二个切片(索引为 --i: 1
)的顶部位于组装顶部下方 1
个切片高度,因此其 background
从垂直方向上方的 1
个切片 height
开始。 由于 y 轴的正方向向下,这意味着在这种情况下,我们沿 y 轴的 background-position
为 calc(-1*var(--h))
。
第三个切片(索引为 --i: 2
)的顶部位于组装顶部下方 2
个切片高度,因此其 background
从垂直方向上方的 2
个切片高度开始。 这使得我们沿 y 轴的 background-position
为 calc(-2*var(--h))
。
我们注意到这里有一个模式:通常,切片的沿 y 轴的 background-position
为 calc(-1*var(--i)*var(--h))
。
background:
linear-gradient(#eccc05, #c26e4c, #a63959, #4e2255, #333f3d)
/* background-position */
50% calc(-1*var(--i)*var(--h))/
100% $d /* background-size */
请参阅 thebabydino 在 Pen 上的演示 (@thebabydino),并在 CodePen 上查看。
但是,如果我们想要从左到右的渐变,那么我们的背景就不再是连续的了,如果我们稍微调整一下停止位置以获得突然的变化,这一点就会变得非常明显。
background: linear-gradient(90deg,
#eccc05 33%, #c26e4c 0, #a63959 67%, #4e2255 0, #333f3d)
/* background-position */
50% calc(-1*var(--i)*var(--h))/
100% $d /* background-size */
请参阅 thebabydino 在 Pen 上的演示 (@thebabydino),并在 CodePen 上查看。
为了解决此问题,我们将偏移量设置为 Sass 变量 $o
,将水平 background-size
设置为切片 width
(100%
或 $d
)加上两倍的偏移量,并确保我们为向左移动的切片(在 x 轴的负方向上,因此为 -$o
)在切片的左侧附加 background
(沿 x 轴的 background-position
为 0%
),以及为向右移动的切片(在 x 轴的正方向上,因此为 $o
)在切片的右侧附加 background
(沿 x 轴的 background-position
为 100%
)。
$o: 2%;
transform: translate(calc(var(--sign)*#{$o}));
background: linear-gradient(90deg,
#eccc05 33%, #c26e4c 0, #a63959 67%, #4e2255 0, #333f3d)
/* background-position */
calc((1 - var(--parity))*100%) calc(-1*var(--i)*var(--h))/
calc(100% + #{2*$o}) $d /* background-size */
请参阅 thebabydino 在 Pen 上的演示 (@thebabydino),并在 CodePen 上查看。
这适用于任何角度的渐变,正如以下交互式演示中所见——拖动以更改渐变角度。
请参阅 thebabydino 在 Pen 上的演示 (@thebabydino),并在 CodePen 上查看。
它也适用于图像,不过在这种情况下,我们需要删除第二个 background-size
值以防止图像失真,这会让我们在图像的纵横比大于 calc(#{$d} + #{2*$o}) : #{$d}
时出现垂直重复。 这对于我们下面使用的方形图像来说不是问题,但这仍然是需要牢记的事情。
请参阅 thebabydino 在 Pen 上的演示 (@thebabydino),并在 CodePen 上查看。
需要注意的另一点是,在上面,图像的顶部与组装的顶部相连。 如果我们希望图像的中间与组装的中间相连,我们需要稍微调整一下 background-position
的垂直分量。
首先,要将图像的中心与切片的中心对齐,我们需要使用 `background-position` 值为 `50%`。但我们不希望图像的中心位于每个切片的中心,而是希望它位于所有切片组合的中心。我们已经知道每个切片顶部到整个组合垂直中点的距离——它就是裁剪圆形中心的 y 坐标。
--y: calc(#{$r} - var(--i)*var(--h));
clip-path: circle($r at 50% var(--y))
每个切片垂直中点到组合垂直中点的距离就是这个值 `--y` 减去半个切片的高度。因此,为了使图像的垂直中点与组合的垂直中点对齐,我们沿 y 轴需要的 `background-position` 值为 `calc(50% + var(--y) - .5*var(--h))`。
查看 thebabydino 在 CodePen 上创建的 Pen (@thebabydino)。
递增切片
这意味着我们的切片不再具有相同的高度。例如,第一个切片可以具有一个单位高度,第二个切片具有两倍于此高度,第三个切片具有三倍于此高度,等等…
所有这些切片的累计高度应等于圆盘直径。换句话说,我们应该有以下等式
h + 2*h + 3*h + ... + n*h = d
这也可以写成
h*(1 + 2 + 3 + ... + n) = d
这让我们更容易注意到一些东西!括号内是前 `n` 个自然数的总和,它总是 `n*(n + 1)/2`!
所以我们的等式变为
h*n*(n + 1)/2 = d
这让我们可以得到单位高度 `h`
h = 2*d/n/(n + 1)
将其应用于我们的演示,我们有
--h: calc(#{2*$d}/var(--n)/(var(--n) + 1));
height: calc((var(--i) + 1)*var(--h));
查看 thebabydino 在 CodePen 上创建的 Pen (@thebabydino)。
与等高切片的情况一样,裁剪圆形 `circle()` 的中心点的 y 坐标为圆盘半径 `$r` 减去从组合顶部到当前切片顶部的距离。这是所有先前切片的高度之和。
对于第一个切片 (--i: 0
),我们没有先前切片,因此这个总和为 `0`。
对于第二个切片 (--i: 1
),我们之前只有第一个切片,它的高度为单位高度 (--h
)。
对于第三个切片 (--i: 2
),我们想要的总和是第一个切片高度(等于单位高度)和第二个切片高度(是单位高度的两倍)之间的总和。即 `calc(var(--h) + 2*var(--h))` 或 `calc(var(--h)*(1 + 2))`。
对于第三个切片 (--i: 3
),总和是第一个切片高度(等于单位高度)、第二个切片高度(是单位高度的两倍)和第三个切片高度(是单位高度的三倍)之间的总和。即 `calc(var(--h) + 2*var(--h) + 3*var(--h))` 或 `calc(var(--h)*(1 + 2 + 3))`。
现在我们可以看到一个模式出现了!对于每个索引为 `--i` 的切片,我们都有其先前切片的累计高度为单位高度 `--h` 乘以前 `--i` 个自然数的总和(前 `--i` 个自然数的总和为 `calc(var(--i)*(var(--i) + 1)/2)`)。这意味着我们的 `clip-path` 值变为
circle($r at 50% calc(var(--h)*var(--i)*(var(--i) + 1)/2))
我们加上偏移量,得到以下结果
查看 thebabydino 在 CodePen 上创建的 Pen (@thebabydino)。
不幸的是,使用递增切片意味着 `repeating-linear-gradient()` 掩码方法无法再用于创建间隙。但垂直 `padding` 方法仍然可以正常工作,我们可以设置填充值,使第一个切片的顶部值为 `0`,最后一个切片的底部值为 `0`。
padding:
calc(var(--i)*#{$g}/var(--n)) /* top */
0 /* lateral */
calc((var(--n) - 1 - var(--i))*#{$g}/var(--n)) /* bottom */
查看 thebabydino 在 CodePen 上创建的 Pen (@thebabydino)。
对于渐变 `background`,主要思想与等高切片的情况相同。我们只需要考虑两件事。
首先,沿 y 轴的 `background-position` 为负值,表示组合顶部到当前切片顶部之间的距离(绝对值)。此距离不再是 `calc(var(--i)*var(--h))`(与高度为 `--h` 的等高切片的情况一样)。而是,如之前计算的那样,为 `calc(var(--i)*(var(--i) + 1)/2*var(--h))`。因此,沿 y 轴的 `background-position` 为 `calc(-1*var(--i)*(var(--i) + 1)/2*var(--h))`。
其次,我们希望我们的 `background` 被裁剪到 `content-box`,以便保留间隙,但我们需要将 `background-origin` 保持为其初始值 `padding-box`,以使我们的渐变保持连续。
background:
linear-gradient(var(--a),
#eccc05, #c26e4c, #a63959, #4e2255, #333f3d)
/* background-position */
calc((1 - var(--parity))*100%) /* x component */
calc(-1*var(--i)*(var(--i) + 1)/2*var(--h)) /* y component */ /
/* background-size */
calc(100% + #{2*$o}) $d
padding-box /* background-origin */
content-box /* background-clip */;
查看 thebabydino 在 CodePen 上创建的 Pen (@thebabydino)。
对于其中心点与我们组合的中心点对齐的图像 `background`,我们需要考虑一个事实,即半个切片高度不再对所有切片都相同。现在,切片的高度为 `calc((var(--i) + 1)*var(--h))`,因此这是我们在 `background-position` 的 y 组件公式中需要减去的值。
--y: calc(#{$r} - .5*var(--i)*(var(--i) + 1)*var(--h));
background:
url(/amur_leopard.jpg)
/* background-position */
calc((1 - var(--parity))*100%) /* x component */
calc(50% + var(--y) - .5*(var(--i) + 1)*var(--h)) /* y component */ /
/* background-size */
calc(100% + #{2*$o})
padding-box /* background-origin */
content-box /* background-clip */;
clip-path: circle($r at 50% var(--y));
查看 thebabydino 在 CodePen 上创建的 Pen (@thebabydino)。
垂直切片
我们也可以沿另一个方向切片圆盘。这意味着从容器中删除 `flex-direction: column` 声明,并让 `flex-direction` 保持初始值(row),交换 `width` 和 `height`、圆形裁剪路径中心点的 x 和 y 坐标、我们移动切片的方向、掩码渐变的尺寸和 x 和 y 位置,我们还需要旋转它,以便它沿 x 轴移动。
body {
/* same as before */
--w: calc(#{$d}/var(--n));
mask: repeating-linear-gradient(90deg,
red 0, red calc(var(--w) - #{$g}),
transparent 0, transparent var(--w))
calc(50% - #{.5*$g}) 50% / calc(#{$d} - #{$g}) #{$d}
}
.slice {
/* same as before */
width: var(--w); height: $d;
transform: translatey(calc(var(--sign)*2%));
background: hsl(36, calc(var(--parity)*100%), calc(80% - var(--parity)*30%));
clip-path: circle($r at calc(#{$r} - var(--i)*var(--w)) 50%)
}
这将为我们提供具有交替背景的等高垂直切片
查看 thebabydino 在 CodePen 上创建的 Pen (@thebabydino)。
对于渐变情况,我们还需要反转两个 `background` 尺寸以及沿 x 和 y 轴的 `background` 位置
background:
linear-gradient(135deg,
#eccc05 15%, #c26e4c, #a63959, #4e2255, #333f3d 85%)
/* background-position */
calc(-1*var(--i)*var(--w)) calc((1 - var(--parity))*100%)/
#{$d} calc(100% + #{2*$o}) /* background-size */
查看 thebabydino 在 CodePen 上创建的 Pen (@thebabydino)。
对于递增切片,我们将递增情况与垂直情况结合起来,这意味着交换我们先前沿两个轴的递增情况下获得的值
--w: calc(#{2*$d}/var(--n)/(var(--n) + 1));
width: calc((var(--i) + 1)*var(--w)); height: $d;
clip-path: circle($r at calc(#{$r} - .5*var(--i)*(var(--i) + 1)*var(--w)) 50%);
查看 thebabydino 在 CodePen 上创建的 Pen (@thebabydino)。
为了创建间隙,我们使用 `padding` 方法。但由于我们现在处于垂直情况,我们需要水平填充,在左侧和右侧,并确保第一个切片的 `padding-left` 为 `0`,最后一个切片的 `padding-right` 也为 `0`
box-sizing: border-box;
padding:
0 /* top */
calc((var(--n) - 1 - var(--i))*#{$g}/var(--n)) /* right */
0 /* bottom */
calc(var(--i)*#{$g}/var(--n)) /* left */;
background:
hsl(36, calc(var(--parity)*100%), calc(80% - var(--parity)*30%))
content-box
查看 thebabydino 在 CodePen 上创建的 Pen (@thebabydino)。
最后,我们有渐变情况
background:
linear-gradient(135deg,
#eccc05 15%, #c26e4c, #a63959, #4e2255, #333f3d 85%)
/* background-position */
calc(-.5*var(--i)*(var(--i) + 1)*var(--w)) /* x component */
calc((1 - var(--parity))*100%) /* y component */ /
/* background-size */
#{$d} calc(100% + #{2*$o})
padding-box /* background-origin */
content-box /* background-clip */;
查看 thebabydino 在 CodePen 上创建的 Pen (@thebabydino)。
二维情况
同样,我们使用一些 Pug 代码生成它,项目的总数是列数和行数的乘积。为简单起见,我们将行数和列数保持相等。
- var n = 8, m = Math.pow(n, 2);
style :root { --n: #{n}; --i: 0; --j: 0 }
- for(var i = 1; i < n; i++) {
| .tile:nth-of-type(#{n}n + #{i + 1}) { --i: #{i} }
| .tile:nth-of-type(n + #{n*i + 1}) { --j: #{i} }
- }
- for(var i = 0; i < m; i++)
.tile
我们还将列索引和行索引(分别为 `--i` 和 `--j`)传递给 CSS。
由于我们处于二维情况,因此我们从使用一维布局 (flex) 切换到使用二维布局 (grid)。我们还从圆盘直径 `$d` 开始,并且考虑到列数等于行数 (--n
),我们的圆盘将被分成边长为 `--l: calc(#{$d}/var(--n))` 的相同瓦片。
$d: 20em;
body {
--l: calc(#{$d}/var(--n));
display: grid;
place-content: center;
grid-template: repeat(var(--n), var(--l))/ repeat(var(--n), var(--l))
}
为了在瓦片之间创建间隙,我们在 `tile` 元素上使用 `padding` 方法,并将水平情况和垂直情况结合起来,以便第一行的 `padding-top` 为 `0`,第一列的 `padding-left` 为 `0`,最后一行 `padding-bottom` 为 `0`,最后一列的 `padding-right` 为 `0`。
padding:
calc(var(--j)*#{$g}/var(--n)) /* top */
calc((var(--n) - 1 - var(--i))*#{$g}/var(--n)) /* right */
calc((var(--n) - 1 - var(--j))*#{$g}/var(--n)) /* bottom */
calc(var(--i)*#{$g}/var(--n)) /* left */
注意,我们使用行索引 `--j` 表示从上到下的方向(垂直填充),使用列索引 `--i` 表示从左到右的方向(水平填充)。
为了获得圆盘形状,我们再次将水平情况和垂直情况结合起来,使用列索引 `--i` 来获取圆形裁剪路径中心点的 x 坐标,使用行索引 `--j` 来获取其 y 坐标。
clip-path:
circle($r at calc(#{$r} - var(--i)*var(--l))
calc(#{$r} - var(--j)*var(--l)))
查看 thebabydino 在 CodePen 上创建的 Pen (@thebabydino)。
对于渐变 `background`,它再次将水平情况和垂直情况结合起来,并考虑到在这里我们此时没有偏移量,这意味着 `background-size` 在两个轴上的圆盘直径均为 `$d`。
background:
linear-gradient(135deg,
#eccc05 15%, #c26e4c, #a63959, #4e2255, #333f3d 85%)
/* background-position */
calc(-1*var(--i)*var(--l))
calc(-1*var(--j)*var(--l)) /
#{$d} #{$d} /* background-size */
padding-box /* background-origin */
content-box /* background-clip */
查看 thebabydino 在 CodePen 上的 @thebabydino 创建的 Pen。
对于图像 `background`,我们删除了第二个 `background-size` 值,以防止图像在非方形情况下被拉伸。我们还将代码从 1D 案例调整到 2D 案例,以将图像的中点附加到网格的中点。
--x: calc(#{$r} - var(--i)*var(--l));
--y: calc(#{$r} - var(--j)*var(--l));
background: url(/amur_leopard.jpg)
/* background-position */
calc(50% + var(--x) - .5*var(--l))
calc(50% + var(--y) - .5*var(--l)) /
#{$d} /* background-size */
padding-box /* background-origin */
content-box /* background-clip */;
clip-path: circle($r at var(--x) var(--y))
查看 thebabydino 在 CodePen 上的 @thebabydino 创建的 Pen。
在增量情况下,我们没有所有瓷砖的相同尺寸,因此我们使用 `auto` 大小来设置 `grid-template`。
body {
/* same as before */
grid-template: repeat(var(--n), auto)/ repeat(var(--n), auto)
}
就像在 1D 案例中一样,我们首先计算一个单位边长 `--u`。
--u: calc(#{2*$d}/var(--n)/(var(--n) + 1))
然后,我们沿着两个轴为瓷砖元素设置增量尺寸。
width: calc((var(--i) + 1)*var(--u));
height: calc((var(--j) + 1)*var(--u))
我们还需要将剪切圆形中心点的坐标适应增量情况。
clip-path:
circle($r at calc(#{$r} - .5*var(--i)*(var(--i) + 1)*var(--u))
calc(#{$r} - .5*var(--j)*(var(--j) + 1)*var(--u)))
查看 thebabydino 在 CodePen 上的 @thebabydino 创建的 Pen。
对于渐变 `background`,我们将等瓷砖版本适应增量情况。这意味着像以前为增量切片一样调整 `background-position`,只是这次我们沿着两个轴进行,而不仅仅是沿着一个轴进行。
background:
linear-gradient(135deg,
#eccc05 15%, #c26e4c, #a63959, #4e2255, #333f3d 85%)
/* background-position */
calc(-.5*var(--i)*(var(--i) + 1)*var(--l))
calc(-.5*var(--j)*(var(--j) + 1)*var(--l)) /
#{$d} #{$d} /* background-size */
padding-box /* background-origin */
content-box /* background-clip */
查看 thebabydino 在 CodePen 上的 @thebabydino 创建的 Pen。
最后,我们有增量 2D 案例的图像 `background` 选项。
background: url(/amur_leopard.jpg)
/* background-position */
calc(50% + var(--x) - .5*(var(--i) + 1)*var(--u))
calc(50% + var(--y) - .5*(var(--j) + 1)*var(--u)) /
#{$d} /* background-size */
padding-box /* background-origin */
content-box /* background-clip */
查看 thebabydino 在 CodePen 上的 @thebabydino 创建的 Pen。
我们可能还能想出更多变体,但我们将在此处停止。如果您有更多关于如何进一步推动此项工作的想法,我很想听听!
你做得很好。
如果网格部分在悬停时连接起来,以显示完整的圆形图像,那么悬停图像效果会很棒。
这是一个很酷的想法!我的尝试。
这太棒了。就像一篇关于 CSS 的论文。
这是一次疯狂的旅程。我真的很希望今年能拥抱 Grid。IE 支持以及可靠支持它所需的大量体操,让我一直对它敬而远之。
这太棒了,而且悬停效果也非常棒!为整个圆圈添加动画绝对是一种方法。我希望我能做到,但我从未在 CSS 设计中使用过任何计算。您知道哪些好的资源可以帮助我入门吗?
谢谢!