本文,我们来看这么个有意思的问题。使用 CSS,实现在页面屏幕中有一长串多行字母,现在需要随着页面滚动,改变每个字母的颜色。这也是掘金上一个同学的私信提问:
这里和这位同学细聊,他没有回复,有一个细节我没获取到,就是多行文本一开始是文字颜色是规律的还是不规律的,另外一个点,随着页面滚动,改变每个字母的颜色,需要有规律的改变,还是没有规律的改变?
这问题细琢磨,还是非常有意思的,效果的核心是:
- 多行文本,如何使用尽可能少的标签,让多行文本下的每一个字,呈现不一样的内容
- 随着改动,如何去控制每一个字颜色的改变?
带着这两个疑问,我们尝试使用 CSS 一步一步解决它!
单个标签实现多行文本下单个文字不同颜色
我们先来看,如何使用单个标签实现多行文本下单个文字不同颜色。这是之前在 【动画进阶】单标签下多色块随机文字随机颜色动画 讲解过的一个技巧。
看看如下效果:
停顿 10s,思考一下,如果仅仅使用一个标签,实现上面的效果,这是可能的吗?
这里,我们还可以利用内联元素的 background
展示特性来实现。
什么意思呢?其实 background
的展示,在 块级元素状态 和 内联元素状态 下的展示规则是不一样的。
表现为 display: inline
内联元素的 background
展现形式与 display: block
块级元素(或者 inline-block
、flex
、grid
)不一致。
简单看个例子:
<p>Lorem .....</p><a>Lorem .....</a>
这里需要注意,<p>
元素是块级元素,而 <a>
是内联元素。
我们给它们统一添加上一个从绿色到蓝色的渐变背景色:
p, a {
background: linear-gradient(90deg, blue, green);
}
看看效果:
什么意思呢?区别很明显:
- 块级元素的背景整体是一个渐变整体
- 内联元素的背景效果是以行为单位进行串连的,每一行都是会有不一样的效果,每行连起来整体组成一个完整的背景效果
基于这一点,我们同样可以实现单个 DIV 下的多重背景。
举个例子:
<div class="g-container">
<span>ABCDEFGHIJKL</span>
</div>
div {
width: 300px;
}
span{
color: #000;
font-size: 50px;
line-height: 50px;
letter-spacing: 25px;
word-wrap: break-word;
background: #fc0;
}
此时,我们只设置了一个背景 background: #fc0
,效果如下:
基于上面说的技巧,我们改造一下 background: #fc0
,拆分成多段渐变背景:
span{
//...
background: linear-gradient(
90deg,
#fc0 0 25%,
#0f0 0 50%,
#00f 0 75%,
#f00 0 100%
);
}
这里,我们每隔 25%,设置了一段不同的颜色,如此一来,整个背景色就变成了 4 块:
基于这个技巧,我们同样可以封装一个 SCSS 函数,用于在单个 DIV 下生成多段色块。代码如下:
@use "sass:string";
@import url('https://fonts.googleapis.com/css2?family=Righteous&family=Ubuntu+Mono&display=swap');
$str: 'QWERTYUIOPASDFGHJKLZXCVBNMabcdefghigklmnopqrstuvwxyz123456789';
$length: str-length($str);
@function randomNum($max, $min: 0, $u: 1) {
@return ($min + random($max)) * $u;
}
@function randomColor() {
@return rgb(randomNum(205, 50), randomNum(255), randomNum(255));
}
@function randomLinear($count, $width) {
$value: '';
@for $i from 0 through ($count - 1) {
$j: $i - 1;
$value: $value + randomColor() + string.unquote(" #{$j * $width}px #{$i * $width}px,");
}
@return linear-gradient(90deg, string.unquote(#{$value}) randomColor() 0 100%);
}
span {
background: randomLinear(36, 50);
}
上面的代码,我们实现了一个 randomLinear($count, $width)
的 SCSS 函数,其中:
$count
表示需要的色块个数$width
表示每个色块的宽度
如此一来,在一个 300px x 300px
的内联元素内,我们同样可以实现多个不同的随机颜色。利用这个技巧,一样可以实现单个平面下的随机文字随机颜色效果:
剩余的技巧都是相同的,这里就不再赘述,此技巧的完整代码,你可以戳这里:CodePen Demo — Single Div Random Text And Random Color
我们将上述技巧,运用在我们今天需要实现的效果之上。
假设,我们有如下一段多行文本:
<a>Lorem ipsum dolor sit amet consectetur adipisicing elit.?</a>
效果如下:
我们只需要利用 inline
display 的特性,利用渐变实现每个文字宽度下,不一样的颜色效果:
给上述效果,添加上 backgroug-clip: text
和 color: transparent
,即可实现单个标签下,多行文本每个文字的颜色都不一样:
整个效果的核心,就是如何利用渐变,给每个文字宽度下,设置不一样的颜色。
基于滚动驱动动画,实现滚动下文字变色效果
下面,我们继续实现基于滚动驱动动画,实现滚动下文字变色效果。
当然,这里的滚动动作,只是实现动画效果的载体,其背后的本质还是基于上述效果下的文字变色。
这里也很好实现,我们有两种方式,基于上述效果实现文字变色效果:
- 利用
filter: hue-rotate()
动画,实现文字变色 - 利用
background-position
的位移动画,实现文字变色效果
两种方式,都可以实现,多行文本下,每个文字的颜色单独变色。
再不加入滚动驱动动画之前,我们首先实现多行文字的颜色变换。
上述效果的完整代码,目前是这样的:
<a>Lorem ipsum dolor sit amet consectetur adipisicing elit.?</a>
@use "sass:string";
@function randomLinear($count, $width) {
$value: '';
@for $i from 0 through ($count - 1) {
$j: $i - 1;
$value: $value + randomColor() + string.unquote(" #{$j * $width}px #{$i * $width}px,");
}
@return linear-gradient(90deg, string.unquote(#{$value}) randomColor() 0 100%);
}
@function randomColor() {
@return rgb(randomNum(205, 50), randomNum(255), randomNum(255));
}
@function randomNum($max, $min: 0, $u: 1) {
@return ($min + random($max)) * $u;
}
a {
width: 600px;
font-size: 42px;
line-height: 54px;
font-family: "Roboto Mono", monospace;
background: randomLinear(72, 25);
background-clip: text;
color: transparent;
}
这里唯一需要花时间理解的是 background: randomLinear(72, 25)
绘制的渐变图案。这里实现了一个 72 段长的渐变效果,每个渐变段的宽度是 25px
。
解释一下,由于我们单行的宽度是 600px
,所以 background: randomLinear(72, 25)
恰好可以实现一个铺满 3 行且每个文字宽度下色块颜色不一致的渐变效果。
接着,我们通过控制 background-position
的变换,实现文字颜色的切换,只需要加入如下的代码:
a {
...
animation: colorChange 5s steps(18);
}
@keyframes colorChange {
0% {
background-position: 0 0;
}
100% {
background-position: -1800px 0;
}
}
其中 72 * 25 = 1800
,所以,我们利用 steps
步骤动画,实现一个渐变色块的位移动画(即 background-position
位移动画),运用到整个效果上,呈现出来的效果就是文字颜色的跳变:
有了这一步,接下来,我们只需要把动画效果,改为通过滚动动作控制即可,这里我们需要用到 CSS 最新的规范 — 滚动驱动动画 — animation-timeline,关于这个最新规范,你可以看看 XboxYan 老师的这篇文章,进行快速入门:
到这里,假设你对 滚动驱动动画 — animation-timeline 已经掌握,我们只需要简单的改造上述的代码,给页面提供一个滚动动作,并且将动画关联上即可,修改我们的代码:
<div class="g-scroll"></div>
<p><a>Lorem ipsum dolor sit amet consectetur adipisicing elit.?</a></p>
@use "sass:string";
@import url('https://fonts.googleapis.com/css2?family=Carter+One&family=Roboto+Mono:ital,wght@0,100..700;1,100..700&family=Sofia+Sans&display=swap');
@function randomLinear($count, $width) {
$value: '';
@for $i from 0 through ($count - 1) {
$j: $i - 1;
$value: $value + randomColor() + string.unquote(" #{$j * $width}px #{$i * $width}px,");
}
@return linear-gradient(90deg, string.unquote(#{$value}) randomColor() 0 100%);
}
@function randomColor() {
@return rgb(randomNum(205, 50), randomNum(255), randomNum(255));
}
@function randomNum($max, $min: 0, $u: 1) {
@return ($min + random($max)) * $u;
}
body, html {
width: 100%;
height: 100%;
overflow: scroll;
background: #000;
scroll-timeline-name: --my-scroller;
}
.g-scroll {
height: 300vh;
}
p {
position: fixed;
display: inline;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
margin: auto;
width: 600px;
}
a {
width: 600px;
font-size: 42px;
line-height: 54px;
font-family: "Roboto Mono", monospace;
color: #fff;
background: randomLinear(72, 25);
background-clip: text;
color: transparent;
animation: colorChange steps(18);
animation-timeline: --my-scroller;
}
@keyframes colorChange {
0% {
background-position: 0 0;
}
100% {
background-position: -1800px 0;
}
}
上面是完整的代码,其实代码量非常少,添加的代码做了几件事:
- 文本效果,通过
fixed
定位到页面中心 - 添加了一个
300vh
高的容器,利用这个容器,触发 body 的滚动效果,并且给body
设置了滚动容器 namescroll-timeline-name: --my-scroller
- 将
a
标签下的动画,改成关联页面的滚动动作animation-timeline: --my-scroller
这样,我们就实现了,在滚动过程中,改变多行文本的每一个字的颜色,并且,其核心效果,其实都是利用一个标签实现的:
完整的代码,你可以戳这里:CodePen Demo — Text Color Change With Scroll
当然,如果这样的效果都已经能够掌握了,那么想要实现规则的文字颜色的规则变换,也非常轻松。
我们改造一下代码,这次的渐变色不需要那么复杂:
<div class="g-scroll"></div>
<p><a>Lorem ipsum dolor sit amet consectetur adipisicing elit.?</a></p>
body, html {
width: 100%;
height: 100%;
overflow: scroll;
background: #000;
scroll-timeline-name: --my-scroller;
}
.g-scroll {
height: 300vh;
}
p {
position: fixed;
display: inline;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
margin: auto;
width: 600px;
}
a {
width: 600px;
font-size: 42px;
line-height: 54px;
font-family: "Roboto Mono", monospace;
color: #fff;
background: linear-gradient(90deg, #fc0 0 1400px, #fff 0 2800px);
background-size: 2800px 100%;
background-clip: text;
color: transparent;
animation: colorChange steps(56);
animation-timeline: --my-scroller;
}
@keyframes colorChange {
0% {
background-position: -1400px 0;
}
100% {
background-position: 0 0;
}
}
由于这里需要精确控制滚动过程中,字体颜色从一个颜色,到滚动到底部变换到另外一个颜色。上面的 background: linear-gradient(90deg, #fc0 0 1400px, #fff 0 2800px)中的
1400px和
2800px,以及
animation: colorChange steps(56)` 中的 56 步骤,都需要根据实际文字的数量进行计算。(当然实际过程中也需要考虑不同字体带来的单个字的宽度影响,一般使用等宽字体处理)。
这样,我们就可以得到这么一种效果:
多行文本字体颜色,随着滚动过程的规律变换,并且重要的是,这里不需要对每个文字单独使用一个标签!
完整的代码效果,你可以戳这里:CodePen — Text Color Change With Scroll
本文章转载于iCSS前端趣闻 ,作者SbCo。