本文,我们来看这么个有意思的问题。使用 CSS,实现在页面屏幕中有一长串多行字母,现在需要随着页面滚动,改变每个字母的颜色。这也是掘金上一个同学的私信提问:
这里和这位同学细聊,他没有回复,有一个细节我没获取到,就是多行文本一开始是文字颜色是规律的还是不规律的,另外一个点,随着页面滚动,改变每个字母的颜色,需要有规律的改变,还是没有规律的改变?
这问题细琢磨,还是非常有意思的,效果的核心是:
-
多行文本,如何使用尽可能少的标签,让多行文本下的每一个字,呈现不一样的内容
-
带着这两个疑问,我们尝试使用 CSS 一步一步解决它!
单个标签实现多行文本下单个文字不同颜色
我们先来看,如何使用单个标签实现多行文本下单个文字不同颜色。这是之前在
【动画进阶】单标签下多色块随机文字随机颜色动画
[1]
讲解过的一个技巧。
看看如下效果:
停顿 10s,思考一下,如果仅仅使用一个标签,实现上面的效果,这是可能的吗?
这里,我们还可以利用内联元素的
background
展示特性来实现。
什么意思呢?其实
background
的展示,在
块级元素状态
和
内联元素状态
下的展示规则是不一样的。
表现为
display: inline
内联元素的
background
展现形式与
display: block
块级元素(或者
inline-block
、
flex
、
grid
)不一致。
简单看个例子:
<p>Lorem .....p><a>Lorem .....a>
这里需要注意,
元素是
块级元素
,而
是
内联元素
。
我们给它们统一添加上一个从绿色到蓝色的渐变背景色:
p, a {
background: linear-gradient(90deg, blue, green);
}
看看效果:
什么意思呢?区别很明显:
-
-
内联元素的背景效果是以行为单位进行串连的,每一行都是会有不一样的效果,每行连起来整体组成一个完整的背景效果
基于这一点,我们同样可以实现单个 DIV 下的多重背景。
举个例子:
<div class="g-container">
<span>ABCDEFGHIJKLspan>
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 函数,其中:
-
-
如此一来,在一个
300px x 300px
的内联元素内,我们同样可以实现多个不同的随机颜色。利用这个技巧,一样可以实现单个平面下的随机文字随机颜色效果:
剩余的技巧都是相同的,这里就不再赘述,此技巧的完整代码,你可以戳这里:
CodePen Demo -- Single Div Random Text And Random Color
[2]
我们将上述技巧,运用在我们今天需要实现的效果之上。
假设,我们有如下一段多行文本:
<a>Lorem ipsum dolor sit amet consectetur adipisicing elit.?a>
效果如下:
image.png
我们只需要利用
inline
display 的特性,利用渐变实现每个文字宽度下,不一样的颜色效果:
image.png
给上述效果,添加上
backgroug-clip: text
和
color: transparent
,即可实现单个标签下,多行文本每个文字的颜色都不一样:
image.png
整个效果的核心,
就是如何利用渐变,给每个文字宽度下,设置不一样的颜色
。
基于滚动驱动动画,实现滚动下文字变色效果
下面,我们继续实现
基于滚动驱动动画,实现滚动下文字变色效果
。
当然,这里的滚动动作,只是实现动画效果的载体,其背后的本质还是基于上述效果下的文字变色。
这里也很好实现,我们有两种方式,基于上述效果实现文字变色效果:
-
利用
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
[3]
,关于这个最新规范,你可以看看 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;
}
}
上面是完整的代码,其实代码量非常少,添加的代码做了几件事:
-
-
添加了一个
300vh
高的容器,利用这个容器,触发 body 的滚动效果,并且给
body
设置了滚动容器 name
scroll-timeline-name: --my-scroller
-
将
a
标签下的动画,改成关联页面的滚动动作
animation-timeline: --my-scroller
这样,我们就实现了,在滚动过程中,改变多行文本的每一个字的颜色,并且,其核心效果,其实都是利用一个标签实现的:
完整的代码,你可以戳这里:
CodePen Demo -- Text Color Change With Scroll
[5]
当然,如果这样的效果都已经能够掌握了,那么想要实现规则的文字颜色的规则变换,也非常轻松。
我们改造一下代码,这次的渐变色不需要那么复杂:
<div class="g-scroll">div>
<p><a>Lorem ipsum dolor sit amet consectetur adipisicing elit.?a>p>
body, html {
width: 100%