12
头图
Welcome to my public account: front-end detective

I have to say, CSS counters are a good thing.

CSS counters have been used in several articles recently. CSS variables can be dynamically displayed through pseudo-elements content , and many interesting animations can be made. If you are interested, you can review these previous articles:

The principle is actually very simple, content Although it does not support direct rendering of CSS variables, it can support counter-reset

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

Through a transfer, you can make content also support CSS variables as characters to display

image-20220827130618618

This technique is learned from Zhang Xinxu's article, which is very practical: Tips: How to display CSS var variable values with the help of the content attribute

However, this method has a pity that the CSS counter does not support decimals in the true sense, that is, if the CSS variable is a decimal, it is directly displayed as 0

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

image-20220827130753591

So, how to make content also support the decimal display of CSS variables, after all, decimals are still required in many cases? For example, if you support decimals, you can easily realize the scrolling animation of numbers

Kapture 2022-08-27 at 14.25.44

Let's talk about it today

1. Dismantling the principles of CSS

Due to the particularity of CSS counters, only integers are currently supported. After all, natural numbers have no decimals (it does not rule out that custom counters can be implemented in the future). In this case, you can change the way of thinking and split it from the digital form. For example, a decimal, 48.69 can be decomposed into integer part 48 and fractional part 69 , and then linked by decimal point. In this way, they are all integers after splitting, and CSS counters are also supported.

image-20220827131936587

It is implemented by code (for easy understanding, some of the following variables are named in Chinese, which is not recommended for actual production)

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

image-20220827131530150

So the question becomes, how to split a decimal?

2. Split CSS variables into integers and decimals

Following the above question, suppose the variable is --percent , the problem is the following two variables --整数 and --小数 how to calculate from --percent ?

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

It seems easy, but it doesn't seem to be very easy to implement in CSS.

To solve this, you need to understand the types of CSS custom variables . There are many types, listed below

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

Most of the specific types can be seen. We need to use two types here, <number> and <integer> , both of which represent numbers, the specific difference is that

  • <number> represents any number, both integers and decimals
  • <integer> represents an integer number, which can only be an integer, and decimals will be considered illegal

Back here, by default, CSS variables can have any value, but with custom variables @property you can specify the type of the variable, which can convert invalid variables.

@property - CSS (Cascading Style Sheets) | MDN (mozilla.org)

For example, we need an integer, which can be defined like this, set the syntax attribute to <integer>

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

This way, the variable will be coerced to an integer. For example, the following --整数 is also set to a decimal

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

result...

image-20220827134012420

It turned into 0 ?

But it doesn't matter, you need to cooperate with some CSS calculation functions to achieve automatic conversion, such as calc

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

image-20220827134338534

However, here it becomes 49 , the reason is actually due to rounding, not rounding down . To eliminate this error, we can subtract 0.5 , so the final implementation of the integer part is

 @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(小数);
}
The future CSS math functions should also have floor, ceil, etc., you can look forward to it~

Then there is the fractional part. With the integer part, the fractional part is easy. You can subtract the integer part from the whole value and multiply by 100, as shown below

image-20220827144136572

Implementing it in code is

 @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(小数);
}

The effect is as follows

image-20220827135400459

The last decimal place is slightly off due to rounding. It doesn't matter, you can correct it and add 0.01 . Secondly, there is a problem, when the decimal place is less than 10, the calculated result may be like this

image-20220827140549249

Then, in this case, dynamic zero padding is required.

The technique of "zero-padding" has been described in detail in this article before: Can CSS also auto-complete strings?

Therefore, you only need to define the counter style after the counter decimal-leading-zero , which means the decimal leading zero, and the final implementation is as follows

 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);
}

In this way, both integers and decimals can be represented by the same variable --percent , perfect~

3. CSS variable animation

Some people may think, why waste so much effort to realize such a function? Can't you set it directly with js? If it's just a change in numbers, of course, but here, in addition to the better maintainability of CSS single variables , you can also do things that even JS can't do (or cost more), such as transition animation

First of all, to improve it, many decimals are in the form of percentages, that is, within the range of 0~1 , so the previous --percent may be such a value 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) "%";
}

The effect is as follows

image-20220827141156639

Then, we let this number change randomly through JS

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

The effect is as follows

Kapture 2022-08-27 at 14.14.47

However, this is too rigid. We need an animation when the number changes, which can be implemented directly through CSS custom variables

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

Now look at the effect, it is very easy to realize the scrolling animation of the number

640 (1).gif

Since the fractional part follows the integer part, for example, the integer changes from 1 to 3 , then the fractional part follows the change for two cycles. Originally this is also very common sense, just like the second of the clock will always be faster than the time, but some people may think that the change is too fast, is there a way to make the fractional part and the integer part independent? Of course, it is also possible, and it is very easy, just set transitions for the integer part and the decimal part respectively.

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

Now look at the effect and compare it with the above

Kapture 2022-08-27 at 14.25.44

These two effects can be selected by yourself, only the transition is different

Just imagine, if this effect is implemented with JS, is it still a little troublesome?

Below is the complete code (not much, just a few lines)

 @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) "%";
}

You can also visit the online demo: CSS double num(runjs.work)

RunJS , front-end code creation and sharing online.

4. Summary and Explanation

The above is the whole content, a good little skill, have you learned it?

  1. CSS variables do not support rendering directly in content , but can be achieved with the help of counter initialization
  2. CSS counters do not support decimal initialization
  3. The implementation principle of CSS counter supporting decimals is to split the decimal into three parts: integer, decimal point and decimal
  4. CSS custom variables can specify the type of variable, so that a decimal can be converted to an integer through CSS math functions
  5. The fractional part can be obtained by subtracting the integer part
  6. The decimal part also needs to pass decimal-leading-zero to complete the number of digits
  7. CSS single variables can bring better maintainability on the one hand, and transition animations can be implemented more easily on the other hand
  8. With the help of @property you can easily control the transition and animation of CSS variables

Digital change animation is quite practical in some large-screen data display scenarios. With CSS variables, there is no need to calculate in real time through JS. However, the compatibility is not very good at present, and it is suitable for small-scale use in internal projects (of course, it doesn't matter if you use it directly, it just doesn't support animation). Finally, if you think it's good and helpful to you, please like, bookmark, and forward ❤❤❤

Welcome to my public account: front-end detective

XboxYan
18.1k 声望14.1k 粉丝