本文将使用纯CSS带你一步步实现这样一个悬疑的人物跳跃背景动画。 与此类似的角色雨动漫:
数字字符雨动画
或者是这样的:
CodePenHome矩阵数字雨(动画版)作者:源川
用在一些类似科技主题的背景上尤其明亮。
文本水平排列
第一步,实现文本的垂直排列:
这一步非常简单,可行的方法有很多种。 这里我简单地列出它们:
使用属性writing-mode 来控制文本排列。 可以通过writing-mode:vertical-lr等方式垂直排列文字,对于数字和英文,会旋转90°显示:
<p>1234567890ABC</p>
<p>中文或其他字符ォヶ</p>
p {
writing-mode: vertical-lr;
}
事实上,在这些情况下,英文字符的显示并不能满足我们的需求。
控制容器的长度,控制每行只能显示1个英文字符。
这种方法是最简单、最方便的方法,而且由于英语的特殊性,我们需要配合word-break:break-all来让连续的长字符串自然换行:
p {
width: 12px;
font-size: 10px;
word-break: break-all;
}
疗效如下,满足需要:
使用CSS实现随机字符串的选择
为了让我们的疗效更加自然。 每列的字符选择优选地是随机的。
然而,CSS 很难在每一列中随机生成字符。 所以这里我们请来CSS预处理器SASS/LESS。
但由于不可能在 CSS 的帮助下给出单个标签,例如
标签插入字符,所以我们将标签中的字符显示出来,并把它们分别放入
该元素的伪元素::内容之前。
我们可以预先设置一组字符串,然后使用SASS函数随机生成每个元素中的内容。 伪代码如下:
<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);
}
简单解释一下里面的代码:
$str定义一串随机字符串,$length表示字符串的宽度
RandomChar()利用SASS的random()方法每次随机选取一个0-$length的整数,记为$r,然后利用SASS的str-slice方法每次从$str中选取一个随机数带下标 $r 的字符
randomChars()就是循环调用randomChar()从$str中随机生成一串字符串,字符串的粗细就是传入的参数$number
这样,每列中的字符每次都不一样:
其实我觉得上面的方法并不是最好的。 CSS伪元素的内容支持字符编码,例如content:'3066'; 将被渲染为字符て。 这样,通过设置字符范围,配合SASS函数,就可以更好的生成随机字符了,我也尝试了很长时间。 SASS函数生成的最终产物会在、3066等数字之间添加空格,最终无法通过字符编码转换成字符,最终被丢弃……
使用 CSS 提高打字效率
OK,继续,接下来我们就用CSS来实现打字效果,也就是让字符一一出现,像这样:
纯CSS实现文本输入的疗效
这是利用动画步骤的特性,即逐帧动画来实现的。
从左到右、从上到上的原理都是一样的。 以从左到右为例,假设我们有26个汉字,我们知道26个汉字组成的字符串的宽度,所以我们只需要设置一个For动画,让它的长度通过26帧从0-100%变化。 通过overflow:hidden,每帧步骤可以显示一个字符。
其实这里就需要一些小方法了。 我们如何通过字符数知道字符串的宽度呢?
要点:通过等宽字体的特性,配合CSS中的ch单元。
如果你不知道哪些字体家族是等宽的,可以阅读我的文章——《你应该知道的字体家族》[1]。
在CSS中,ch单位代表数字“0”的长度。 如果字体恰好是等宽字体,即每个字符的长度相同,那么可以将ch做成每个英文字符的长度,所以26ch也是整个字符串的粗细。
有了这个功能,结合动画步骤,我们就可以轻松的利用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;
}
}
您可以获得以下结果:
纯CSS实现文本输入的疗效
您可以点击这里查看完整代码:
CodePenDemo--纯CSS实现文本输入的疗效[2]
改变垂直打字的功效
出来后我们就用上面的方法修改一下。 将垂直打字效果更改为垂直打字效果。
核心伪代码如下:
<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点改造:
根据动画的animation-time和动画的animation-delay,在一定范围内降低随机性
在每个动画的最后或者过程中,替换伪元素的内容,即重新生成一个内容
您可以使用 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的匹配来获得实际的垂直高度。 所以这里有一个确定的选择。 在实际的动画中,没有单词的出现可能是不完整的。
事实上,在快速的动画效果下几乎无法察觉。
减少光影和透明度变化
最后一步是减少光影和透明度的变化。
最好的效果是让每个新角色的色温保持在最大值,而已经出现的角色的色温则逐渐降低。
而由于我们这里无法精细的控制每个字符,只能控制每一行字符,所以必须另想办法来实现。
最终的形式借用了另一个伪元素来进行同步笔画以达到最终的效果。 下面我们一步步看一下这个过程。
向文本添加条纹和突出显示
第一步,给文字添加条纹和高光,这很简单,只需在红色背景色下选择一条条纹,然后使用文字阴影使文字发光即可。
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;
}
看一下效果,右边是蓝色字符,中间改变了字符颜色,左边改变了字体颜色并添加了字体阴影:
向文本添加同步笔画
然后,在文本动画进行的过程中,在透明笔划上添加红色,尽可能恢复每个新字符的色温,而已经出现的字符的色温则逐渐降低。
这个疗效的示意图大致是这样的。 这里我将文本层和遮罩层分开,并将背景颜色从蓝色改为红色,以便于理解:
面罩行程示意图
笔画层的近似伪代码如下,使用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%;
}
}
嗯css 字体阴影,最后综合起来的疗效大致是这样的:
通过调整@keyframesmask的一些参数,可以得到不同的字符淡入淡出效果css 字体阴影,这需要一定的调试。
完整代码及效果
OK,我把主要步骤拆解了,最后上传了完整的代码,使用了Pug模板引擎和SASS句型。
完整的代码仅添加了100行。
.g-container
-for(var i=0; i<50; i++)
p
@import url('https://fonts.googleapis.com/css2?family=Inconsolata:wght@200&display=swap');
$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%;
}
}
最终疗效如题图所示:
数字字符雨动画
完整代码及演示效果可以点击这里:
CodePenDemo--DigitalCharRainAnimation[3]
参考
[1]
“你应该知道的字体系列”:
[2]
CodePenDemo--纯CSS实现文本输入的效果:
[3]
CodePenDemo——DigitalCharRainAnimation:
[4]
袁川:
[5]
CodePenDemo——Matrixdigitalrain:
[6]
CSS 灵感:#/
[7]
Github--iCSS:
发表评论