Search code examples
htmlcsssvgwebkit

Make a svg shape invert whats behind it


I am trying to create a SVG shape that inverts the color behind it. I played around with masking to get the SVG shape to the way I wanted it. The problem with my current code is that it only inverts the SVG rectangle I created inside the SVG element and I would like it to invert whatever is behind it. I tried to set the SVG rectangle inside the element to 0 opacity but that did not help. My current code looks like this:

<svg width="800px" height="600px">
    <defs>
        <filter id="invert">
            <feComponentTransfer>
                <feFuncR type="table" tableValues="1 0"/>
                <feFuncG type="table" tableValues="1 0"/>
                <feFuncB type="table" tableValues="1 0"/>
            </feComponentTransfer>
        </filter>
        <mask id="mask-me">
            <svg style="top: 50px" xmlns="http://www.w3.org/2000/svg" width="700" height="80" viewBox="0 0 700 80">
                <g id="Rectangle_1" data-name="Rectangle 1" fill="rgba(255,255,255,0)" stroke="#b2bdce" stroke-linecap="round" stroke-width="3">
                    <rect width="700" height="80" rx="40" stroke="none"></rect>
                    <rect x="1.5" y="1.5" width="697" height="77" rx="38.5" fill="none"></rect>
                </g>
            </svg>
        </mask>
    </defs>

    <!--the shape that gets inverted-->
    <svg xmlns="http://www.w3.org/2000/svg" width="700" height="80" viewBox="0 0 700 80">
        <rect id="Rectangle_5" data-name="Rectangle 5" width="700" height="80" fill="yellow"/>
    </svg>

    <!--the shape that inverts whats below it-->
    <svg style="filter: invert(1); mix-blend-mode: difference;" mask="url(#mask-me)" filter="url(#invert)" xmlns="http://www.w3.org/2000/svg" width="700" height="80" viewBox="0 0 700 80">
        <rect id="Rectangle_5" data-name="Rectangle 5" width="700" height="80" />
    </svg>
</svg>

here is what I want: enter image description here

Here is my codepen: https://codepen.io/Julius-olsson/pen/gOKKBKR

Any help would be appreciated.


Solution

  • I think I have understood what you are trying to do, but I am not completely sure.

    One problem you seem to have is that you are trying to use both an inverting filter and mix-blend-mode difference, which can both do similar things but here are almost certainly working against each other.

    The following code has been pared down to the barest minimum to provide an example of what I think you are trying to do:

    • It provides an image to be inverted, in this case just a rectangle, but it can be anything.
    • It provides an inverting image, that is anything overlapping the image will be inverted, again in this case a rectangle.
    • The inverting image is masked by another image, so only the parts of the inverting image that are in common with the mask actually cause inversion.
    • It does not use a filter. It uses mix-blend-mode difference with its colour set to white, that is, a difference from white is the opposite, it is inverted.

    I have offset the inverted image and the inverting image just for show, so that the boundary of the effect can be readily seen.

    .container {
        padding: 10px;
        border: 3px solid red;
        background-color: wheat;
    
        width: calc( 700px + 250px );
        height: calc( 80px + 80px - 40px );
    
        /*  Stop any bacground colouring affecting the inversion  */
        isolation: isolate;
    }
    
    .inverted {
        position: relative;
        left: 0px;
        top: 0px;
    
        fill: yellow;
    
        width: 700px;
        height: 80px;
    }
    
    .inverter {
        width: 700px;
        height: 80px;
    
        /*  Set the inversrion reference colour  */
        fill: white;
    }
    
    .masked {
        position: relative;
        left: 250px;
        top: -40px;
    
        /*  Set up the inversion  */
        mix-blend-mode: difference;
        /*  Inversion only where the mask allows  */
        -mask-image: url('data:image/svg+xml; utf8, <svg xmlns="http://www.w3.org/2000/svg" height="80px"  viewBox="0 0 80 80"><circle cx="40" cy="40" r="40" /></svg>');
        -webkit-mask-image: url('data:image/svg+xml; utf8, <svg xmlns="http://www.w3.org/2000/svg" height="80px"  viewBox="0 0 80 80"><circle cx="40" cy="40" r="40" /></svg>');
    }
    <div class="container">
        <!-- the shape that gets inverted -->
        <svg class="inverted" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 700 80">
            <rect width="700" height="80" />
        </svg>
        <!-- the shape that does the inverting -->
        <div class="masked">
            <svg class="inverter" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 700 80">
                <rect width="700" height="80" />
            </svg>
        </div>
    </div>

    The bottom half of the circles are black because there is nothing there to invert, the background has been isolated, so difference from white is black.

    [EDIT 22021204 Added example for sticky]

    The coding below shows an example of a sticky div which inverts material "behind" its background. It is using mix-mode-difference, so if it has a border, which by definition will be a different colour from the background (if it is to be discernible) the material "behind" the border is not actually inverted but is differenced relative to the colour of the border.

    An invert filter function applies to the selected element, not other material stacked with the element.

    body {
        font-size: 150%;
    }
    
    .container {
        width: 900px;
        height: 75vh;
        overflow-y: scroll;
        /*  background-color: beige;  */
        isolation: isolate;
    }
    
    .sticky {
        width: 700px;
        padding: 20px;
        text-align: center;
        position: sticky;
        top: 0px;
        left: 180px;
        border: 20px solid yellow;
        font-size: 300%;
        font-weight: bold;
        mix-blend-mode: difference;
        background-color: white;
    }
    
    svg,
    img {
        float: right;
        width: 300px;
    }
    
    .background {
        background-image: url('data:image/svg+xml; utf8, <svg xmlns="http://www.w3.org/2000/svg" height="80px"  viewBox="0 0 80 80"><circle cx="40" cy="40" r="40" fill="wheat"/></svg>');
    }
    <body>
    <div class="container">
        <h1>A heading</h1>
        <div class="textstuff">Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image. Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image. Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image.</div>
        <div class="sticky">This is some sticky text with a border.</div>
        <h1>A heading</h1>
        <div class="textstuff" style="color: red;">Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image. Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image. Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image.</div>
        <div class="textstuff" style="color: green;">Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image. Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image. Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image.</div>
        <svg xmlns='http://www.w3.org/2000/svg' viewbox='65.60520541730344 -3.8540656494687013 800.3826769425817 539.7901985072457'><path fill='grey' d='M 156.40357183517773 23.19316100057085 L 83.97002318399646 188.27914171909507 L 518.4511031561435 60.897074118366035 L 799.3826769425817 214.44658030407507 L 304.1247347870089 -2.8540656494687013 L 593.7387174567936 199.93582818685073 L 773.3354502925422 66.72541735224387 L 625.6142873407109 92.7726440022834 L 428.65273673826925 127.50227953566946 L 379.41234908765887 136.184688419016 L 446.0175545049623 225.98305483689026 L 448.871620154431 530.1077896238992 L 509.768694272797 11.65668646775564 L 373.58400585378104 391.06903555541453 L 602.4211263401401 249.17621583746111 L 182.45079848521726 170.91432395240204 L 616.9318784573643 43.53225635167299 L 165.08598071852424 72.43354865118125 L 312.80714367035546 46.3863220011417 L 225.86284290194985 417.1162622054541 L 399.63123250382057 538.7901985072457 L 66.60520541730344 89.79836641787429 Z'/></svg>
        <div class="textstuff" style="color: brown;">Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image. Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image. Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image.</div>
        <h1>A heading</h1>
        <div class="textstuff" style="color: red;">Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image. Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image. Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image.</div>
        <div class="textstuff" style="color: green;">Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image. Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image. Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image.</div>
        <img src='data:image/svg+xml; utf8, <svg xmlns="http://www.w3.org/2000/svg" height="80px" viewBox="0 0 160 160"><circle fill="pink" stroke="lime" stroke-width="10" cx="80" cy="80" r="70" /></svg>' />
        <div class="textstuff" style="color: brown;">Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image. Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image. Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image.</div>
        <h1>A heading</h1>
        <div class="textstuff" style="color: red;">Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image. Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image. Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image.</div>
        <div class="textstuff" style="color: green;">Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image. Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image. Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image.</div>
        <div class="textstuff" style="color: brown;">Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image. Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image. Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image.</div>
        <h1>A heading</h1>
        <div class="background">
        <div class="textstuff" style="color: red;">Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image. Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image. Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image.</div>
        <div class="textstuff" style="color: green;">Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image. Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image. Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image.</div>
        <div class="textstuff" style="color: brown;">Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image. Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image. Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image.</div>
        </div>
        <h1>A heading</h1>
        <div class="textstuff" style="color: red;">Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image. Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image. Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image.</div>
        <div class="textstuff" style="color: green;">Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image. Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image. Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image.</div>
        <div class="textstuff" style="color: brown;">Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image. Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image. Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image.</div>
        <h1>A heading</h1>
        <div class="textstuff" style="color: red;">Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image. Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image. Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image.</div>
        <div class="textstuff" style="color: green;">Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image. Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image. Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image.</div>
        <div class="textstuff" style="color: brown;">Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image. Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image. Some scrollable text spread across the page to the other side of the page, wrapped around a right floated image.</div>
    </div>
    </body>

    It will probably be informative to try different combinations of background colours and fill colours for various elements depending on how you want it to look in the end.