Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

【动画进阶】单标签实现多行文本随滚动颜色变换 #269

Open
chokcoco opened this issue Jul 28, 2024 · 0 comments
Open

【动画进阶】单标签实现多行文本随滚动颜色变换 #269

chokcoco opened this issue Jul 28, 2024 · 0 comments

Comments

@chokcoco
Copy link
Owner

chokcoco commented Jul 28, 2024

本文,我们来看这么个有意思的问题。使用 CSS,实现在页面屏幕中有一长串多行字母,现在需要随着页面滚动,改变每个字母的颜色。这也是掘金上一个同学的私信提问:

这里和这位同学细聊,他没有回复,有一个细节我没获取到,就是多行文本一开始是文字颜色是规律的还是不规律的,另外一个点,随着页面滚动,改变每个字母的颜色,需要有规律的改变,还是没有规律的改变?

这问题细琢磨,还是非常有意思的,效果的核心是:

  1. 多行文本,如何使用尽可能少的标签,让多行文本下的每一个字,呈现不一样的内容
  2. 随着改动,如何去控制每一个字颜色的改变?

带着这两个疑问,我们尝试使用 CSS 一步一步解决它!

单个标签实现多行文本下单个文字不同颜色

我们先来看,如何使用单个标签实现多行文本下单个文字不同颜色。这是之前在 【动画进阶】单标签下多色块随机文字随机颜色动画 讲解过的一个技巧。

看看如下效果:

停顿 10s,思考一下,如果仅仅使用一个标签,实现上面的效果,这是可能的吗?

这里,我们还可以利用内联元素的 background 展示特性来实现。

什么意思呢?其实 background 的展示,在 块级元素状态内联元素状态 下的展示规则是不一样的。

表现为 display: inline 内联元素的 background 展现形式与 display: block 块级元素(或者 inline-blockflexgrid)不一致。

简单看个例子:

<p>Lorem .....</p><a>Lorem .....</a>

这里需要注意,<p> 元素是块级元素,而 <a>内联元素

我们给它们统一添加上一个从绿色到蓝色的渐变背景色:

p, a {
  background: linear-gradient(90deg, blue, green);
}

看看效果:

什么意思呢?区别很明显:

  1. 块级元素的背景整体是一个渐变整体
  2. 内联元素的背景效果是以行为单位进行串连的,每一行都是会有不一样的效果,每行连起来整体组成一个完整的背景效果

基于这一点,我们同样可以实现单个 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 函数,其中:

  1. $count 表示需要的色块个数
  2. $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: text0color: transparent,即可实现单个标签下,多行文本每个文字的颜色都不一样:

整个效果的核心,就是如何利用渐变,给每个文字宽度下,设置不一样的颜色

基于滚动驱动动画,实现滚动下文字变色效果

下面,我们继续实现基于滚动驱动动画,实现滚动下文字变色效果

当然,这里的滚动动作,只是实现动画效果的载体,其背后的本质还是基于上述效果下的文字变色。

这里也很好实现,我们有两种方式,基于上述效果实现文字变色效果:

  1. 利用 filter: hue-rotate() 动画,实现文字变色
  2. 利用 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;
    }
}

上面是完整的代码,其实代码量非常少,添加的代码做了几件事:

  1. 文本效果,通过 fixed 定位到页面中心
  2. 添加了一个 300vh 高的容器,利用这个容器,触发 body 的滚动效果,并且给 body 设置了滚动容器 name scroll-timeline-name: --my-scroller
  3. 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)中的1400px2800px,以及 animation: colorChange steps(56)` 中的 56 步骤,都需要根据实际文字的数量进行计算。(当然实际过程中也需要考虑不同字体带来的单个字的宽度影响,一般使用等宽字体处理)。

这样,我们就可以得到这么一种效果:

多行文本字体颜色,随着滚动过程的规律变换,并且重要的是,这里不需要对每个文字单独使用一个标签!

完整的代码效果,你可以戳这里:CodePen -- Text Color Change With Scroll

最后

好了,本文到此结束,希望本文对你有所帮助 :)

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

更多精彩 CSS 技术文章汇总在我的 Github -- iCSS ,持续更新,欢迎点个 star 订阅收藏。

如果还有什么疑问或者建议,可以多多交流,原创文章,文笔有限,才疏学浅,文中若有不正之处,万望告知。

# for free to join this conversation on GitHub. Already have an account? # to comment
Projects
None yet
Development

No branches or pull requests

1 participant