乐趣区

关于前端:Web动画科技感十足的暗黑字符雨动画

本文将应用纯 CSS,带大家一步一步实现一个这样的科幻字符跳动背景动画。相似于这样的字符雨动画:

或者是相似于这样的:

![CodePen Home
Matrix digital rain (animated version) By yuanchuan](https://user-images.githubuse…

使用在一些相似科技主题的背景之上,十分的添彩。

文字的竖排

首先第一步,就是须要实现文字的竖向排列:

这一步十分的简略,可能办法也很多,这里我简略列举一下:

  1. 应用管制文本排列的属性 writing-mode 进行管制,能够通过 writing-mode: vertical-lr 等将文字进行竖向排列,然而对于数字和英文,将会旋转 90° 展现:
<p>1234567890ABC</p>
<p> 中文或其余字符ォヶ </p>
p {writing-mode: vertical-lr;}

当然这种状况下,英文字符的展现不太满足咱们的需要。

  1. 管制容器的宽度,管制每行只能展现 1 个中文字符。

这个办法算是最简略便捷的办法了,然而因为英文的特殊性,要让间断的长字符串天然的换行,咱们还须要配合 word-break: break-all

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

成果如下,满足需要:

应用 CSS 实现随机字符串的选取

为了让咱们的成果更加天然。每一行的字符的选取最好是随机的。

然而要让 CSS 实现随机生成每一行的字符可太难了。所以这里咱们请出 CSS 预处理器 SASS/LESS。

而且因为不太可能利用 CSS 给单个标签内,譬如 <p> 标签插入字符,所以咱们把标签内的字符展现,放在每个 <p> 元素的伪元素 ::beforecontent 当中。

咱们能够提前设置好一组字符串,而后利用 SASS function 随机生成每一次元素内的 content,伪代码如下:

<div>
    <p></p>
    <p></p>
    <p></p>
</div>
$str: 'ぁぃぅぇぉかきくけこんさしすせそた◁▣▤▥▦▧♂♀♥☻►◄▧▨♦ちつってとゐなにぬねのはひふへほゑまみむめもゃゅょゎをァィゥヴェォカヵキクケヶコサシスセソタチツッテトヰンナニヌネノハヒフヘホヱマミムメモャュョヮヲㄅㄉㄓㄚㄞㄢㄦㄆㄊㄍㄐㄔㄗㄧㄛㄟㄣㄇㄋㄎㄑㄕㄘㄨㄜㄠㄤㄈㄏㄒㄖㄙㄩㄝㄡㄥ 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);
}

简略解释下下面的代码:

  1. $str 定义了一串随机字符串,$length 示意字符串的长度
  2. randomChar() 中利用了 SASS 的 random() 办法,每次随机选取一个 0 – $length 的整形数,记为 $r,再利用 SASS 的 str-slice 办法,每次从 $str 中选取一个下标为 $r 的随机字符
  3. randomChars() 就是循环调用 randomChar() 办法,从 $str 中随机生成一串字符串,长度为传进去的参数 $number

这样,每一列的字符,每次都是不一样的:

当然,上述的办法我认为不是最好的,CSS 的伪元素的 content 是反对字符编码的,譬如 content: '\3066'; 会被渲染成字符 ,这样,通过设定字符区间,配合 SASS function 能够更好的生成随机字符,然而我尝试了十分久,SASS function 生成的最终产物会在 \3066 这样的数字间增加上空格,无奈最终通过字符编码转换成字符,最终放弃 …

应用 CSS 实现打字成果

OK,持续,接下来咱们要应用 CSS 实现打字成果,就是让字符一个一个的呈现,像是这样:

这里借助了 animation 的 steps 的个性实现,也就是逐帧动画。

从左向右和从上向下原理是一样的,以从左向右为例,假如咱们有 26 个英文字符,咱们已知 26 个英文字符组成的字符串的长度,那么咱们只须要设定一个动画,让它的宽度变动从 0 - 100% 经验 26 帧即可,配合 overflow: hidden,steps 的每一帧即可展出一个字符。

当然,这里须要利用一些小技巧,咱们如何通过字符的数量晓得字符串的长度呢?

划重点: 通过等宽字体的个性,配合 CSS 中的 ch 单位

如果不理解什么是等宽字体族,能够看看我的这篇文章 —《你该晓得的字体 font-family》。

CSS 中,ch 单位示意数字“0”的宽度。如果字体凑巧又是等宽字体,即每个字符的宽度是一样的,此时 ch 就能变成每个英文字符的宽度,那么 26ch 其实也就是整个字符串的长度。

利用这个个性,配合 animation 的 steps,咱们能够轻松的利用 CSS 实现打字动画成果:

<h1>Pure CSS Typing animation.</h1>
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;}
}

就能够失去如下后果啦:

残缺的代码你能够戳这里:

CodePen Demo — 纯 CSS 实现文字输出成果

革新成竖向打字成果

接下来,咱们就使用上述技巧,革新一下。将一个横向的打字成果革新成竖向的打字成果。

外围的伪代码如下:

<div>
    <p></p>
    <p></p>
    <p></p>
</div>
$str: 'ぁぃぅぇぉかきくけこんさしすせそた◁▣▤▥▦▧♂♀♥☻►◄▧▨♦ちつってとゐなにぬねのはひふへほゑまみむめもゃゅょゎをァィゥヴェォカヵキクケヶコサシスセソタチツッテトヰンナニヌネノハヒフヘホヱマミムメモャュョヮヲㄅㄉㄓㄚㄞㄢㄦㄆㄊㄍㄐㄔㄗㄧㄛㄟㄣㄇㄋㄎㄑㄕㄘㄨㄜㄠㄤㄈㄏㄒㄖㄙㄩㄝㄡㄥ 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%;}
}

这样,咱们就实现了竖向的打字成果:

当然,这样看上去比拟整齐划一,短少了肯定的随机,也就短少了肯定的美感。

基于此,咱们进行 2 点革新:

  1. 基于动画的时长 animation-time、和动画的提早 animation-delay,减少肯定幅度内的随机
  2. 在每次动画的开端或者过程中,从新替换伪元素的 content,也就是从新生成一份 content

能够借助 SASS 十分轻松的实现这一点,外围的 SASS 代码如下:

$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.1s * -1} infinite;
    }

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

看看成果,曾经有不错的改观:

当然,上述由横向打字转变为竖向打字成果其实是有一些不一样的。在现有的竖向排列规定下,无奈通过 ch 配合字符数拿到理论的竖向高度。所以这里有肯定的取舍,理论加快动画来看,没个字的现出不肯定是残缺的。

当然,在疾速的动画成果下简直是觉察不到的。

减少光影与透明度变动

最初一步,就是减少光影及透明度的变动。

最佳的成果是要让每个新呈现的字符放弃亮度最大,同时曾经呈现过的字符亮度缓缓削弱。

然而因为这里咱们无奈精密操控每一个字符,只能操控每一行字符,所以在实现形式上必须另辟蹊径。

最终的形式是借用了另外一个伪元素进行同步的遮罩以实现最终的成果。上面咱们就来一步一步看看过程。

给文字削减亮色及高光

第一步就是给文字削减亮色及高光,这点非常容易,就是选取一个彩色底色下的亮色,并且借助 text-shadow 让文字发光。

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

看看成果,右边是红色字符,两头是扭转字符色彩,左边是扭转了字体色彩并且增加了字体暗影的成果:

给文字增加同步遮罩

接下来,就是在文字动画的前进过程中,同步增加一个彩色到通明的遮罩,尽量还原让每个新呈现的字符放弃亮度最大,同时曾经呈现过的字符亮度缓缓削弱。

这个成果的示意图大略是这样的,这里我将文字层和遮罩层离开,并且底色从彩色改为红色,不便了解:

大略的遮罩的层的伪代码如下,用到了元素的另外一个伪元素:

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

好,合在一起的最终成果大略就是这样:

通过调整 @keyframes mask 的一些参数,能够失去不一样的字符渐隐成果,须要肯定的调试。

残缺代码及成果

OK,拆解了一下次要的步骤,最初上一下残缺代码,利用了 Pug 模板引擎和 SASS 语法。

残缺代码加起来不过 100 行。

.g-container
    -for(var i=0; i<50; i++)
        p


$str: 'ぁぃぅぇぉかきくけこんさしすせそた◁▣▤▥▦▧♂♀♥☻►◄▧▨♦ちつってとゐなにぬねのはひふへほゑまみむめもゃゅょゎをァィゥヴェォカヵキクケヶコサシスセソタチツッテトヰンナニヌネノハヒフヘホヱマミムメモャュョヮヲㄅㄉㄓㄚㄞㄢㄦㄆㄊㄍㄐㄔㄗㄧㄛㄟㄣㄇㄋㄎㄑㄕㄘㄨㄜㄠㄤㄈㄏㄒㄖㄙㄩㄝㄡㄥ 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.1s * -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.1s * -1} linear;
        z-index: 2;
    }

    @keyframes typing-#{$i} {
        0% {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%;}
}

最终成果也就是题图所示:

残缺的代码及演示成果你能够戳这里:

CodePen Demo — Digital Char Rain Animation

最初

灵感源自 袁川 老师的这个成果 CodePen Demo — Matrix digital rain,原成果应用了 JavaScript· 实现,本文利用纯 CSS 进行了演绎。

更多精彩 CSS 成果能够关注我的 CSS 灵感

好了,本文到此结束,心愿对你有帮忙 :)

想 Get 到最有意思的 CSS 资讯,千万不要错过我的公众号 — iCSS 前端趣闻 😄

更多精彩 CSS 技术文章汇总在我的 Github — iCSS,继续更新,欢送点个 star 订阅珍藏。

如果还有什么疑难或者倡议,能够多多交换,原创文章,文笔无限,满腹经纶,文中若有不正之处,万望告知。

退出移动版