乐趣区

关于前端:如何让CSS计数器支持小数的动态变化

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

不得不说,CSS 计数器是个好货色。

最近在几篇文章中都用到了 CSS 计数器,能够将 CSS 变量通过伪元素 content 动静展现进去,还能够做出很多乏味的动画。有趣味的能够先回顾一下之前的这几篇文章:

  • 还在应用定时器吗?CSS 也能实现电子时钟
  • 动画合成小技巧!CSS 实现动感的倒计时成果
  • 自定义计数器小技巧!CSS 实现长按点赞累积动画

原理其实很简略,content尽管自身不反对 CSS 变量间接渲染,然而能够反对counter-reset

count::before {
      --percent: 50;
    counter-reset: progress var(--percent);
    content: counter(progress);
}

通过一次直达,就能够让 content 也能反对 CSS 变量作为字符展现了

这个技巧是通过张鑫旭的这篇文章理解的,十分实用:小 tips: 如何借助 content 属性显示 CSS var 变量值

然而,这个办法有个比拟遗憾的中央就是,CSS 计数器不反对真正意义上的小数,也就是如果 CSS 变量为小数的话,间接展现为 0

count::before {
      --percent: 50.15;
    counter-reset: progress var(--percent);
    content: counter(progress);
}

那么,如何让 content 也反对 CSS 变量的小数展现呢,毕竟很多状况下还是须要小数的?比方上面这个,如果反对了小数,就能够轻易的实现数字的滚动动画

明天一起来探讨一下

一、CSS 原理拆解

CSS 计数器因为特殊性,目前都是仅反对整数的,毕竟天然个数是没有小数的(不排除当前自定义计数器能够实现)。既然这样,能够换一种思路,从数字状态上进行拆分。比方一个小数,48.69能够分解成整数局部 48 和小数局部69,而后再通过小数点链接起来。这样拆分后就都是整数了,CSS 计数器也是反对的

用代码实现就是(便于了解,以下的一些变量都是中文命名的,理论生产不举荐)

count::before {
  -- 整数: 48;
  -- 小数: 69;
  counter-reset: 整数计数器 var(-- 整数) 小数计数器 var(-- 小数);
  content: counter(整数计数器) "." counter(小数计数器);
}

所以问题就变成了,如何将一个小数进行拆分呢?

二、CSS 变量拆分成整数和小数

接着下面的问题,假如变量是 --percent,问题就是上面两个变量-- 整数-- 小数 如何通过 --percent 计算而来呢?

count::before {
  --percent: 48.69;
  -- 整数: 48;
  -- 小数: 69;
  counter-reset: 整数计数器 var(-- 整数) 小数计数器 var(-- 小数);
  content: counter(整数计数器) "." counter(小数计数器);
}

看似很容易,但在 CSS 中如同并不怎么好实现。

为了解决这个,须要理解一下 CSS 自定义变量的类型。类型有很多,上面列举一下

  • <length>
  • <number>
  • <percentage>
  • <length-percentage>
  • <color>
  • <image>
  • <url>
  • <integer>
  • <angle>
  • <time>
  • <resolution>
  • <transform-function>
  • <custom-ident>
  • <transform-list>

大部分能能够看出具体的类型,咱们这里须要用到的就两种,<number><integer>,两者都示意数字,具体的区别在于

  • <number>示意任意的数字,整数和小数都能够
  • <integer>示意整型数字,只能是整数,小数会认为不非法

回到这里,默认状况下,CSS 变量能够是任意值,然而通过自定义变量 @property 能够指定变量的类型,它能够对不非法的变量进行转换。

@property – CSS(层叠样式表)| MDN (mozilla.org)

比方,咱们须要一个整数,能够这样来定义,将 syntax 属性设置为 <integer> 就能够了

@property -- 整数 {
  syntax: "<integer>"; /* 整型 */
  initial-value: 0;
  inherits: false;
}

这样,这个变量会被强制转换成整数。比方,上面给 -- 整数 也设置成一个小数

count::before {
  --percent: 48.69;
  -- 整数: 48.69;
  -- 小数: 69;
  counter-reset: 整数 var(-- 整数) 小数 var(-- 小数);
  content: counter(整数) "." counter(小数);
}

后果 …

竟然间接变成了 0

不过没关系,须要能够配合一些 CSS 计算函数实现主动转换,比方calc

count::before {
  --percent: 48.69;
  -- 整数: calc(48.69);/* 应用 CSS 计算后能够转换成整数 */
  -- 小数: 69;
  counter-reset: 整数 var(-- 整数) 小数 var(-- 小数);
  content: counter(整数) "." counter(小数);
}

然而,这里变成了 49,起因 其实是四舍五入造成的,并不是向下取整。为了打消这种误差,能够再减去0.5,所以整数局部的最终实现就是

@property -- 整数 {
  syntax: "<integer>";
  initial-value: 0;
  inherits: false;
}
count::before {
  --percent: 48.69;
  -- 整数: calc(var(--percent) - 0.5);
  -- 小数: 69;
  counter-reset: 整数 var(-- 整数) 小数 var(-- 小数);
  content: counter(整数) "." counter(小数);
}

将来的 CSS 数学函数应该也会有 floor、ceil 这样的,能够期待一下~

而后是小数局部,有了整数局部,小数局部就容易了,能够用整个值减去整数局部,而后乘以 100,示意如下

用代码实现就是

@property -- 小数 {
  syntax: "<integer>";
  initial-value: 0;
  inherits: false;
}
count::before {
  --percent: 48.69;
  -- 整数: calc(var(--percent) - 0.5);
  -- 小数: calc((var(--percent) - var(-- 整数)) * 100 - 0.5);
  counter-reset: 整数 var(-- 整数) 小数 var(-- 小数);
  content: counter(整数) "." counter(小数);
}

成果如下

前面最末位的小数因为四舍五入的关系略微有些偏差,没关系,能够修改一下,加上 0.01 就行了。其次,还有一个问题,当小数位小于 10 的时候,计算出的后果可能是这样

那么,这种状况就须要动静补零了。

对于“补零”的技巧,之前在这篇文章中有过具体介绍:CSS 也能主动补全字符串?

所以,只须要在计数器前面定义一下计数器款式decimal-leading-zero,示意十进制前置零,最终实现如下

count::before {
  --percent: 48.69;
  -- 整数: calc(var(--percent) - 0.5);
  -- 小数: calc((var(--percent) - var(-- 整数)) * 100 - 0.5 + 0.01);
  counter-reset: 整数 var(-- 整数) 小数 var(-- 小数);
  content: counter(整数) "." counter(小数, decimal-leading-zero);
}

这样整数和小数都能够用同一个变量 --percent 示意进去了,完满~

三、CSS 变量动画

有人可能会感觉,为啥要废这么大劲去实现这样一个性能?用 js 间接设置不行吗?如果仅仅是数字的变动,那当然能够,但在这里,除了 CSS 繁多变量带来更好的可维护性外,还能够做到连 JS 也难以做到(或者说老本更高)的事件,比方 过渡动画

首先,再改良一下,很多小数都是百分比模式的,也就是 0~1 范畴内,所以后面 --percent 可能是这样的值0.4869

count::before {
  --percent: 0.4869;
  -- 百分比: calc(var(--percent) * 100);
  -- 整数: calc(var(-- 百分比) - 0.5);
  -- 小数: calc((var(-- 百分比) - var(-- 整数)) * 100 - 0.5 + 0.01);
  counter-reset: 整数 var(-- 整数) 小数 var(-- 小数);
  content: counter(整数) "." counter(小数, decimal-leading-zero) "%";
}

成果如下

而后,咱们通过 JS 让这个数字随机变动

count.addEventListener('click', ev => {ev.target.style.setProperty("--percent", Math.random());
})

成果如下

然而,这样太死板了,咱们须要数字变动的时候有个动画,能够间接通过 CSS 自定义变量实现

@property --percent {
  syntax: "<number>";
  initial-value: 0;
  inherits: false;
}
count{
  /**/
  transition: --percent 1s
}

当初看看成果,十分轻松的就实现了数字的滚动动画

小数局部因为是追随整数局部的,比方整数从 1 变为3,那么小数局部就追随变动两个循环。原本这个也十分合乎常理,就像时钟的秒永远要比分要转的快一样,然而有人可能感觉变的太快了,有没有方法让小数局部和整数局部独立开来呢?当然也是能够的,而且非常容易,只须要给整数局部和小数局部别离设置过渡就行了

count{
  /**/
  transition: -- 整数 1s, -- 小数 1s;
}

当初再看看成果,和下面比照一下

这两种成果能够自行抉择,仅仅只是过渡的不同

试想一下,如果这个成果用 JS 来实现,是不是还有点点麻烦呢?

上面是残缺代码(不多,就这么几行)

@property --percent {
  syntax: "<number>";
  initial-value: 0;
  inherits: false;
}
@property -- 整数 {
  syntax: "<integer>";
  initial-value: 0;
  inherits: false;
}
@property -- 小数 {
  syntax: "<integer>";
  initial-value: 0;
  inherits: false;
}
count {
  --percent: 0.4512;
  font-size: 60px;
  font-weight: bolder;
  cursor: pointer;
  font-family: 'Courier New', Courier, monospace;
  -- 百分比: calc(var(--percent) * 100);
  -- 整数: calc(var(-- 百分比) - 0.5);
  -- 小数: calc((var(-- 百分比) - var(-- 整数)) * 100 - 0.5 + 0.01);
  counter-reset: 整数 var(-- 整数) 小数 var(-- 小数);
  transition: -- 整数 1s, -- 小数 1s;
}
count::before {content: counter(整数) "." counter(小数, decimal-leading-zero) "%";
}

你也能够拜访线上 demo:CSS double num(runjs.work)

RunJS,前端代码在线创作与分享。

四、总结和阐明

以上就是全部内容了,一个还不错的小技巧,你学会了吗?

  1. CSS 变量不反对间接在 content 中渲染,然而能够借助计数器初始化来实现
  2. CSS 计数器不反对小数初始化
  3. CSS 计数器反对小数的实现原理在于将小数拆分为整数、小数点、小数三个局部
  4. CSS 自定义变量能够指定变量的类型,这样通过 CSS 数学函数能够将一个小数转换成整数
  5. 小数局部能够通过减去整数局部失去
  6. 小数局部还须要通过 decimal-leading-zero 补全位数
  7. CSS 繁多变量一方面能够带来更好的可维护性,另一方面还能够更轻易地实现过渡动画
  8. 借助 @property能够很不便的管制 CSS 变量的过渡和动画

数字变动动画在一些数据大屏展现的场景下还是挺实用的,有了 CSS 变量,再也不须要通过 JS 去实时计算了。不过目前兼容性还不是太好,适宜外部我的项目小范畴应用(当然间接用了不要紧,不反对的只是没有动画而已)。最初,如果感觉还不错,对你有帮忙的话,欢送点赞、珍藏、转发❤❤❤

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

退出移动版