⚠️ this article for nuggets community first contract article, not authorized to forbid reprint

preface

In many cases we need to show a string of numbers, which can be written to a fixed page, dynamically refreshed and requested in real time, or changed based on user interactions. Before, we used anime.js to create an animation like this when the numbers were changing on our site:

One of the drawbacks of animating with anime. Js is that the numbers do not have commas in the middle, as in the figure above. It must be a value of type number, not a string like ‘6,000.00’. Of course, we can apply the effect individually for each string or number, but the effect is not as good as the one above. It will look something like this:

Estimate pay treasure also is met the same problem with us, the content of the display on the page looks like Numbers, but in fact it is a string, can only use similar to split the same way find a comma and the position of the decimal point to break up, then get the numeric characters with parseInt parsed into numeric types and then apply this effect.

The product side was not very satisfied with this effect, finally in a meeting:

Product Group: Our main task in this issue is to optimize the interactive experience. Have you starved to death? Their software had this effect on the digital side:

Development group: you this what appreciation level? Want to change it to this?

Product group: Of course not, just to show you this effect, just that effect may not be very good, let’s change a page for you to see:

Development Team:…

If you really have nothing to do, go clean up! Don’t think of a is a, site users sooner or later let you get lost…

Product: Oops no! I didn’t want it to look this bad! Just this effect reminds me of the slot machine 🎰 two days ago still in starved yao to see it! Let me see… Voila! Here it is:

R & D: ok know to do according to this effect, right?

Product: Hey ~ don’t go yet! I’m not finished yet! First of all, I think we’re going in the wrong direction. We’re going to make it roll down like a slot machine:

Then you can’t just slow down like you’re starving to death.

R&d: How do you stop if you don’t stop slowly? Quick stop?

Product: Not this meaning, because slowly stop that kind of effect is too common, can we make a more dynamic effect?

R & D: How do you want to move?

Product: It is similar to stopping but the speed is too fast to stop, pass a bit and then bounce back, do you have a professional term to describe this effect?

R & D: is it rebound effect?

Product: Right! That’s it!

R & D: Yes! No problem! It’s better than that animation you just showed us.

Animation display

Just in case you can’t imagine what that might look like, let’s show you some of the components that we’ve already written and actually used on our pages:

The number above seems a little unlucky… Hurry to change a 缟

Does that make a lot of sense?

It reminds me of before in high school have seen the movie: “killing phone” actor by a opened hanging mobile phone into the big casino in Las Vegas, just crazy earning hundreds of euros 💶 within minutes before he came to a slot machine coin after press the button, the slot machines will bring the rebound effect:

I think the rebound effect is not dynamic enough, so I used CSS to enhance the rebound effect. Do you like the no-rebound effect of starving, the slight rebound effect of the Las Vegas slot machine, or the dynamic rebound effect that will be developed in this article?

Good luck to those who like and follow you when you go to the casino and play the slot machines like the picture above 💰💰💰

However small wager happy feelings big wager injury body ☠️ cherish life away from gambling 🎲

The principle of analysis

In fact, the principle of this is very similar to the rotation diagram:

A good front end should at least be able to write a rotation diagram. Click the arrow to change the value of the variable. Then map the variable to the DOM style property. Finally, use overflow: hidden; Hide the ones that are out there:

Of course, this is just a simple rotograph, a complete rotograph should have a bunch of dots at the bottom, to show on one hand how many graphs, and on the other hand to show the current number of graphs. But that’s not going to be useful for the slot-machine scrolling numbers we’re developing, so I’ll leave it at that for now. Isn’t the rotation diagram horizontal? Let’s try it upright:

Next, replace the hounai Hashimoto GIF with a number:

Done by infinite friends should know, from last to first or from first to last a favorite to look like a direct rolling in the past, usually in the head and finally a copy version, in the tail to join a copy of the first edition, this is no exception, but because we don’t like them by figure can roll around, so we just from top to bottom roll, So let’s put the first number down here, which is 0. Then remove the arrow and let it roll by itself:

Then use overflow: hidden; To hide the numbers that are exposed:

Doesn’t it look a bit like this:

But there’s another difference: the slot machine, pictured above, has a blurring effect, giving the illusion that it’s rolling fast enough to double. This reminds me of what the product manager asked me to do: “Hongmeng’s opening animation is so handsome, please give us a whole page.”

I know the first thing that comes to mind is: filter: blur(px); , this CSS property has the advantage of blurring the element in all directions. In some cases, you don’t need to blur in all directions. You need to blur along the x axis or along the y axis. The result achieved:

The blur along the Y-axis looks like this:

As you can see, the effect is quite different, and we need to blur along the Y-axis to get the slot machine effect, so let’s copy the filter code from the article and apply it to our page:

The effect seems to be good! If you use filter: blur(); What is the experience of adding a blur to a number? Let’s try it:

Emmmmmmm… Looks like presbyopia…

On a whim, since we have this SVG filter that controls whether we blur along the X axis or the y axis, we can also blur along the x axis and apply the same effect to the rotation, right? Try it out:

Here’s a comparison with the previous rotation diagram:

How’s that? Does the rotation look more dynamic after adding this filter?

Animation positioning

Let’s say we want to get the number to the number 6:

However, this 6 also has the top and bottom blur effect we added. Let’s remove the blur filter when we stop and look at it again:

Does it look like it just happens to roll to 6 and stop? But it’s not like that. It’s like this:

If you look closely, you can see that it’s not that the string of numbers just happens to scroll to 6 and then stop, it’s that wherever you scroll, as long as it’s time, it’s directly positioned to 6. If it’s hard to see, let’s slow down and add a red background to 6 and take a look:

Because of the fast rolling speed, so even if it does not roll to the sixth digit and suddenly stopped at the sixth digit, the human eye can not see, but will feel that it is rolling to the front of the number 6, in fact, it is a smoke screen. Many of the CSS effects are implemented in a sort of smoke-screen way, such as an infinite scrolling rosette that looks like there are literally countless images connected together. It’s a bit like magic, and it’s all about using tricks to trick the user’s eyes to achieve impressive results. This is why I like cool CSS effects. I feel like a magician in the web, performing a magic trick for everyone.

But what good will it do, one must ask? Why not just scroll to the corresponding number and stop:

First of all, whether it’s done this way or that way, they both end up looking like this:

Unless your product manager requires a snail’s pace, you won’t be able to tell the difference. Another reason is that we can control exactly when to stop rolling. Let’s say we set it to stop after a few seconds, so it’s not certain what position it’s in at the time it stops rolling, right? If we want to stop at position 9, but the animation happens to be at position 1 at the time, the animation will continue scrolling until position 9. Assuming we set the scroll to two seconds and it takes 0.8 seconds to scroll from bit 1 to bit 9, the entire animation actually stops after 2.8 seconds, which is obviously inconsistent with the two seconds we set.

However, smart students will certainly think of: you do not default from the 0 start roll not to get it! It’s a dynamic calculation of where to start rolling based on the number you pass in. For example, if you plan to scroll for 2 seconds and then stop at the 6th position, you just need to calculate which position to roll from, and it happens to roll to the 6th position after 2 seconds.

It’s possible to do this, but it adds to the complexity of our code with the same effect, and wastes several hairs of our hair doing the calculation, when there’s a much simpler way to do it: run the animation in two segments!

Segmented animation

First animation

Infinite scroll animation, we’ll wrap it as a component, and how long it will scroll depends on the parameters passed in.

The second animation

As you can see, we will eventually select a number to do this dynamic rebound effect, and then switch to the animation immediately after the infinite scrolling, which number is also determined by the parameters passed in.

together

Component code

Since this project is made in vue2.x, the code posted is also Vue2 style, but it doesn’t matter, the JS part is very simple, the main code is concentrated in the CSS part. So you can easily change this component to fit your project’s Vue3. X component or React component:

<template>
  <component
    :is="as"
    class="scroll-num"
    :style="{ '--i': i, '--delay': delay }"
  >
    <ul ref="ul">
      <li>0</li>
      <li>1</li>
      <li>2</li>
      <li>3</li>
      <li>4</li>
      <li>5</li>
      <li>6</li>
      <li>7</li>
      <li>8</li>
      <li>9</li>
      <li>0</li>
    </ul>

    <svg width="0" height="0">
      <filter id="blur">
        <feGaussianBlur
          in="SourceGraphic"
          :stdDeviation="`0 ${blur}`"
        />
      </filter>
    </svg>
  </component>
</template>

<script>
export default {
  name: 'ScrollNum'.props: {
    as: {
      type: String.default: 'div'
    },
    i: {
      type: Number.default: 0.validator: v= > v < 10 && v >= 0 && Number.isInteger(v)
    },
    delay: {
      type: Number.default: 1
    },
    blur: {
      type: Number.default: 2}},data: () = > ({ timer: null }),
  mounted () {
    const ua = navigator.userAgent.toLowerCase()
    const testUA = regexp= > regexp.test(ua)
    const isSafari = testUA(/safari/g) && !testUA(/chrome/g)

    // Compatibility code for Safari
    isSafari && (this.timer = setTimeout(() = > {
      this.$refs.ul.setAttribute('style'.` animation: none; Transform: translateY(calc(var(-- I) * -9.09%)) ')},this.delay * 1000))
  },
  beforeDestroy () { clearTimeout(this.timer) }
}
</script>

<style scoped>
.scroll-num {
  width: var(--width, 20px);
  height: var(--height, calc(var(--width, 20px) * 1.8));
  color: var(--color, # 333);
  font-size: var(--height, calc(var(--width, 20px) * 1.1));
  line-height: var(--height, calc(var(--width, 20px) * 1.8));
  text-align: center;
  overflow: hidden;
  animation: enhance-bounce-in-down 1s calc(var(--delay) * 1s) forwards;
}

ul {
  /* padding: 0; margin: 0; list-style: none; * /
  animation: move .3s linear infinite,
  bounce-in-down 1s calc(var(--delay) * 1s) forwards
}

@keyframes move {
  from {
    transform: translateY(-90%);
    filter: url(#blur)}to {
    transform: translateY(1%);
    filter: url(#blur)}}@keyframes bounce-in-down {
  from {
    transform: translateY(calc(var(--i) * -9.09% - 7%));
    filter: none
  }
  25% { transform: translateY(calc(var(--i) * -9.09% + 3%))}50% { transform: translateY(calc(var(--i) * -9.09% - 1%))}70% { transform: translateY(calc(var(--i) * -9.09%+.6%))}85% { transform: translateY(calc(var(--i) * -9.09%-.3%))}to { transform: translateY(calc(var(--i) * -9.09%))}}@keyframes enhance-bounce-in-down {
  25% { transform: translateY(8%)}50% { transform: translateY(-4%)}70% { transform: translateY(2%)}85% { transform: translateY(-1%)}to { transform: translateY(0)}}</style>

Copy the code

⚠️ If you copy this component into a project and find that the style is not displayed correctly, just unannotate the style in the UL section of the CSS. The reason for this is that you did not introduce reset. CSS, which causes ul labels to have default margins and LI labels to have default dots. If there is reset.css, delete this useless comment.

< span style = “box-sizing: border-box; color: RGB (74, 74, 74); font-size: 14px; word-break: inherit;” I know some people will say: If you want to keep ratios, just use aspect-ratio. Why bother?


The first is that this property is relatively new and not particularly compatible. While Edge, Firefox, and the latest versions of Google support this property, Safari only supports this property in the 15- Technical Preview, and IOS does not support it at all:

Keep in mind that most iPhone users choose Safari because they don’t know much about browsers, except that the compass 🧭 icon is used to surf the Web. The other thing is that we don’t really have to keep this ratio, it’s just a habit of encapsulating components. Sometimes lazy, want to use a component when only pass a width or height on it, not passed that parameter can be automatically calculated, so it will be encapsulated like this. You can change that code to look whatever you like.


If you’re not sure what a CSS variable is, check out this article to learn about it. It’s 2021, so it’s time to learn about this technology, but if you have to say it’s not supported in Internet Explorer:

Refuse to learn any new technology on the grounds that IE doesn’t support it, and soon, very soon, you’ll be obsolete faster than IE. Because even Microsoft and Vue3 have both decided to drop IE: Vue3 will not support IE11 and will focus on Vue2.7.

usage

This is just a component, normally we don’t just have one number scroll, we have a string of numbers scroll, we define a number 886, then use computed to change 886 to [8, 8, 6], and then v-for one:

<template>
  <ul class="flex">
    <ScrollNum
      v-for="(num, idx) of numArr"
      :key="idx"
      as="li"
      :i="num"
      :delay="idx + 1"
    />
  </ul>
</template>

<script>
import ScrollNum from './components/ScrollNum.vue'

export default {
  name: 'App'.components: { ScrollNum },
  data: () = > ({ num: 886 }),
  computed: {
    numArr () {
      const str = String(this.num)
      let arr = []

      for (let i = 0; i < str.length; i++) {
        arr.push(parseInt(str[i]))
      }
      
      return arr
    }
  }
}
</script>

<style scoped>
.flex {
  display: flex;
}
ul {
  padding: 0;
  margin: 0;
  list-style: none;
}
</style>

Copy the code

Here’s a perfect slot machine effect:

If you want to resize it, just give it a –width, and the height and font size will adjust automatically. We can also add a border:

<template>
  <ul class="flex">
    <ScrollNum
      v-for="(number, idx) of numArr"
      :key="idx"
      :i="number"
      :delay="Independence idx + 2.5"
      as="li"
      class="num"
    />
  </ul>
</template>

<script>
import ScrollNum from './components/ScrollNum.vue'

export default {
  name: 'App'.components: { ScrollNum },
  data: () = > ({ num: 886 }),
  computed: {
    numArr () {
      const str = String(this.num)

      return [parseInt(str[0]), parseInt(str[1]), parseInt(str[2])]}}}</script>

<style>
.flex {
  display: flex;
}
ul {
  padding: 0;
  margin: 0;
  list-style: none;
}
.num{-width: 26px;
  margin-right: 6px;
  border: 1px solid black;
  border-radius: 8px
}
</style>

Copy the code

⚠️ If you copy my code to your own project and find that the scrolling cannot stop, it may be a bug caused by the vue-loader version is too low, so you can upgrade vue-cli or remove the scoped from

conclusion

So, did it work out well? Now all you have to do is copy my components and make them into a cool widget for your own project.

I open source, you happy, the boss drives ferrari!

Previous excellent article

  • “Product Manager: The opening animation of Hongmeng is very handsome. Please give us a whole page.”
  • Create your own Visual Data Map without any libraries
  • Vue3 will not support IE11, but focus on Vue2.7
  • A fun new feature of Vue: Introducing JS variables into CSS
  • What? Can you pull and pull with just the H5 TAB?
  • Microsoft launches comments section on GitHub
  • Vue 3.0.3: New CSS variable passing and the latest Ref Proposal
  • “Double 11 small black box is cool? Let’s use CSS variables to improve!”
  • “Don’t underestimate the nine grid, one question can let a candidate reveal his true colors!”
  • “Mobile Layout Interview Questions to test your CSS Skills (Center)”
  • A series of confusing behaviors after setting prototype Objects as Proxies
  • Vue’s Super Fun New Feature: DOM Portal
  • “Use of React’s Super-popular CSS-in-JS Library in the Vue Project: Styled – Components”
  • Is It Finally Vue’s Turn to Inspire React?
  • A Small Pit in Vue3 on IOS
  • Hooks use of the New VUe-Router
  • React 17 is officially a transition version!
  • Yu Yuxi: The Design Process of Vue3
  • The Father of Node’s refactoring Deno is finally released. Will it Replace Node after all?