user5182794
user5182794

Reputation:

CSS Transition with filter breaks clipping path

Browser: Chrome 60

**TLDR: ** It seems that using a transition with a filter on a child of a div with a clipping path applied causes strange behavior (take a look at the live code example and the pictures). However, this only happens when the background child has a blur filter on it too.

I am trying to create a scroll triggered animation using jQuery and CSS transitions. I have two divs on the page: a fixed div on the left side with a slanted edge (made using clipping path), and a right div containing content:

Working clipping path

When the page detects that the user scrolls past a certain point in the page, it will add a class to the text in the left div, making it blur out. This all works fine, until I add transition: filter 0.5s; to the text - this causes the clipping path to break (randomly jumps up by around 100px) while the transition is occuring:

Broken clipping path

Take a look at a live example (code included):

https://codepen.io/sunnylan/full/eEWKYX

I have tried to isolate where the problem comes from, but I am having trouble, since it seems as if a combination of multiple elements are causing the problem. For example, the problem disappears when I remove the clipping path. It also goes away when I remove the transitions from the navigation list items. More specifically, here are the problem areas (removing any of them will fix the problem):

Adding a clipping path to the sidebar

clip-path: polygon( // line 191
              0 0,
              100% 0,
              100% - $sidebar-angle 100%,
              0 100%); 

Adding a blur filter to the sidebar background:

filter: blur(20px); //line 201

Adding a transition to the nav items:

transition: filter $animation-speed ease, opacity $animation-speed ease; // line 222

Upvotes: 2

Views: 1865

Answers (1)

helllomatt
helllomatt

Reputation: 1339

That's a tricky one. Looks like it's something specific to Chrome, seeing as FF works OK minus a few oddities.

It looks like the specific cause to the problem is animating the nav-bar when the selected class is applied to it. If you don't do that then the clip-path animates correctly, but then there's no opacity/blur animation. You can see that when the selected class is applied, the clip-path messes up for the duration of the transition, and then goes the way you want when it's done. I'm guessing this is rendering from the innermost child outwards.

The fix, is to separate the two, instead of nesting them.

  $(window).scroll(function () {
    var s = $(window).scrollTop(),
        d = $(document).height(),
        c = $(window).height();
    var percent = s.map(0, d - c, 0, 100);
    if (percent > 15) {
      $('.nav-bar-wrapper .nav-bar').css('top', s.map(0, d - c, 10, -10) + '%');
      $('.sidebar').addClass('closed');
      $('.nav-bar-wrapper').addClass('closed');
      $('.nav-item').addClass('selected');
    } else {
      $('.nav-bar-wrapper .nav-bar').css('top', '40%');
      $('.sidebar').removeClass('closed');
      $('.nav-bar-wrapper').removeClass('closed');
      $('.nav-item').removeClass('selected');
    }
  });
.sidebar {
  background: black;

  //fix on left side of page
  position: fixed;
  left: 0;
  top: 0;
  bottom: 0;

  //initial width
  width: $sidebar-open;

  //make slanted edge
  clip-path: polygon(
                  0 0,
                  100% 0,
                  100% - $sidebar-angle 100%,
                  0 100%);

  //background picture layer
  .background {
    @include fill-parent-bg;
    background-image: url('/img/cb-colour-nightsky.jpg');
    filter: blur(20px);
    background-size: cover;
    z-index: -1;
  }

  //center sliding nav bar
  @include flex;
  justify-content: center;

  //animate it!
  transition: width $animation-speed ease;

  &.closed {
    width: $sidebar-closed;
  }
}

//moving component
.nav-bar-wrapper {
  top: 0;
  left: 0;
  bottom: 0;
  position: fixed;
  width: $sidebar-open;
  transition: width $animation-speed ease;
  
  &.closed {
    width: $sidebar-closed;
  }
  
  .nav-bar {
    background: gray;
    //keep component where it is, unless modified by jQuery
    position: absolute;
    top: 40%; //default vertical position
    left: 50%;
    transform: translateX(-50%);

    //animate it!
    transition: top $animation-speed ease;

    .nav-item {
      //fade in/out animations
      transition: filter $animation-speed ease, opacity $animation-speed ease;
      filter: blur(15px);
      opacity: 0.5;
      &.selected {
        opacity: 1;
        filter: blur(0);
      }
    }
  }
}
<div class="sidebar background"></div>
<div class="nav-bar-wrapper">
  <div class="nav-bar">
    <h1 class="nav-item" style="color:white; height: 100px;">Test content</h1>
  </div>
</div>

Here's your CodePen modified: https://codepen.io/anon/pen/RZVBNJ

With this, though, it looks like for a moment the text gets rendered twice, even though it's only on there once. Again, this is a Chrome-specific issue.

Upvotes: 0

Related Questions