33

I'm trying to implement a text loading animation using only CSS. what I have is a text with black color, then when the page loads the text will fill start filling with a red color over several seconds.

The issue I'm facing is that the text loading animation is working fine, but when the text ends and begins with a new line the animation text still continues on the same line.

How can I fix this?

CSS loading text

body {
  background: #3498db;
  font-family: sans-serif;
}

h1 {
  position: relative;
  color: rgba(0, 0, 0, .3);
  font-size: 5em;
  white-space: wrap;
}

h1:before {
  content: attr(data-text);
  position: absolute;
  overflow: hidden;
  max-width: 100%;
  white-space: nowrap;
  word-break: break-all;
  color: #fff;
  animation: loading 8s linear;
}

@keyframes loading {
  0% {
    max-width: 0;
  }
}
<h1 data-text="Suspendisse mollis dolor vitae porta egestas. Nunc nec congue odio.">Suspendisse mollis dolor vitae porta egestas. Nunc nec congue odio.</h1>
Temani Afif
  • 245,468
  • 26
  • 309
  • 415
CJAY
  • 6,989
  • 18
  • 64
  • 106
  • 1
    Hi! Great question, is CSS only mandatory? Since I believe this is impossible with mere CSS because you're animating the width of the tag. Line breaks will always break this format. – roberrrt-s Dec 21 '18 at 09:18
  • 1
    @Roberrrt hi, i'm only trying to reduce the effort of writing more code. i think css should have the other way around... i'm even trying other possibilities. and also considering that javascript can be disabled by browser. – CJAY Dec 21 '18 at 09:21
  • 1
    @Roberrrt there is a solution with CSS ;) – Temani Afif Dec 21 '18 at 09:42

1 Answers1

37

An idea is to consider gradient coloration with background-clip: text applied to an inline element.

body {
  background: #3498db;
  font-family: sans-serif;
}

h1 {
  font-size: 5em;
}

h1 span {
  background:
    linear-gradient(#fff,#fff) left no-repeat
    rgba(0, 0, 0, .3);
  background-size:0% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
  animation:loading 5s forwards linear;
}

@keyframes loading {
  to {
    background-size:100% 100%;
  }
}
<h1><span>Suspendisse mollis dolor vitae porta egestas. Nunc nec congue odio.</span></h1>

To better understand how it works, here is a basic example where you can see how inline element behave with background coloration and how its different from block level element:

.color {
  font-size: 1.5em;
  line-height: 1.5em;
  border: 2px solid;
  background: linear-gradient(red, red) left no-repeat;
  background-size: 0% 100%;
  animation: change 5s linear forwards;
}

@keyframes change {
  100% {
    background-size: 100% 100%
  }
}
<span class="color">
 lorem ipsume lorem ipsume lorem ipsume lorem ipsume lorem ipsume lorem ipsume lorem ipsume lorem ipsume lorem ipsume 
</span>
<div class="color">
  lorem ipsume lorem ipsume lorem ipsume lorem ipsume lorem ipsume lorem ipsume lorem ipsume lorem ipsume lorem ipsume
</div>

I simply apply the same logic using background-clip:text to color the text instead of the background:

.color {
  font-size: 1.5em;
  line-height: 1.5em;
  border: 2px solid;
  background: linear-gradient(red, red) left no-repeat;
  background-size: 0% 100%;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  color: transparent;
  animation: change 5s linear forwards;
}

@keyframes change {
  100% {
    background-size: 100% 100%
  }
}
<span class="color">
 lorem ipsume lorem ipsume lorem ipsume lorem ipsume lorem ipsume lorem ipsume lorem ipsume lorem ipsume lorem ipsume 
</span>
<div class="color">
  lorem ipsume lorem ipsume lorem ipsume lorem ipsume lorem ipsume lorem ipsume lorem ipsume lorem ipsume lorem ipsume
</div>
Temani Afif
  • 245,468
  • 26
  • 309
  • 415
  • Could you also explain the logic behind this? I can't wrap my head around what's happening here. @TemaniAfif – roberrrt-s Dec 21 '18 at 09:47
  • Ah, playing around explained this. But it still dazzles me this works as intended. The background-clip: text; creates a background of the text itself. Animating the background-size will simply treat the text as a solid rectangle, therefore not taking the line breaks into account. That's very smart indeed! – roberrrt-s Dec 21 '18 at 09:49
  • 2
    @Roberrrt it's all about the inline element .. unlike block level element, the coloration is not made to the whole block but to each line ... it's like you have a long continous line that you cut but they are still linked. Check this with border and you will understand : https://jsfiddle.net/xf73yg9u/ .. then I apply the same logic with background making only the text colored – Temani Afif Dec 21 '18 at 09:49
  • 1
    @MithunRaikar I added another example to explain how it works – Temani Afif Dec 21 '18 at 10:00
  • This doesn’t work on mobile (iOS Safari 12): the first line is animated, but everything else remains blue – Charlie Harding Dec 25 '18 at 22:32