Skip to content
New issue

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

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

Already on GitHub? Sign in 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 订阅收藏。

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

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

No branches or pull requests

1 participant