想要搞明白CSS里的asin()、acos()、atan()和atan2()这些反三角函数到底有啥用,先得从三角函数那点事儿唠起。之前咱聊过sin()、cos()和tan(),它们就像是一个翻译官,给一个角度,就能返回一个比值。但现实里经常碰到反过来的时候:手头有个比值,比如两条边的长度,就想知道那个角度是多少度,这活儿就得反三角函数来干了。
从比值挖出那个角
acos()是cos()的逆向操作,asin()是sin()的逆向操作,atan()和atan2()是tan()的逆向操作。它们还有个挺文艺的名字叫“弧函数”,因为在单位圆里,每个角度都对应着圆弧上一段长度。
有个点得先拎清楚,acos()和asin()这俩家伙有点“挑食”,它们只能接收-1到1之间的数字。为啥呢?因为cos()和sin()不管塞进去多大的角度,吐出来的结果永远在这个范围里晃悠。所以acos(0)可能对应90°,也可能对应270°,为了不打架,acos()只返回0°到180°之间的角,而asin()只返回-90°到90°之间的角。
干活前先掂量掂量
acos()和asin()的“挑食”特性,在实际干活时限制挺大。比如直接甩个asin(1.2)过去,CSS立马给你返回个NaN,直接懵圈。这俩更适合那些已经知道比值肯定在合法范围内的场景,比如处理某些特定角度的三角形。
解决渐变不走心的麻烦
在搞渐变的时候,经常碰到个糟心事:想让圆锥渐变(conic-gradient)的背景色块随着盒子大小变化,自动调整起始角度,而不是死死固定住。用linear-gradient时还能用to top right这种关键字糊弄过去,但conic-gradient可没这待遇。
换个思路,把盒子的宽度和高度看作直角三角形的两条直角边。宽是邻边,高是对边,想找到斜边对应的那个角度,atan(高 / 宽)直接安排上。
.element {
--宽度: 300px;
--高度: 200px;
--角: atan(var(--高度) / var(--宽度));
/* 圆锥渐变默认从顶部开始,得转一下 */
--旋转: calc(90deg - var(--角));
background: conic-gradient(from var(--旋转),
#84a59d 180deg, #f28482 180deg);
}这么一搞,不管盒子是宽屏还是瘦高个,渐变那个分割线永远老老实实对着对角。这法子同样能用在双色渐变卡片的制作上,把一个渐变劈成两半,各自从对角往中间跑,拼出来的效果绝绝子。
让眼睛跟着鼠标转
atan2(y, x)这个函数比atan()更懂事儿。atan()处理的是除法,正负号一搅和,角度就容易搞混。比如点(1, 1)和(-1, -1),两者比值都是1,atan()根本分不清谁是谁。atan2()就不一样了,它把x和y分开送进去,能精准定位到四个象限的任何位置。
做个会盯着鼠标看的小东西,比如一个卡通角色的眼睛。先得用点JS把鼠标的坐标抓出来,塞到CSS变量里。
const 页面 = document.querySelector("body");
页面.addEventListener("pointermove", (事件) => {
let 横坐标 = 事件.clientX;
let 纵坐标 = 事件.clientY;
页面.style.setProperty("--鼠标-x", `${横坐标}px`);
页面.style.setProperty("--鼠标-y", `${纵坐标}px`);
});现在CSS里就有了--鼠标-x和--鼠标-y。假设眼睛默认是朝右看的(正x轴方向),把眼睛初始旋转135deg调整个舒服的姿势,然后用atan2()算出需要转的角度。
.眼睛::before,
.眼睛::after {
rotate: calc(135deg + atan2(var(--鼠标-y), var(--鼠标-x)));
}这么一写,鼠标挪哪儿,眼睛就瞄哪儿。如果想更精准点,把角色自身的偏移量也减掉,让坐标系原点落在角色身上。
.眼睛::before,
.眼睛::after {
rotate: calc(
135deg +
atan2(
var(--鼠标-y) - var(--内边距) - var(--角色尺寸) / 2,
var(--鼠标-x) - var(--内边距) - var(--角色尺寸) / 2
)
);
}把视口宽度变成数字
atan2()和tan()这俩组合拳还能干件神奇的事儿——把带单位的长度(比如100vw)转换成纯粹的整数。具体咋整呢?
先定义一个自定义属性--100vw,类型是<length>,初始值0px,不让它继承。然后在根元素上把--100vw设置成100vw。接着就能用数学魔法了:
:root {
--100vw: 100vw;
--整数宽度: calc(10000 * tan(atan2(var(--100vw), 10000px)));
}atan2(100vw, 10000px)算出的是一个角度,这个角度再丢给tan(),就能把单位给消掉,变成纯数字。乘上10000之后,--整数宽度就是视口的整数宽度。这招在需要根据视口尺寸动态调整样式时,简直不要太爽。
那些藏在背后的兄弟
三角函数还有三个“倒过来的兄弟”:正割sec(x)是1/cos(x),余割csc(x)是1/sin(x),余切cot(x)是1/tan(x)。虽然CSS里没直接提供,但用现有的函数一样能算出来。它们跟sin、cos、tan一起,都住在那个熟悉的单位圆里,描述着圆上各个点与角度之间的各种关系。
