Search code examples
cssclip-path

Background positioned at right with clip-path: path moves to right when animating width from 100% to 0


In my HTML document I have an absolutely positioned element with background color and clip path on its ::before pseudo-element. This element works as a shadow to elements behind it. I want to remove this shadow by animating its width from 100% to 0. As I want the animation to move rightwards, I have positioned the element at right, with right: 0.

The problem is that when I set the element's width to 0 the clipped path seems to move rightwards.

Better have a look at the example because I find it hard to explain.

The example on the left is what I got now and where I have the problem. On hovering the rectangle the animation is triggered and the clip path should stay still, but it moves to the right of the element. It looks as if the path is always positioned on the left of the element and so by moving the left side to the right it just pushes the path rightwards. But what I want is the left side to move to the right while the rest of the background stays in place, disappearing (starting from the left) as the left side advances.

The example on the right is what I'm looking for but from right to left. I want the same effect from left to right.

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title></title>
        <style type="text/css">
    </head>
    <body>
        <div id="test-1" class="test"></div>
        <div id="test-2" class="test"></div>
        <div id="svg-container">
        <svg width="200" height="81" viewBox="0 0 200 81" xmlns="http://www.w3.org/2000/svg">
            <defs>
                <path id="test-path" d="M144.5 16L181 1.00195H5L20.5 7.00022L23 19.5002L17.5 31.5L1 24.5V76.0007C1 80.0007 5 80.002 5 80.002H195C195 80.002 199 80.0007 199 76.0007L199 21.5019L189 38.5L168.5 43.5L150 31.5L144.5 16Z" stroke="#36B5A8" stroke-width="2" stroke-linejoin="round"/>
                <clipPath id="test-clip">
                    <use xlink:href="#test-path" />
                </clipPath>
            </defs>
        </svg>
    </body>
</html>
body {
    margin: 0;
    position: relative;
    min-height: 100vh;
}
.test {
  width: 200px;
  height: 80px;
    position: absolute;
    top: 100px;
}
.test:before {
    content: '';
    position: absolute;
  top: 0;
  height: 100%;
  width: 100%;
  transition: width 5s linear;
  background-color: rgba(236,71,228, 0.7);
  clip-path: url(#test-clip);
}
.test:hover:before {
    width: 0;
}
#test-1 {
  left: 50px;
}
#test-1:before {
  right: 0;
}
#test-2 {
  left: 300px;
}
#test-2:before {
  left: 0;
}

I've tried to change the clip path clipPathUnits property and the background-position property, but no success.

I haven't found any question here or elsewhere in the net that solves (or explains) my problem.

I'm thinking this may have something to do with how the path is drawn, where it starts.

Any help will be appreciated.


Solution

  • You could consider applying the clip-path to the #test-1 element instead of its ::before pseudo-element:

    body {
        margin: 0;
        position: relative;
        min-height: 100vh;
    }
    .test {
      width: 200px;
      height: 80px;
        position: absolute;
        top: 100px;
    }
    .test:before {
        content: '';
        position: absolute;
      top: 0;
      height: 100%;
      width: 100%;
      transition: width 5s linear;
      background-color: rgba(236,71,228, 0.7);
    }
    .test:hover:before {
        width: 0;
    }
    #test-1 {
      left: 50px;
      clip-path: url(#test-clip);
    }
    #test-1:before {
      right: 0;
    }
    <div id="test-1" class="test"></div>
    <div id="svg-container">
    <svg width="200" height="81" viewBox="0 0 200 81" xmlns="http://www.w3.org/2000/svg">
        <defs>
            <path id="test-path" d="M144.5 16L181 1.00195H5L20.5 7.00022L23 19.5002L17.5 31.5L1 24.5V76.0007C1 80.0007 5 80.002 5 80.002H195C195 80.002 199 80.0007 199 76.0007L199 21.5019L189 38.5L168.5 43.5L150 31.5L144.5 16Z" stroke="#36B5A8" stroke-width="2" stroke-linejoin="round"/>
            <clipPath id="test-clip">
                <use xlink:href="#test-path" />
            </clipPath>
        </defs>
    </svg>

    Otherwise, if the clip-path must be on the ::before pseudo-element, you could draw the animation via a background gradient instead, animating its background-size to achieve the same effect visually:

    body {
        margin: 0;
        position: relative;
        min-height: 100vh;
    }
    .test {
      width: 200px;
      height: 80px;
        position: absolute;
        top: 100px;
    }
    .test:before {
        content: '';
        position: absolute;
      top: 0;
      height: 100%;
      width: 100%;
      transition: background-size 5s linear;
      background: linear-gradient(0deg, rgba(236,71,228, 0.7), rgba(236,71,228, 0.7)) no-repeat right / 100% 100%;
      clip-path: url(#test-clip);
    }
    .test:hover:before {
        background-size: 0% 100%;
    }
    #test-1 {
      left: 50px;
    }
    #test-1:before {
      right: 0;
    }
    <div id="test-1" class="test"></div>
    <div id="svg-container">
    <svg width="200" height="81" viewBox="0 0 200 81" xmlns="http://www.w3.org/2000/svg">
        <defs>
            <path id="test-path" d="M144.5 16L181 1.00195H5L20.5 7.00022L23 19.5002L17.5 31.5L1 24.5V76.0007C1 80.0007 5 80.002 5 80.002H195C195 80.002 199 80.0007 199 76.0007L199 21.5019L189 38.5L168.5 43.5L150 31.5L144.5 16Z" stroke="#36B5A8" stroke-width="2" stroke-linejoin="round"/>
            <clipPath id="test-clip">
                <use xlink:href="#test-path" />
            </clipPath>
        </defs>
    </svg>