IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    CSS魔法!如何将任意CSS类型转换为数值?

    XboxYan发表于 2023-10-16 10:04:09
    love 0
    欢迎关注我的公众号前端侦探
    本技巧参考自:https://dev.to/janeori/css-type-casting-to-numeric-tanatan2-scalars-582j

    在 CSS 中有各式各样的类型值,例如 1rem、10vw、100cqw等等,这些相对值给与了 CSS 强大的适应能力。但有时候,我们还需要知道这些相对值所对应的真实值,也就是px值,比如在移动端,我们经常会设置这样的根字号:

    :root{
      font-size: max(12px, min( 12px + (100vw - 320px) / 55 * 4, 20px ));
    }
    p{
      font-size: 2rem;
    }

    那么,p此时的真实px值应该是多少呢?

    在以往,我们只能通过 JS去获取,但是现在,仅仅凭 CSS也能做到,一起了解一下吧

    一、CSS 三角函数

    从理论上来讲,将一个长度除以1px就应该得到具体的数值,相当于将这个长度以1px被分成了多少份

    2rem / 1px

    但是,大家可能都知道,calc在进行除法运算时,除数是不能带单位的

    calc(2rem / 1px) /*不合法*/
    不排除浏览器以后会支持

    不过现在终于迎来了转机,在 Chrome 111中,CSS 新增了一系列三角函数

    • sin()'):正弦
    • cos()'):余弦
    • tan()'):正切
    • asin()'):反正弦
    • acos()'):反余弦
    • atan()'):反正切
    • atan2()'):反正切
    这里我们不聊三角函数的作用,只是作为一个桥梁中转一下

    关于正切函数,这里简单回顾一下高中数学

    在直角三角形中,其中一个锐角的对边和临边的比值就是这个角的正切值,反正切就是通过两条直角边反向去求出该角的弧度值,示意如下:

    image.png

    这些函数本身没什么特别的,业界通用,但有一个非常例外,那就是atan2,它和atan作用是一样的,都是反正切函数。唯一的区别是,atan2支持两个值

    前端中的 atan 就是数学中的 arctan
    atan2(y, x)

    例如:

    transform: rotate(atan2(3, 2));
    /*等同于*/
    transform: rotate(atan(1.5));

    看似好像没啥意义,直接用tan不就好了?🤔

    transform: rotate(atan2(3/2));

    和JS不同的是,CSS 中还能支持单位,如下

    transform: rotate(atan2(1rem, 20px));
    /*等同于?*/

    这就不得了了,如果此时的1rem是16px,那么浏览器会根据1rem的真实长度和20px做对比,相当于让两个带单位的长度做除法运算

    /*等同于*/
    transform: rotate(atan(.8)); /* 16px / 20px */ 

    也就是说,y/x的值可以得到两个CSS长度之间的真实比值,而无需额外转换。

    image.png

    然后,通过正切函数tan将括号里面的值解析出来

    tan(atan2(1rem, 20px)) /*.8*/

    这一点其实在 JS 中也可以验证一下,只是 CSS 可以带单位的值

    image.png

    如果将后面的值设置为1px,就相当于把任意长度按照1px进行切分,也就得到了任意长度的px数值,如下

    tan(atan2(1rem, 1px)) /*16*/

    有了这个不带单位的绝对值,我们就可以做很多事情了,下面举几个例子

    二、CSS 获取当前字号

    回到文章开头的问题,如何通过 CSS 获取当前字号呢?

    根据上一节的原理,只要通过tan() 和 atan2()就可以将任意长度转换为数值了

    :root{
      font-size: max(12px, min( 12px + (100vw - 320px) / 55 * 4, 20px ));
    }
    body{
      --px-font: tan(atan2(1rem, 1px));
    }

    这样--px-font就得到了真实的px数值。

    然后,我们还可以借助 CSS计数器,将这个数字渲染到页面

    关于这个技巧,可以参考之前的这篇文章:如何让CSS计数器支持小数的动态变化
    body::after{
      counter-reset: font var(--px-font);
      content: "当前根字号:" counter(font);
    }

    结果...效果如下

    image.png

    好像不太对?貌似是浏览器的 bug,这种情况下忽略了tan(atan2(1rem, 1px))中的1rem单位。

    要修复这个问题其实很容易,需要通过@property定义一个CSS变量,类型为长度,然后将需要计算的值赋给这个变量,如下

    @property --font {
      syntax: "<length>";
      initial-value: 1px;
      inherits: false;
    }
    :root{
      font-size: max(12px, min( 12px + (100vw - 320px) / 55 * 4, 20px ));
    }
    body::after{
      --font: 1rem; /*定义好的CSS变量*/
      --px-font: tan(atan2(var(--font), 1px));
      counter-reset: font var(--px-font);
      content: "当前根字号:" counter(font);
    }

    这样就正常了,在改变屏幕尺寸时会自动渲染出当前字号

    Kapture 2023-10-12 at 19.52.05.gif

    你也可以访问在线链接

    • CSS font-size (codepen.io)')

    三、实时显示容器尺寸

    CSS容器查询中出现了几个新的尺寸单位。这里只介绍下面两种

    关于容器查询,可以参考这篇文章:介绍2022最期待且已正式支持的CSS container容器查询 « 张鑫旭-鑫空间-鑫生活 (zhangxinxu.com),本文并不涉及具体容器查询语句
    • cqw 容器查询宽度(Container Query Width)占比。1cqw等于容器宽度的1%。假设容器宽度是1000px,则此时1cqw对应的计算值就是10px。
    • cqh 容器查询高度(Container Query Height)占比。1cqh等于容器高度的1%。

    利用前面的技巧,可以将cqw、cqh这些单位转换为具体的px值。

    假设有这样一个容器

    .box{
      display: grid;
      place-content: center;
      width: 200px;
      height: 200px;
      background-color: #fff;
    }

    为了使容器尺寸生效,必须声明容器类型

    .box{
      /**/
      container-type: size;
    }

    然后用同样的技巧,在伪元素中通过计数器将宽高渲染出来

    .box::before{
      --w: 100cqw;
      --h: 100cqh;
      --px-width: tan(atan2(var(--w), 1px));
      --px-height: tan(atan2(var(--h), 1px));
      counter-reset: w var(--px-width) h var(--px-height);
      content: counter(w) " x " counter(h);
    }

    别忘了修复 bug

    @property --w {
      syntax: "<length>";
      initial-value: 100px;
      inherits: false;
    }
    @property --h {
      syntax: "<length>";
      initial-value: 100px;
      inherits: false;
    }

    效果如下,完全没有任何 JS

    Kapture 2023-10-12 at 20.19.43.gif

    你也可以访问在线链接

    • CSS resize (codepen.io)')

    四、自适应文本的头像

    下面看一个自适应文本的头像,当文本较多时,会自动缩放,让文本可以完整展示,如下

    image.png

    之前在这篇文章中有详细介绍:CSS 实现自适应文本的头像,有兴趣的可以回顾一下。

    原理其实是通过容器尺寸的映射关系来动态设置文本大小,关键实现如下

    .avator-container span {
      font-size: calc( 24px - 10cqw );
    }

    但是,通过文字大小来缩放有一个限制,在PC上,一般会有最小字号限制(通常是12号),所以更好的方式应该是通过scale来实现。

    现在,我们可以通过上面的技巧,将容器尺寸转换成scale能够识别的数值,通过文本宽度和容器尺寸的比值来确实缩放比例,关键实现如下

    .avator-container span {
      --w: 100cqw;
      --scale: tan(atan2(30px, var(--w)));
      transform: scale(var(--scale));
    }

    这样就得到了完全自适应尺寸的文本头像了

    完整代码可以访问在线链接

    • CSS avator auto scale (codepen.io)')

    五、总结一下

    以上就是本文的全部内容了,介绍了一个非常巧妙并且有用的 CSS 小技巧,下面总结一下

    1. CSS 有很多类型的尺寸,这些尺寸给与了 CSS 强大的适应能力
    2. 从理论上讲,将一个长度除以 1px 就可以得到具体的数值,但是calc不支持
    3. CSS 反正切函数 atan2(y,x) 支持两个参数,并且还支持带CSS单位
    4. 通过 tan(atan2(y,x))可以得到y/x的比值,如果x是1px,可以得到y的实际px值
    5. 利用这个技巧,可以将任意类型值转换为数值
    6. 纯数值可以在各个地方都可以使用了,比如scale(var(--n)),如果需要 px,直接calc(var(--n) * 1px )

    最后,如果觉得还不错,对你有帮助的话,欢迎点赞、收藏、转发❤❤❤

    欢迎关注我的公众号前端侦探


沪ICP备19023445号-2号
友情链接