joshmoto
joshmoto

Reputation: 1760

use multiple css filters at the same time?

I am experimenting with css filters.

And I would like use the blur and grayscale at the same time, but I can't seem to use both simultaneously on the same image?

See fiddle here...

http://jsfiddle.net/joshmoto/fw0m9fzu/1/

.blur {
    filter: blur(5px);
    -webkit-filter: blur(5px);
    -moz-filter: blur(5px);
    -o-filter: blur(5px);
    -ms-filter: blur(5px);
}

.grayscale {
    filter: grayscale(1);
    -webkit-filter: grayscale(1);
    -moz-filter: grayscale(1);
    -o-filter: grayscale(1);
    -ms-filter: grayscale(1);
}


.blur-grayscale {
    filter: blur(5px) grayscale(1);
    -webkit-filter: blur(5px) grayscale(1);
    -moz-filter: blur(5px) grayscale(1);
    -o-filter: blur(5px) grayscale(1);
    -ms-filter: blur(5px) grayscale(1);
}

Upvotes: 126

Views: 160809

Answers (6)

Sean Vinci
Sean Vinci

Reputation: 56

Use CSS vars and fallbacks

Building off of Billy Mitchell's answer, you could set fallbacks for each custom property removing the need to define "empty" variables up front in the :root. Depending on how many filters you're using, this could get quite long, but possibly still a cleaner option.

.filter {
  filter: blur(var(--blur, 0)) contrast(var(--contrast, 1)) brightness(var(--brightness, 1)) grayscale(var(--grayscale, 0)) hue-rotate(var(--hue-rotate, 0deg)) invert(var(--invert, 0)) saturate(var(--saturate, 1)) sepia(var(--sepia, 0));
}

From MDN CSS custom properties (variables) article

The first argument to the function is the name of the custom property. The second argument to the function is an optional fallback value, which is used as the substitution value when the referenced custom property is invalid. The function accepts two parameters, assigning everything following the first comma as the second parameter. If the second parameter is invalid, the fallback will fail.

EXAMPLE

.myImage {

    /* Set baseline vars */
    --filter_Sepia: 0%;
    --filter_HueRotate: 0deg;
    --filter_Blur: 0px;

    /* Set one filter that handles multiple dynamic properties */
    /* Extra fallback (after the comma) if there's a problem getting a var */
    filter: sepia(var(--filter_Sepia, 0%)) hue-rotate(var(--filter_HueRotate, 0deg)) blur(var(--filter_Blur, 0px));
}

/* Classes for different external contexts; change var value accordingly */
.myImage.useSepia {
    --filter_Sepia: 90%;
}

.myImage.useHueRotate {
    --filter_HueRotate: 336deg;
}

.myImage.useBlur {
    --filter_Blur: 10px;
}

Upvotes: 1

Pierre-Alexis Ciavaldini
Pierre-Alexis Ciavaldini

Reputation: 3759

If that can be applied to your situation, you can use filter classes on parent elements. If you have an arbitrary number of classes to apply, you can create the containing divs using javascript.

<div class="blur">
  <div class="grayscale">
    <img />
  </div>
</div>

Upvotes: 0

Gorgsenegger
Gorgsenegger

Reputation: 7856

As this seems to be missing from the answers so far and only mentioned in a comment, here is what works:

Simply combine multiple filters separated by space, such as:

filter: grayscale() blur(5px);

Also see here.

Upvotes: 8

Soul Ripper
Soul Ripper

Reputation: 21

I am using this.

/* Multiple filters */
backdrop-filter: url(filters.svg#filter) blur(4px) saturate(150%);

Upvotes: 2

Billy Mitchell
Billy Mitchell

Reputation: 149

I'm trying to create utility classes in vanilla CSS and this would be helpful but it looks like it can not be done this way.

<img class="brightness-20 image-grayscale-100">
.brightness-20 {
    filter:brightness(20%);
}
.image-grayscale-100 {
    filter: grayscale(100%);
}

I'm not sure why they didn't just create a more specific property like:

filter-brightness: 20%; filter-grayscale: 100%

After some more work I came up with this solution:

/*Initalize Variables No Adjustments*/
:root {
    --blur:0px;
    --contrast:100%;
    --brightness:100%;
    --contrast:100%;
    --dropshadow:0px 0px 0px black;
    --grayscale:0%;
    --hue-rotate:0deg;
    --invert:0%;
    --opacity:100%;
    --saturate:100%;
    --sepia:0%;
}
/*Apply Defult Variables To Image*/
.filter {
    filter: blur(var(--blur)) contrast(var(--contrast)) brightness(var(--brightness)) contrast(var(--contrast)) drop-shadow(var(--dropshadow)) grayscale(var(--grayscale)) hue-rotate(var(--hue-rotate)) invert(var(--invert)) opacity(var(--opacity)) saturate(var(--saturate)) sepia(var(--sepia)); 
}
/*Override Defults*/
.brightness-20 {
    --brightness:20%;
}
.image-grayscale-100 {
    --grayscale: 100%;
}

Upvotes: 11

Ren&#233;
Ren&#233;

Reputation: 6176

Because it's one property named filter, every time you want to add a style to it you override it.

CSS version 1

Fortunately you can add multiple styles in some properties like background-image and filter! To get this working you'll have to put all the filter styles in one space separated filter property.

.grayscale.blur {
    filter: blur(5px) grayscale(1);
}

CSS version 2

An alternative, flexible, solution would be to create a "div soup" on purpose and set different filters in the html stack. e.g.

<div class='demo__blurwrap' style='filter: blur(5px);'>
    <div class="demo__graywrap" style='filter: grayscale(1);'>
        <img src="awesome_image.jpeg" alt="">
    </div>
</div>

CSS version 3

edit: just realised I just wrote this version with transforms, but the same idea applies.

Yet another solution is CSS vars. I wouldn't say it's ideal but it's a nice experiment. The major downside is that you need to declare a lot of variables, have default long rules for transform and nested transforms will definitely break.

// Added just for fun
setInterval(() => {
  yes_this_works_and_one_of_many_reasons_ids_are_bad.classList.toggle('translate');
}, 1000);
setInterval(() => {
  yes_this_works_and_one_of_many_reasons_ids_are_bad.classList.toggle('scale');
}, 1500);
:root {
  --scale: 1;
  --translate: 0px;
}
.box {
  background: blue;
  width: 20px;
  height: 20px;
  transform: 
    scale(var(--scale))
    translate(var(--translate), var(--translate));
  transition: transform .3s;
}
.box.translate {
  --translate: 20px;
}
.box.scale {
  --scale: 3;
}
<div 
  id='yes_this_works_and_one_of_many_reasons_ids_are_bad' 
  class='box scale translate'
></div>

Javascript

Lastly, if you were to use JavaScript to render the styles you can read the current applied filters using getComputedStyle and add more to the mix.

And a relevant article - this is more for animations and not yet supported by many browsers: Additive animations

And another relevant article on css-tricks: Houdini

Upvotes: 197

Related Questions