搞前端的应该都听说过,CSS的三角函数在去年被评为了“最让人头秃”的特性。这玩意儿真有那么难用吗?其实吧,只要弄明白tan()到底是啥,就会发现它简直是为做多边形形状量身定制的神器。
tan()是个啥玩意儿
从数学课本上讲,tan()就是一个角度的正切值,等于这个角度的对边长度除以邻边长度。放到一个直角三角形里头,假如选了个锐角,那么tan()就等于对面的那条边长度除以旁边的那个边长度。这玩意儿在CSS里最大的用处,就是帮咱们算出那些看起来特别规整的多边形切片到底该有多大。
从零开始造个多边形菜单
搭个架子
先整一个无序列表,里头放上咱们需要的菜单项。每个菜单项都得有个独立的编号,方便后面计算。
<ul style="--total: 6">
<li style="--i: 1">🍕</li>
<li style="--i: 2">🍔</li>
<li style="--i: 3">🍟</li>
<li style="--i: 4">🌭</li>
<li style="--i: 5">🥗</li>
<li style="--i: 6">🍦</li>
</ul>这里用--total记录了总共有几个菜单项,--i记录了每个项目自己的序号。写HTML的时候可以手动把这些变量塞进去,等以后浏览器支持了sibling-index,就能自动识别了,但现在先这么干着。
计算旋转角度
每个菜单项都得绕着中心点转一定的角度,这样才能形成一个完整的圆环。这个角度很好算,一圈360度除以总个数,再乘上当前项目的序号。
li {
--angle: calc(360deg / var(--total) * var(--i));
transform: rotate(var(--angle));
}直接这么一转,所有元素都堆在一块儿了,因为它们的旋转中心都在各自的正中间。这时候得把transform-origin改成left center,让它们都往左偏一点。
定位三角形
先把每个元素挪到正确的位置上,再旋转。注意顺序不能搞反,得先平移再转。
li {
--radius: 35vmin;
width: var(--radius);
transform: translateX(calc(var(--radius) / 2)) rotate(var(--angle));
transform-origin: left center;
}这样摆出来之后,每个元素就像风扇叶子一样向外辐射开了。但是它们都还是长方形,得用clip-path切成三角形。
li {
clip-path: polygon(100% 0, 0 50%, 100% 100%);
}切完以后,看起来就像是披萨被切了一刀,但是每块之间都有缝隙,没完全接上。这是因为每个三角形的高度不对,需要重新算。
用tan()算完美高度
想象一下,把每个三角形从中间劈开,就变成了两个直角三角形。这两个直角三角形的角度,正好等于一圈360度除以总个数的两倍。知道了角度,又知道了三角形的宽度(就是--radius),就能用tan()算出对边的高度了。
li {
--theta: calc(360deg / 2 / var(--total));
height: calc(2 * var(--radius) * tan(var(--theta)));
}这个公式算出来的高度,能让三角形的尖尖刚好挨在一起,形成一个严丝合缝的多边形。如果总共有6个菜单项,算出来就是一个六边形;如果是8个,就是个八边形。可以随便改--total的值,形状自动适配。
玩转transform-origin还能整出不一样的效果。如果把旋转中心从left center换成center center,就能做出从中心往外辐射的效果,看起来像是那种可以展开收缩的圆形菜单。
搞个高能版圆形选人盘
把上面做的多边形外面再套一层圆形裁剪,立马就能变成游戏里那种选角色的转盘。
ul {
clip-path: circle(50% at 50% 50%);
}每个菜单项还是三角形的尖角,但是外层容器被裁成圆了,看起来就像是每个菜单项都是圆形的一部分。这招在做那种角色选择、颜色切换、或者类似游戏手柄的轮盘菜单时特别管用。
把菜单项摆成多边形相册
一样的原理,把每个三角形换成图片,就能整出多边形相册墙。每个图片都被裁剪成三角形,拼在一起就成了一个完整的正多边形。鼠标滑上去还能给每个三角形单独加特效,交互感直接拉满。
这背后全靠tan()撑腰,它让每个三角形的高度能根据角度自动算出来。要是没有它,就得每个角度都手动调高度,换一次菜单项数量就得重新算一次,头都能大。
tan()在CSS里最大的本事,就是解决那些“知道角度、知道一条边、想求另一条边”的问题。只要遇到这种直角三角形的场景,直接上tan()准没错。以后再也不用对着那些奇奇怪怪的多边形形状挠头了,数学公式一摆,代码一敲,完美形状就出来了。
