Add a gradually increasing blur to a block of text

Viewed 403

I have this text:

<div class"blur-gradient">
 <p>
    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Aenean et tortor at risus viverra adipiscing at in. Cursus vitae congue mauris rhoncus aenean vel. Tristique nulla aliquet enim tortor at auctor urna nunc id. Nisl tincidunt eget nullam non. Purus sit amet luctus venenatis lectus magna fringilla. Velit scelerisque in dictum non consectetur a erat nam. Vel pretium lectus quam id leo in. Tristique nulla aliquet enim tortor at auctor. In vitae turpis massa sed elementum. Erat imperdiet sed euismod nisi porta lorem mollis. Mattis enim ut tellus elementum sagittis. Convallis tellus id interdum velit laoreet id. Urna condimentum mattis pellentesque id nibh tortor id. Vitae congue mauris rhoncus aenean vel. Aliquet porttitor lacus luctus accumsan tortor posuere ac ut. Nulla porttitor massa id neque aliquam vestibulum morbi. Condimentum mattis pellentesque id nibh tortor id.
 </p>
 <p>
    Volutpat lacus laoreet non curabitur gravida arcu ac. Id aliquet lectus proin nibh nisl condimentum id. Sapien faucibus et molestie ac feugiat sed lectus. Facilisis magna etiam tempor orci eu lobortis elementum nibh. Dui vivamus arcu felis bibendum. Interdum posuere lorem ipsum dolor sit amet consectetur adipiscing elit. Odio pellentesque diam volutpat commodo sed egestas egestas. Adipiscing diam donec adipiscing tristique risus nec feugiat in. Risus nullam eget felis eget nunc lobortis. Accumsan sit amet nulla facilisi morbi. Elit eget gravida cum sociis natoque penatibus et magnis. Morbi tempus iaculis urna id volutpat lacus laoreet. In nibh mauris cursus mattis molestie a iaculis at erat. Fermentum dui faucibus in ornare quam. Duis at tellus at urna condimentum mattis pellentesque id. Est ullamcorper eget nulla facilisi. Sed arcu non odio euismod lacinia. Orci ac auctor augue mauris augue neque gravida in fermentum.
 </p>
 </div>

I want the text to start out with filter: blur(0px) and end up with filter: blur(4px).

Is this possible with css? If so, how do you recommend I go about it?

9 Answers

So we definitely can't apply double styles in one element in css. Eg This is wrong approach in css.

p{
filter:blur(0px);
...
filter:blur(4px);   // will set this style instead of 0px. ignoring above one.

font-size: 10px;
...
font-size: 13px;    // will set font-size to 13px instead of 10px; ignoring above one.
}

The only way that you can approach is you should wrap your text in several parts

And then apply css to each p.

.blur-gradient p:nth-child(1){
  filter: blur(1px);
}
.blur-gradient p:nth-child(2){
  filter: blur(2px);
}
...

To make smoother you should divide text more and more. This is only one way. You could make javascript code but not recommended.

It can be done with modern WebKit approach, but you need to duplicate the text layer

<div style="
    width: 400px;
    position: relative;
">
<p style="
    width: 100%;
    position: absolute;
    -webkit-mask-image: -webkit-gradient(linear, left 10%, left 70%, from(rgba(0,0,0,1)), to(rgba(0,0,0,0)));
">test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test</p>
<p style="
    -webkit-mask-image: -webkit-gradient(linear, left 10%, left bottom, from(rgba(0,0,0,0)), to(rgba(0,0,0,1)));
    filter: blur(4px);
">test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test</p>
</div>

One approach to achieve this effect is to divide the text into <span>s and apply an incrementally stronger filter: blur() to each <span>.

Working Example:

const blurGradient = document.getElementsByClassName('blur-gradient')[0];

// COUNT PARAGRAPHS
let blurGradientParagraphs = [...blurGradient.getElementsByTagName('p')];


// CALCULATE BLUR INCREMENT
let textLength = 0;

blurGradientParagraphs.forEach((blurGradientParagraph) => {
  
  textLength += blurGradientParagraph.textContent.length
});

const blurIncrement = (4 / textLength);


// SPLIT EACH PARAGRAPH INTO SPANS
blurGradientParagraphs.forEach((blurGradientParagraph) => {

  let blurGradientParagraphArray = blurGradientParagraph.textContent.split('');
  
  blurGradientParagraph.innerHTML = '<span>' + blurGradientParagraphArray.join('</span><span>') + '</span>';
  
});


// APPLY INCREMENTAL BLUR TO EACH SPAN, IN ORDER

let spans = [... blurGradient.getElementsByTagName('span')];

for (let i = 0; i < spans.length; i++) {

  spans[i].style.filter = 'blur(' + (blurIncrement * i) + 'px)';
}
<div class="blur-gradient">
 <p>
    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Aenean et tortor at risus viverra adipiscing at in. Cursus vitae congue mauris rhoncus aenean vel. Tristique nulla aliquet enim tortor at auctor urna nunc id. Nisl tincidunt eget nullam non. Purus sit amet luctus venenatis lectus magna fringilla. Velit scelerisque in dictum non consectetur a erat nam. Vel pretium lectus quam id leo in. Tristique nulla aliquet enim tortor at auctor. In vitae turpis massa sed elementum. Erat imperdiet sed euismod nisi porta lorem mollis. Mattis enim ut tellus elementum sagittis. Convallis tellus id interdum velit laoreet id. Urna condimentum mattis pellentesque id nibh tortor id. Vitae congue mauris rhoncus aenean vel. Aliquet porttitor lacus luctus accumsan tortor posuere ac ut. Nulla porttitor massa id neque aliquam vestibulum morbi. Condimentum mattis pellentesque id nibh tortor id.
 </p>
 <p>
    Volutpat lacus laoreet non curabitur gravida arcu ac. Id aliquet lectus proin nibh nisl condimentum id. Sapien faucibus et molestie ac feugiat sed lectus. Facilisis magna etiam tempor orci eu lobortis elementum nibh. Dui vivamus arcu felis bibendum. Interdum posuere lorem ipsum dolor sit amet consectetur adipiscing elit. Odio pellentesque diam volutpat commodo sed egestas egestas. Adipiscing diam donec adipiscing tristique risus nec feugiat in. Risus nullam eget felis eget nunc lobortis. Accumsan sit amet nulla facilisi morbi. Elit eget gravida cum sociis natoque penatibus et magnis. Morbi tempus iaculis urna id volutpat lacus laoreet. In nibh mauris cursus mattis molestie a iaculis at erat. Fermentum dui faucibus in ornare quam. Duis at tellus at urna condimentum mattis pellentesque id. Est ullamcorper eget nulla facilisi. Sed arcu non odio euismod lacinia. Orci ac auctor augue mauris augue neque gravida in fermentum.
 </p>
 </div>

You can use mask on any filter (filter and backdrop-filter). This will let you progressively blur content (text or not).

enter image description here

eg. https://codepen.io/QuiteQuinn/pen/jOBxGjr

In this specific case I would add an element on top of your content with the following css.

mask: linear-gradient(black, black, transparent);
backdrop-filter: blur(8px);

Support is there with the exception of Firefox for "backdrop-filter", as of June 5, 2021.

https://caniuse.com/css-backdrop-filter

I wanted to to know this also, but today was the day I would find out. After a lot of searching around the internet and learning some more css, I have found the best way to do this. We shouldn't achieve this with blur because blur is very difficult to control as a gradient.

p {
  font-size: 12px;
  color: black;
  -webkit-mask-image: linear-gradient(to bottom, rgba(255,255,255,1), rgba(255,255,255,0));
}
<p>
  Lorem ipsum dolor sit amet, consectetur adipiscing elit.
  Cras interdum purus sed felis accumsan, sit amet laoreet orci
  sodales. Nunc pretium ante vitae orci ultricies, id luctus arcu
  volutpat. Nulla ornare tellus et turpis pretium ullamcorper.
  Maecenas eget nunc at erat imperdiet auctor. Nulla eros ante,
  porta vitae semper rhoncus, dapibus non ligula. Proin cursus
  convallis tortor, sed molestie erat. Nullam mauris urna, varius
  et efficitur eu, porttitor et augue. Mauris condimentum egestas
  condimentum. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
  Cras interdum purus sed felis accumsan, sit amet laoreet orci
  sodales. Nunc pretium ante vitae orci ultricies, id luctus arcu
  volutpat. Nulla ornare tellus et turpis pretium ullamcorper.
  Maecenas eget nunc at erat imperdiet auctor. Nulla eros ante,
  porta vitae semper rhoncus, dapibus non ligula. Proin cursus
  convallis tortor, sed molestie erat. Nullam mauris urna, varius
  et efficitur eu, porttitor et augue. Mauris condimentum egestas
  condimentum.
</p>

I have seen this on lots of websites as a way for 'only premium members' to access the data. Now, I finally know how to do it! Obviously, this way can be adjusted to your needs, but this is the basic and general solution.

EDIT: I have found yet another way.

p {
  font-size: 12px;
  background: linear-gradient(to bottom, black, rgba(255, 255, 255, 0));
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
}
<p> 
  Lorem ipsum dolor sit amet, consectetur adipiscing elit.
  Cras interdum purus sed felis accumsan, sit amet laoreet orci
  sodales. Nunc pretium ante vitae orci ultricies, id luctus arcu
  volutpat. Nulla ornare tellus et turpis pretium ullamcorper.
  Maecenas eget nunc at erat imperdiet auctor. Nulla eros ante,
  porta vitae semper rhoncus, dapibus non ligula. Proin cursus
  convallis tortor, sed molestie erat. Nullam mauris urna, varius
  et efficitur eu, porttitor et augue. Mauris condimentum egestas
  condimentum. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
  Cras interdum purus sed felis accumsan, sit amet laoreet orci
  sodales. Nunc pretium ante vitae orci ultricies, id luctus arcu
  volutpat. Nulla ornare tellus et turpis pretium ullamcorper.
  Maecenas eget nunc at erat imperdiet auctor. Nulla eros ante,
  porta vitae semper rhoncus, dapibus non ligula. Proin cursus
  convallis tortor, sed molestie erat. Nullam mauris urna, varius
  et efficitur eu, porttitor et augue. Mauris condimentum egestas
  condimentum.
 </p>

backdrop-filter combined with mask can approximate this. The only drawback is the low support

.box {
  font-size:25px;
  margin:0 10%;
  position:relative;
}
.box::before {
  content:"";
  position:absolute;
  top:0;
  left:0;
  right:0;
  bottom:0;
  backdrop-filter:blur(5px);
  -webkit-mask:linear-gradient(transparent,#fff);
  pointer-events:none;
}
<div class="box">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas rhoncus lobortis justo sed interdum. Ut quis purus lacinia, gravida justo eu, auctor mauris. Phasellus imperdiet, elit in lobortis fermentum, lacus diam sollicitudin lectus, in suscipit eros dui vitae sem. Integer commodo et nibh ut vulputate. Duis vel mauris pharetra, venenatis libero eu, malesuada elit. In rhoncus mi tellus, a lacinia orci vehicula id. Aliquam ultricies dolor leo lectus, in suscipit eros dui vitae sem. Integer commodo et nibh ut vulputate. Duis vel mauris pharetra, venenatis libero eu, malesuada elit. In rhoncus mi tellus, a lacinia orci vehicula id. Aliquam ultricies dolor leo
</div>

This question intrigued me enough to try and work out a "decent" solution which I consider as such that would gradually blur only the last few lines, in a visual pleasing manner, without the hacks of duplicate-content such as the other answers here.

Mixing backdrop-filter with mask-image (with a gradient), this is achievable, but only for Chrome browser and FF when a flag (layout.css.backdrop-filter.enabled) is set to true in about:config.

See Codepen Demo

The below code is the compiled versions from the SCSS of my Codepen demo, which is using auto-prefixing. I advise playing with the parameters of the source code in Codepen due to the lack of Stackoverflow in supporting SCSS syntax...

html{
  --blur: 6px;
}

p{
  font: calc(.5vw + 11px) Arial;
  width: 50%;
  position: relative;
}

p::after {
  content: '';
  position: absolute;
  top: 0;
  bottom: calc(var(--blur) * -1);
  right: calc(var(--blur) * -2);
  left: calc(var(--blur) * -2);
  -webkit-backdrop-filter: blur(var(--blur)) brightness(1.1);
          backdrop-filter: blur(var(--blur)) brightness(1.1);
          
  -webkit-mask-image: -webkit-gradient(linear, left bottom, left top, 
                      color-stop(1em, red), 
                      color-stop(2.5em, transparent)), 
                      -webkit-gradient(linear, left bottom, left top, 
                      color-stop(2.5em, rgba(0, 0, 0, 0.88)), 
                      color-stop(5em, transparent));
  -webkit-mask-image: linear-gradient(to top, red 1em, transparent 2.5em), 
                      linear-gradient(to top, rgba(0, 0, 0, 0.88) 2.5em, transparent 5em);

}

/* Non-supported browsers fallback to a fade without blur: */
@supports not ((-webkit-backdrop-filter: none) or (backdrop-filter: none)) {
  p::after {
    background-color: white;
    mask-image: linear-gradient(transparent 60%, white calc(100% - var(--blur)));
  }
}
<p>
  The <mark>backdrop-filter</mark> CSS property lets you apply graphical effects such as blurring or color shifting to 
the area behind an element. Because it applies to everything behind the element, to see the effect 
you must make the element or its background at least partially transparent.
  <br><br>
  The feature can be enabled in Firefox by setting the <em><code>layout.css.backdrop-filter.enabled</code></em> to true in <em><code>about:config</code></em> (soon-to-be rolled-out). 
</br>
<br><br>
  The <mark>mask-image</mark> property may reference a mask element. The content of the mask element serves as the mask. 
  Alternatively, for many simple uses, the <code>mask-image</code> property may refer directly to images to be used as mask, 
forgoing the need for an explicit mask element. This mask can then be sized and positioned 
just like CSS background images using the mask-position, mask-size and other characterizing properties. 
</p>

In this demo I have tried to only blur the last few lines, and especially the very last ones, so it would be completely unreadable.

To make the fade more gradual, regardless of the lines, I would suggest this:

mask-image: linear-gradient(transparent 50%, red calc(90% - 1em - var(--blur)));

(the name of the color is irrelevant here, so i'm using the shortest name)

Generates two elements :before and :after. They get text from attribute data-text

The idea is for the two elements to have opposite transparency. As one, a blur value is set.

This solution not have a SEO problem for duplicate text in content!

Example:

.myText {
    position: relative;
    z-index: 0;
    padding-bottom: 20px;
}

.myText:before {
    content: attr(data-text);
    position: relative;
    background: linear-gradient(to right, rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0) 100%);
    -webkit-mask-image: linear-gradient(to left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 1) 100%);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    z-index: 1;
}

.myText:after {
    content: attr(data-text);
    position: absolute;
    top: 0;
    right: 0;
    left: 0;
    bottom: 0;
    -webkit-mask-image: linear-gradient(to top, rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, 0) 100%);
    -webkit-background-clip: text;
    -webkit-filter: blur(4px);
    z-index: -1;
}

.myText span {
    position: absolute;
    top: 0;
    right: 0;
    left: 0;
    bottom: 0;
    color: rgba(0,0,0,0);
    z-index: 2;
}
<div class="myText" data-text="Lorem ipsum dolor sit amet consectetur adipisicing elit. Iure qui, earum tenetur facilis quod vero, porro, sequi consequuntur inventore id atque modi fugiat soluta iste? Eveniet omnis animi mollitia doloribus? Lorem ipsum dolor sit, amet consectetur adipisicing elit. Odio tenetur et vero placeat eaque harum minus velit, ratione iste
architecto rem dolores cumque officiis perferendis saepe vitae provident mollitia sed! Lorem ipsum dolor sit, amet consectetur adipisicing elit. Perferendis libero, dolor officiis vero aspernatur itaque? In obcaecati nobis reprehenderit dolorem autem deserunt. Ratione, reiciendis adipisci. Sed consectetur ipsa tempora! Lorem ipsum dolor sit amet consectetur adipisicing elit. Iure qui, earum tenetur facilis quod vero, porro, sequi consequuntur inventore id atque modi fugiat soluta iste? Eveniet omnis animi mollitia doloribus? Lorem ipsum dolor sit, amet consectetur adipisicing elit. Odio tenetur et vero placeat eaque harum minus velit, ratione iste architecto rem dolores cumque officiis perferendis saepe vitae provident mollitia sed! Lorem ipsum dolor sit, amet consectetur adipisicing elit. Perferendis libero, dolor officiis vero aspernatur itaque? In obcaecati nobis reprehenderit dolorem autem deserunt. Ratione, reiciendis adipisci. Sed consectetur ipsa tempora!">
    <span>
        Lorem ipsum dolor sit amet consectetur adipisicing elit. Iure qui, earum tenetur facilis quod vero, porro, sequi consequuntur inventore id atque modi fugiat soluta iste? Eveniet omnis animi mollitia doloribus? Lorem ipsum dolor sit, amet consectetur adipisicing elit. Odio tenetur et vero placeat eaque harum minus velit, ratione iste architecto rem dolores cumque officiis perferendis saepe vitae provident mollitia sed! Lorem ipsum dolor sit, amet consectetur adipisicing elit. Perferendis libero, dolor officiis vero aspernatur itaque? In obcaecati nobis reprehenderit dolorem autem deserunt. Ratione, reiciendis adipisci. Sed consectetur ipsa tempora! Lorem ipsum dolor sit amet consectetur adipisicing elit. Iure qui, earum tenetur facilis quod vero, porro, sequi consequuntur inventore id atque modi fugiat soluta iste? Eveniet omnis animi mollitia doloribus? Lorem ipsum dolor sit, amet consectetur adipisicing elit. Odio tenetur et vero placeat eaque harum minus velit, ratione iste architecto rem dolores cumque officiis perferendis saepe vitae provident mollitia sed! Lorem ipsum dolor sit, amet consectetur adipisicing elit. Perferendis libero, dolor officiis vero aspernatur itaque? In obcaecati nobis reprehenderit dolorem autem deserunt. Ratione, reiciendis adipisci. Sed consectetur ipsa tempora!
    </span>
</div>

Please try to use with CSS animation:

<style> 
div p {
  animation-name: example;
  animation-duration: 4s;
  animation-fill-mode: forwards;
}

@keyframes example {
  from { filter: blur(0px); }
  to { filter: blur(4px); }
}
</style>
Related