Search code examples
htmlcsscss-animationscss-transformscss-filters

CSS blur filter on 3D transformed elements


document.querySelector( 'style' ).innerHTML += `
  hr:nth-of-type( 1 ) {
    transform: translateZ( calc( var( --mid ) * 1rem ) );
  }
  hr:nth-of-type( 2 ) {
    transform: translateZ( calc( var( --mid ) * -1rem ) );
  }
  hr:nth-of-type( 3 ) {
    transform: rotateY( 90deg ) translateZ( calc( var( --mid ) * 1rem ) );
  }
  .rotate_y, .rotate_x, hr {
    animation-name: rotateY;
    animation-duration: 8s;
    animation-delay: 0s;
    animation-timing-function: linear;
    animation-iteration-count: infinite;
  }
  @keyframes rotateY {
    0% {
      transform: rotateY( 0deg );
    }
    100% {
      transform: rotateY( 360deg );
    }  
  }
  .rotate_x {
    animation-name: rotateX;
  }
  @keyframes rotateX {
    0% { transform: rotateX( 0deg ); }
    100% { transform: rotateX( 360deg ); }  
  }
`
:root {
  --long: 6;
  --mid: calc( var( --long ) / 2 );
}
* {
  box-sizing: border-box; transform-style: preserve-3d;
  margin: 0rem; padding: 0rem;
}
html, body { height: 100%; }
body, section, div, span {
  display: flex;
  justify-content: center; align-items: center;
}
body {
  perspective: calc( var( --long ) * 3rem )
}
span:nth-of-type( 2 ) {
  transform: rotateY( 180deg ) rotateX( calc( 90deg ) );
}
span:nth-of-type( 2 ) hr {
  box-shadow: 0rem 0rem 0rem 0.25rem #000 inset;
}
section, hr {
  position: absolute;
  box-shadow: 0rem 0rem 0rem 0.25rem #444 inset;
  border-style: none;
  width: calc( var( --long ) * 1rem ); height: calc( var( --long ) * 1rem );
  background-color: rgba( 200, 200, 200, 0.9 );
}
section {
  box-shadow: 0rem 0rem 0rem 0rem transparent;
  background-color: transparent;
}
<style>
  hr {
    animation-name: brightness;
    animation-duration: 4s;
    animation-timing-function: ease-in-out;  
    animation-direction: alternate;
  }
  span:nth-of-type( 1 ) hr:nth-of-type( 1 ) {
    animation-delay: 2s;
  }
  span:nth-of-type( 1 ) hr:nth-of-type( 2 ) {
    animation-delay: 3s;
  }
  span:nth-of-type( 1 ) hr:nth-of-type( 3 ),
  span:nth-of-type( 2 ) hr:nth-of-type( 3 ) {   
    animation-delay: 2s;
  }
  span:nth-of-type( 2 ) hr:nth-of-type( 1 ), 
  span:nth-of-type( 2 ) hr:nth-of-type( 2 ) {
    animation-delay: 1s;
  }
  @keyframes brightness {
    0% { filter: brightness( 110% ); }
    50% { filter: brightness( 50% ); }
    100% { filter: brightness( 110% ); }    
  }
  hr{ filter: blur( 0.5rem ); }
</style>
<section class='rotate_x'>
  <div class='rotate_y'>
    <span> <hr><hr><hr> </span> <span> <hr><hr><hr> </span>
  </div>
</section>

In chrome this cube starts off blurred then quickly reverts back to it's appearance before blur; ignoring this line:

hr{ filter: blur( 0.5rem ); }

Here is a gif below that demonstrates what it looks like: enter image description here

The aim is to blur the faces of a 3D transformed element. How can I achieve this with Vanilla CSS?

I first attempted blurring the cube element as a whole which fully broke the illusion. Here I am trying to blur each hr or "face" individually. Still without the desired result


Solution

  • You should wrap the cube in another container that doesn't has any 3D transform applied to it, let's say .cube:

    <section class='cube'>
      <section class='rotate_x'>
        <div class='rotate_y'>
          <span> <hr><hr><hr> </span> <span> <hr><hr><hr> </span>
        </div>
      </section>
    </section>
    

    Then you can apply filter animation to it without any issue:

    Also you need to move perspective: calc( var( --long ) * 3rem ); from body to the .cube element itself.

    .cube{ 
      animation: focus 3s linear both alternate;
      perspective: calc( var( --long ) * 3rem );
    }
    @keyframes focus {
      from {
        filter: blur( 0.5rem );
      }
      
      to {
        filter: blur( 0rem );
      }
    }
    

    document.querySelector( 'style' ).innerHTML += `
      hr:nth-of-type( 1 ) {
        transform: translateZ( calc( var( --mid ) * 1rem ) );
      }
      hr:nth-of-type( 2 ) {
        transform: translateZ( calc( var( --mid ) * -1rem ) );
      }
      hr:nth-of-type( 3 ) {
        transform: rotateY( 90deg ) translateZ( calc( var( --mid ) * 1rem ) );
      }
      .rotate_y, .rotate_x, hr {
        animation-name: rotateY;
        animation-duration: 8s;
        animation-delay: 0s;
        animation-timing-function: linear;
        animation-iteration-count: infinite;
      }
      @keyframes rotateY {
        0% {
          transform: rotateY( 0deg );
        }
        100% {
          transform: rotateY( 360deg );
        }  
      }
      .rotate_x {
        animation-name: rotateX;
      }
      @keyframes rotateX {
        0% { transform: rotateX( 0deg ); }
        100% { transform: rotateX( 360deg ); }  
      }
    `
    :root {
      --long: 6;
      --mid: calc( var( --long ) / 2 );
    }
    * {
      box-sizing: border-box; transform-style: preserve-3d;
      margin: 0rem; padding: 0rem;
    }
    html, body { height: 100%; }
    body, section, div, span {
      display: flex;
      justify-content: center; align-items: center;
    }
    span:nth-of-type( 2 ) {
      transform: rotateY( 180deg ) rotateX( calc( 90deg ) );
    }
    span:nth-of-type( 2 ) hr {
      box-shadow: 0rem 0rem 0rem 0.25rem #000 inset;
    }
    section, hr {
      position: absolute;
      box-shadow: 0rem 0rem 0rem 0.25rem #444 inset;
      border-style: none;
      width: calc( var( --long ) * 1rem ); height: calc( var( --long ) * 1rem );
      background-color: rgba( 200, 200, 200, 0.9 );
    }
    section {
      box-shadow: 0rem 0rem 0rem 0rem transparent;
      background-color: transparent;
    }
    .cube{ 
      animation: focus 3s linear both alternate;
      perspective: calc( var( --long ) * 3rem );
    }
      
    @keyframes focus {
      from {
        filter: blur( 0.5rem );
      }
      
      to {
        filter: blur( 0rem );
      }
    }
    <style>
      hr {
        animation-name: brightness;
        animation-duration: 4s;
        animation-timing-function: ease-in-out;  
        animation-direction: alternate;
      }
      span:nth-of-type( 1 ) hr:nth-of-type( 1 ) {
        animation-delay: 2s;
      }
      span:nth-of-type( 1 ) hr:nth-of-type( 2 ) {
        animation-delay: 3s;
      }
      span:nth-of-type( 1 ) hr:nth-of-type( 3 ),
      span:nth-of-type( 2 ) hr:nth-of-type( 3 ) {   
        animation-delay: 2s;
      }
      span:nth-of-type( 2 ) hr:nth-of-type( 1 ), 
      span:nth-of-type( 2 ) hr:nth-of-type( 2 ) {
        animation-delay: 1s;
      }
      @keyframes brightness {
        0% { filter: brightness( 110% ); }
        50% { filter: brightness( 50% ); }
        100% { filter: brightness( 110% ); }    
      }
    </style>
    <section class='cube'>
      <section class='rotate_x'>
        <div class='rotate_y'>
          <span> <hr><hr><hr> </span> <span> <hr><hr><hr> </span>
        </div>
      </section>
    </section>