This article will use pure CSS, take you step by step to achieve such a sci-fi character jump background animation. Something like this character rain animation:

Or something like this:

Used in some similar technology theme background, very colorful.

The vertical arrangement of text

The first step is to achieve vertical text arrangement:

This step is very simple, and there may be many ways, here I will briefly list:

  1. Use properties that control text alignmentwriting-modeTo control, you can go throughwriting-mode: vertical-lrThe text will be arranged vertically, but for numbers and English, it will be rotated 90° to display:
<p>1234567890ABC</p>
<p>Chinese or other characters zhu ヶ</p>
Copy the code
p {
    writing-mode: vertical-lr; 
}
Copy the code

Of course, in this case, the display of English characters does not quite meet our needs.

  1. Control the width of the container, control each line can display only 1 Chinese character.

This method is the simplest and most convenient method, but due to the particularity of English, to make the continuous long string natural newline, we also need to cooperate with word-break: break-all:

p {
    width: 12px;
    font-size: 10px;
    word-break: break-all;
}
Copy the code

The effect is as follows:

Use CSS to select random strings

To make it a little bit more natural. The selection of characters on each line should be random.

But it’s hard to get CSS to randomly generate characters on every line. So here we use CSS preprocessor SASS/LESS.

And since it’s not possible to use CSS to insert characters into a single tag, such as a

tag, we put the tag’s characters inside the pseudo-element ::before content of each

element.

We can set up a set of strings in advance and use the SASS function to randomly generate each element’s content, with pseudo-code like this:

<div>
    <p></p>
    <p></p>
    <p></p>
</div>
Copy the code
$str: 'ぁ ぃ ぅ ぇ ぉ か き く け こ ん さ し す せ そ た ◁ ▣ ▤ ▥ ▦ ▧ came ♀ has ☻ for ◄ ▧ ▨ ♦ ち つ っ て と ゐ な に ぬ ね の は ひ ふ へ ほ ゑ ま み む め も ゃ ゅ ょ ゎ を ァ ィ ゥ ヴ ェ ォ カ ヵ キ ク ケ ヶ コ サ シ ス セ ソ タ チ ツ ッ テ ト ヰ ン ナ ニ ヌ ネ ノ ハ ヒ フ ヘ ホ ヱ マ ミ ム メ モ ャ ュ ョ ヮ ヲ ㄅ themselves are really vivi ㄞ ㄢ rich holiday ㄆ ㄊ ㄍ ㄐ ㄔ ㄗ Having one boat well be picture ㄎ ㄑ ㄕ ㄘ ㄨ ㄜ ㄠ ㄤ from ㄏ ㄒ ㄖ ㄙ ㄩ all ㄡ ㄥ abcdefghigklmnopqrstuvwxyz123456789% @ # $< > ^ & * _ + '; $length: str-length($str); @function randomChar() { $r: random($length); @return str-slice($str, $r, $r); } @function randomChars($number) { $value: ''; @if $number > 0 { @for $i from 1 through $number { $value: $value + randomChar(); } } @return $value; } p:nth-child(1)::before { content: randomChars(25); } p:nth-child(2)::before { content: randomChars(25); } p:nth-child(3)::before { content: randomChars(25); }Copy the code

A quick explanation of the above code:

  1. $strDefines a random string,$lengthRepresents the length of a string
  2. RandomChar () uses SASS’srandom()Method, pick a random 0 – at a time$lengthThe number of plastic operations is denoted as$rAnd use SASS’sstr-sliceMethod every time from$strSelect a subscript of$rRandom character of
  3. RandomChars () is a loop calling randomChar() from$strTo generate a random string of the length of the parameter passed in$number

In this way, the characters in each column are different each time:

Of course, I don’t think the above method is the best. CSS pseudo-element content supports character encoding, such as content: ‘\3066’; In this way, random characters can be generated better by setting the character range in combination with SASS Function, but I have been trying for a long time and the final product generated by SASS Function will add Spaces between numbers like \ and 3066. Unable to eventually convert to characters through character encoders, eventually abandoned…

Use CSS for typing effect

OK, moving on, next we’ll use CSS for typing, where the characters appear one by one, like this:

Here, we use the features of animation steps to achieve, that is, frame by frame animation.

From left to right is the same as from top to bottom. Taking from left to right as an example, suppose we have 26 English characters and we know the length of the string composed of 26 English characters, then we just need to set an animation and let its width change from 0 to 100% through 26 frames. Hidden, Steps displays one character per frame.

Of course, there’s a little trick here, how do we know the length of a string from the number of characters?

Highlight: Use the same width font feature to match the CH unit in CSS.

If you don’t know what iso-width font families are, check out my article “Font family you Should Know”.

In the CSS, the CH unit indicates the width of the number 0. If the font happens to be a monospaced font, that is, each character is the same width, then ch becomes the width of each English character, and 26ch is the length of the entire string.

Using this feature, together with the steps of animation, we can easily use CSS to achieve typing animations:

<h1>Pure CSS Typing animation.</h1>
Copy the code
h1 {
    font-family: monospace;
    width: 26ch;
    white-space: nowrap;
    overflow: hidden;
    animation: typing 3s steps(26, end);
}

@keyframes typing {
    0{
        width: 0;
    }
    100% {
        width: 26ch; }}Copy the code

You can get the following result:

The full code you can poke here:

CodePen Demo – Pure CSS text input effect

Change to vertical type effect

Now, let’s use the above techniques to transform it. Change a landscape type effect to a vertical type effect.

The core pseudocode is as follows:

<div>
    <p></p>
    <p></p>
    <p></p>
</div>
Copy the code
$str: 'ぁ ぃ ぅ ぇ ぉ か き く け こ ん さ し す せ そ た ◁ ▣ ▤ ▥ ▦ ▧ came ♀ has ☻ for ◄ ▧ ▨ ♦ ち つ っ て と ゐ な に ぬ ね の は ひ ふ へ ほ ゑ ま み む め も ゃ ゅ ょ ゎ を ァ ィ ゥ ヴ ェ ォ カ ヵ キ ク ケ ヶ コ サ シ ス セ ソ タ チ ツ ッ テ ト ヰ ン ナ ニ ヌ ネ ノ ハ ヒ フ ヘ ホ ヱ マ ミ ム メ モ ャ ュ ョ ヮ ヲ ㄅ themselves are really vivi ㄞ ㄢ rich holiday ㄆ ㄊ ㄍ ㄐ ㄔ ㄗ Having one boat well be picture ㄎ ㄑ ㄕ ㄘ ㄨ ㄜ ㄠ ㄤ from ㄏ ㄒ ㄖ ㄙ ㄩ all ㄡ ㄥ abcdefghigklmnopqrstuvwxyz123456789% @ # $< > ^ & * _ + ';
$length: str-length($str);

@function randomChar() {
    $r: random($length);
    @return str-slice($str, $r, $r);
}

@function randomChars($number) {
    $value: ' ';

    @if $number > 0 {
        @for $i from 1 through $number {
            $value: $value + randomChar();
        }
    }
    @return $value;
}

p {
    width: 12px;
    font-size: 10px;
    word-break: break-all;
}

p::before {
    content: randomChars(20);
    color: #fff;
    animation: typing 4s steps(20, end) infinite;
}

@keyframes typing {
    0% {
        height: 0;
    }
    25% {
        height: 100%;
    }
    100% {
        height: 100%; }}Copy the code

In this way, we have achieved vertical typing:

Of course, this looks more neat and uniform, the lack of a certain random, but also the lack of a certain sense of beauty.

Based on this, we made two changes:

  1. Based on the duration of the animationanimation-time, and animation delaysanimation-delayAnd increase the randomness within a certain range
  2. At the end of each animation, or during each animation, replace the false elementcontentThat is, to regenerate itcontent

This can be done very easily with SASS. The core SASS code is as follows:

$n: 3;
$animationTime: 3;
$perColumnNums: 20;

@for $i from 0 through $n {
    $content: randomChars($perColumnNums);
    $contentNext: randomChars($perColumnNums);
    $delay: random($n);
    $randomAnimationTine: #{$animationTime + random(20) / 10 - 1}s;

    p:nth-child(# {$i})::before {
        content: $content;
        color: #fff;
        animation: typing-#{$i} $randomAnimationTine steps(20, end) #{$delay * 0.1 s * -1} infinite;
    }

    @keyframes typing-#{$i} {
        0% {
            height: 0;
        }
        25% {
            height: 100%;
        }
        100% {
            height: 100%;
            content: $contentNext; }}}Copy the code

Look at the effect, there has been a good improvement:

Of course, the above conversion from horizontal typing to vertical typing effect is actually a little different. Under the existing vertical arrangement rules, the actual vertical height cannot be obtained through ch and the number of characters. So there’s a trade-off here, and actually slowing down the animation, no word is necessarily complete.

Of course, it’s almost imperceptible with fast animation.

Add shadow and transparency changes

The last step is to add changes in light and transparency.

The best effect is to keep the brightness of each new character at maximum while the brightness of existing characters gradually decreases.

But because we can’t manipulate every character, only every line of characters, we have to take a different approach.

The final approach is to borrow another pseudo-element mask for synchronization to achieve the final effect. Let’s take a step by step look at the process.

Add bright colors and highlights to the text

The first step is to add light and highlights to the text. This is very easy, just pick a light color on a black background and use text-shadow to make the text glow.

p::before {
    color: rgb(179.255.199);
    text-shadow: 0 0 1px #fff.0 0 2px #fff.0 0 5px currentColor, 0 0 10px currentColor;
}
Copy the code

To see the effect, the left side is white character, the middle is changed character color, the right side is changed font color and added font shadow effect:

Add a synchronization mask to the text

Next, in the process of the text animation, add a black to transparent mask synchronously, restore as much as possible to keep the brightness of each new character, while the brightness of the existing character gradually decreased.

Here I have separated the text layer from the mask layer and changed the background color from black to white to make it easier to understand:

The approximate pseudo-code for the mask layer is as follows, using another pseudo-element of the element:

p::after {
    content: ' ';
    background: linear-gradient(rgba(0.0.0.9), transparent 75%, transparent);
    background-size: 100% 220%;
    background-repeat: no-repeat;
    animation: mask 4s infinite linear;
}

@keyframes mask {
    0% {
        background-position: 0 220%;
    } 
    30% {
        background-position: 0 0%;
    }
    100% {
        background-position: 0 0%; }}Copy the code

Ok, so together the net result is something like this:

By adjusting some parameters of @Keyframes mask, different character fading effects can be obtained, which requires some debugging.

Complete code and effect

OK, breaking down the main steps, and finally writing the complete code, applying the Pug template engine and SASS syntax.

The complete code adds up to less than 100 lines.

.g-container
    -for(var i=0; i<50; i++)
        p
Copy the code
@import url('https://fonts.googleapis.com/css2?family=Inconsolata:wght@200&display=swap');

$str: 'ぁ ぃ ぅ ぇ ぉ か き く け こ ん さ し す せ そ た ◁ ▣ ▤ ▥ ▦ ▧ came ♀ has ☻ for ◄ ▧ ▨ ♦ ち つ っ て と ゐ な に ぬ ね の は ひ ふ へ ほ ゑ ま み む め も ゃ ゅ ょ ゎ を ァ ィ ゥ ヴ ェ ォ カ ヵ キ ク ケ ヶ コ サ シ ス セ ソ タ チ ツ ッ テ ト ヰ ン ナ ニ ヌ ネ ノ ハ ヒ フ ヘ ホ ヱ マ ミ ム メ モ ャ ュ ョ ヮ ヲ ㄅ themselves are really vivi ㄞ ㄢ rich holiday ㄆ ㄊ ㄍ ㄐ ㄔ ㄗ Having one boat well be picture ㄎ ㄑ ㄕ ㄘ ㄨ ㄜ ㄠ ㄤ from ㄏ ㄒ ㄖ ㄙ ㄩ all ㄡ ㄥ abcdefghigklmnopqrstuvwxyz123456789% @ # $< > ^ & * _ + ';
$length: str-length($str);
$n: 50;
$animationTime: 4;
$perColumnNums: 25;

@function randomChar() {
    $r: random($length);
    @return str-slice($str.$r.$r);
}

@function randomChars($number) {
    $value: ' ';

    @if $number > 0 {
        @for $i from 1 through $number {
            $value: $value+ randomChar(); }}@return $value;
}

body.html {
    width: 100%;
    height: 100%;
    background: # 000;
    display: flex;
    overflow: hidden;
}

.g-container {
    width: 100vw;
    display: flex;
    justify-content: space-between;
    flex-wrap: nowrap;
    flex-direction: row;
    font-family: 'Inconsolata', monospace, sans-serif;
}

p {
    position: relative;
    width: 5vh;
    height: 100vh;
    text-align: center;
    font-size: 5vh;
    word-break: break-all;
    white-space: pre-wrap;
    
    &::before,
    &::after {
        position: absolute;
        top: 0;
        left: 0;
        right: 0;
        height: 100%;
        overflow: hidden; }}@for $i from 0 through $n {
    $content: randomChars($perColumnNums);
    $contentNext: randomChars($perColumnNums);
    $delay: random($n);
    $randomAnimationTine: # {$animationTime + random(20) / 10 - 1}s;
  
    p:nth-child(#{$i})::before {
        content: $content;
        color: rgb(179.255.199);
        text-shadow: 0 0 1px #fff.0 0 2px #fff.0 0 5px currentColor, 0 0 10px currentColor;
        animation: typing-#{$i} $randomAnimationTine steps(20, end) #{$delay * 0.1 s * -1} infinite;
        z-index: 1;
    }

    p:nth-child(#{$i})::after {
        $alpha: random(40) / 100 + 0.6;
        content: ' ';
        background: linear-gradient(rgba(0.0.0.$alpha), rgba(0.0.0.$alpha), rgba(0.0.0.$alpha), transparent 75%, transparent);
        background-size: 100% 220%;
        background-repeat: no-repeat;
        animation: mask $randomAnimationTine infinite #{($delay - 2) * 0.1 s * -1} linear;
        z-index: 2;
    }

    @keyframes typing-#{$i0%} {{height: 0; 25%} {height: 100%; 100%} {height: 100%;
            content: $contentNext; }}}@keyframes mask{
    0% {
        background-position: 0 220%; 30%} {background-position: 0 0%; 100%} {background-position: 0 0%; }}Copy the code

The final result is shown in the picture:

For the full code and demo you can click here:

CodePen Demo — Digital Char Rain Animation

The last

Inspiration comes from teacher Yuan Chuan’s CodePen Demo — Matrix Digital Rain. The original effect is realized by JavaScript. This paper deduces it by pure CSS.

More wonderful CSS effects can follow my CSS inspiration

Well, the end of this article, I hope to help you 🙂

Want to Get the most interesting CSS information, do not miss my public account – iCSS front-end interesting news 😄

More interesting CSS technology articles are summarized in my Github — iCSS, constantly updated, welcome to click on the star subscription favorites.

If there are any questions or suggestions, you can exchange more original articles, writing is limited, talent and learning is shallow, if there is something wrong in the article, hope to inform.