Transitioning from clip to clip-path in order to utilize percentage values

My current approach involves using a scss-based animation to create border animations with movement. The animation's core functionality relies on the use of clip.

$box-width: 80px;
$box-height: 50px;

@keyframes clipMe {
    0%, 100% {
        clip: rect(0, $box-width, $path-width, 0);
    }
    25% {
        clip: rect(0, $path-width, $box-height, 0);
    }
    50% {
        clip: rect(resolveCalc($box-height, $path-width), $box-width, $box-height, 0);
    }
    75% {
        clip: rect(0, $box-width, $box-height, resolveCalc($box-width, $path-width));
    }
}

Initially, the animation was tailored for square blocks where width matched height. However, adjustments were made to allow distinct specifications for width and height.

Later on, there was a requirement to adapt the script to accommodate percentage values alongside pixels. After some exploration, I stumbled upon an enlightening article on Clipping and Masking in CSS. This resource recommended these syntax changes:

  • Previous syntax:
    clip: rect(10px, 20px, 30px, 40px);
  • New syntax:
    clip-path: inset(10px 20px 30px 40px);

Applying these insights, the tweaks below should make the animation operational:

$box-width: 100%;
$box-height: 100%;

@keyframes clipMe {
    0%, 100% {
        clip-path: inset(0 $box-width $path-width 0);
    }
    25% {
        clip-path: inset(0 $path-width $box-height 0);
    }
    50% {
        clip-path: inset(resolveCalc($box-height, $path-width) $box-width $box-height 0);
    }
    75% {
        clip-path: inset(0 $box-width $box-height resolveCalc($box-width, $path-width));
    }
}

However, post these alterations, the animation ceases to function. Despite various attempts at tweaking, it appears that the new API adjustment has altered its behavior. How can the keyframes be modified to accommodate percentage values?

Full scss code:

$anime-duration: 8s;

// Remaining SCSS code here...
    </div></questionbody>
<exquestionbody>
<div class="question">
                
<p>I am currently using <code>scss written animation for "moving" borders. Animation is mainly based on clip.

$box-size-w: 80px;
$box-size-h: 50px;

    @keyframes clipMe {
        0%, 100% {
            clip: rect(0, $box-size-w, $path-width, 0);
        }
        25% {
            clip: rect(0, $path-width, $box-size-h, 0);
        }
        50% {
            clip: rect(resolveCalc($box-size-h, $path-width), $box-size-w, $box-size-h, 0);
        }
        75% {
            clip: rect(0, $box-size-w, $box-size-h, resolveCalc($box-size-w, $path-width));
        }
    }

At first animation was only going to work for square shaped blocks (w === h), then i adjusted it so it could be specified separate with and height.

Then the need came to again adapt the script so it accepts percent values, not only pixels. I did some research and i found this article Clipping and Masking in CSS. From the article it seems that:

  • old syntax:
    clip: rect(10px, 20px, 30px, 40px);
  • new syntax:
    clip-path: inset(10px 20px 30px 40px);

I made changes and it seems following should work:

$box-size-w: 100%;
$box-size-h: 100%;

    @keyframes clipMe {
        0%, 100% {
            clip-path: inset(0 $box-size-w $path-width 0);
        }
        25% {
            clip-path: inset(0 $path-width $box-size-h 0);
        }
        50% {
            clip-path: inset(resolveCalc($box-size-h, $path-width) $box-size-w $box-size-h 0);
        }
        75% {
            clip-path: inset(0 $box-size-w $box-size-h resolveCalc($box-size-w, $path-width));
        }
    }

However animation doesnt work after this change. I tried to play with this but with not great result, it seems like new api also changed the way it works. How the keyframes can be modified so they work with % values?

Full scss code:

    $anime-time: 8s;

    $box-size-w: 80px;
    $box-size-h: 50px;
    $path-width: 1px;

    $main-color: #000;

    @function resolveCalc($s, $f) {
        @return calc(#{$s} - #{$f});
    }

    %full-fill {
        position: absolute;
        top: 0;
        bottom: 0;
        left: 0;
        right: 0;
    }

    .box {
        width: $box-size-w;
        height: $box-size-h;
        margin: auto;
        color: $main-color;
        box-shadow: inset 0 0 0 1px rgba($main-color, .1);
        position:relative;

        &::before,
        &::after {
            @extend %full-fill;
            content: '';
            z-index: -1;
            margin:0;
            box-shadow: inset 0 0 0 $path-width;
        }

        &.active {
            &::before {
                animation: clipMe $anime-time linear infinite;
                animation-delay: $anime-time * -.5;
            }
            &::after {
                animation: clipMe $anime-time linear infinite;
            }

            // for debug
            &:hover {
                &::after,
                &::before {
                    background-color: rgba(#fff, .3);
                }
            }
        }

    }

    @keyframes clipMe {
        0%, 100% {
            clip: rect(0, $box-size-w, $path-width, 0);
        }
        25% {
            clip: rect(0, $path-width, $box-size-h, 0);
        }
        50% {
            clip: rect(resolveCalc($box-size-h, $path-width), $box-size-w, $box-size-h, 0);
        }
        75% {
            clip: rect(0, $box-size-w, $box-size-h, resolveCalc($box-size-w, $path-width));
        }
    }

    html,
    body {
        height: 100%;
    }

    body {
        position: relative;
    }

    .wrap {
        width:50%;
        height:25%;
        margin:50px auto;
    }

    *,
    *::before,
    *::after {
        box-sizing: border-box;
    }

Working (compiled to css) demo:

.box::before, .box::after {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
}

.box {
  width: 80px;
  height: 50px;
  margin: auto;
  color: #000;
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1);
  position: relative;
}

.box::before, .box::after {
  content: '';
  z-index: -1;
  margin: 0;
  box-shadow: inset 0 0 0 1px;
}

.box.active::before {
  animation: clipMe 8s linear infinite;
  animation-delay: -4s;
}

.box.active::after {
  animation: clipMe 8s linear infinite;
}

.box.active:hover::after, .box.active:hover::before {
  background-color: rgba(255, 255, 255, 0.3);
}

@keyframes clipMe {
  0%, 100% {
    clip: rect(0, 80px, 1px, 0);
  }
  25% {
    clip: rect(0, 1px, 50px, 0);
  }
  50% {
    clip: rect(calc(50px - 1px), 80px, 50px, 0);
  }
  75% {
    clip: rect(0, 80px, 50px, calc(80px - 1px));
  }
}

html,
body {
  height: 100%;
}

body {
  position: relative;
}

.wrap {
  width: 50%;
  height: 25%;
  margin: 10px auto;
}

*,
*::before,
*::after {
  box-sizing: border-box;
}
<div class="wrap">
    <div class="box active"></div>
</div>

Answer №1

One possible solution would be to use a mask in this scenario

.box {
  width: 40%;
  height: 100px;
  margin: 10px auto;
  color: #000;
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1);
  position: relative;
}

.box::before, .box::after {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: -1;
  box-shadow: inset 0 0 0 1px;
  -webkit-mask:linear-gradient(#fff 0 0) no-repeat;
  animation: 
    clipMe 2s linear infinite alternate,
    pos    8s linear infinite;
}

.box::before {
  animation-delay: -4s;
}


@keyframes clipMe {
  0% {
    -webkit-mask-size:100% 2px;
  }
  100% {
    -webkit-mask-size:2px 100%;
  }
}

@keyframes pos {
  0%,24.9% {
    -webkit-mask-position:top left;
  }
  25%,49.9% {
    -webkit-mask-position:bottom left;
  }
  50%,74.9% {
    -webkit-mask-position:bottom right;
  }
  75%,100% {
    -webkit-mask-position:top right;
  }
}
<div class="box"></div>

<div class="box" style="width:50%;height:150px;"></div>

Another approach would be using clip-path as shown below:

.box {
  width: 40%;
  height: 100px;
  margin: 10px auto;
  color: #000;
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1);
  position: relative;
}

.box::before,
.box::after {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: -1;
  box-shadow: inset 0 0 0 1px;
  animation: clipMe 4s linear infinite;
}

.box::before {
  animation-delay: -2s;
}

@keyframes clipMe {
  0%, 100% {
    clip-path: inset(0 0 calc(100% - 1px) 0);
  }
  25% {
    clip-path: inset(0 calc(100% - 1px) 0 0);
  }
  50% {
    clip-path: inset(calc(100% - 1px) 0 0 0);
  }
  75% {
    clip-path: inset(0 0 0 calc(100% - 1px));
  }
}
<div class="box"></div>

<div class="box" style="width:50%;height:150px;"></div>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Tips for utilizing innerHeight for a div during both window loading and resizing tasks?

I'm currently working on calculating the top/bottom padding of a div (.content) based on its height, and updating it upon loading and resizing the window. The goal is to have it centered nicely next to another div (.character) positioned beside it. I ...

Deactivated dropdown menu options with the help of Twitter's bootstrap framework

Using markup, I have created a dropdown menu utilizing Twitter Bootstrap. <ul class="nav pull-right"> <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown">Menu <b class="caret"></b>< ...

How to Keep Bootstrap 3 Accordion Open Even When Collapsed

I am in the process of building an accordion group with bootstrap 3. Here is the code I have so far: <div id="accordion" class="panel-group"> <div class="panel panel-default"> <div class="panel-heading"> <div class ...

Issue with CSS on various mobile devices

Using this CSS to display a fixed part at the bottom of the mobile screen, I noticed that it works well on the Samsung Grand Prime but appears narrower on the Samsung J7 Max. Here is the code snippet and images for reference: .footer { position: fixed ...

I recently attempted a meyers reset and now I'm struggling to get my unordered list to display in a horizontal layout

Check out my code snippet below: .navbar-container { width:100%; display:inline-block; } ul { list-style-type: none; display: inline; } <div class="navbar-container"> <ul> <li><a href="gmail.com">Gmail</a>& ...

Troubleshooting a CSS problem within mat-autocomplete component in Angular 7

While using mat-autocomplete, I noticed that when I select an option from the dropdown and then scroll through the main bar, the dropdown menu does not move along with the autocomplete input field. view image here Here is the code snippet: <td width ...

Ways to adjust the width of grid columns within an implicit row

I am curious about changing grid columns on an implicit row. While I won't provide the exact code that I'm working on, I will offer a brief example to clarify my question. <div class='container'> <p>item-1</p> <p& ...

Maintaining the proportions of images in different screen sizes when resizing

I apologize if this question has already been addressed, but I have been unable to find a solution that works for my specific issue. My Gallery consists of a side list of available images in one section, which when clicked changes the image source in anot ...

What is the best way to merge two AngularJS form validation directives criteria using CSS?

When both $dirty and $invalid are true, I would like the input fields to have a red background. However, I am only able to do this one by one, which is not ideal. input.ng-dirty && input.ng-invalid { background-color: red; } ...

hierarchical browsing system

Take a look at the image provided below. Currently, I am using multiple unordered lists - specifically 5. However, I would prefer to consolidate them all into a single nested ul. I am encountering two issues: How can I add a border-bottom to the hori ...

The dimensions of the div element are adjusting as I scroll on Firefox Mobile, however, there is a way to prevent this from happening using Angular

I've been conducting tests on my Angular application on various mobile devices. While everything seems to function properly on Chrome and Safari, an issue arises when it comes to Firefox: during scrolling, the browser's toolbar and URL bar appear ...

Is there a way to create a blurred background effect while the popup is in use?

I prefer my popup to have a dark or blurred background when active. The popup itself should remain the same, with only the background being darkened. I would like a dark layer overlaying the entire page. This script should be compatible with any website wh ...

Is there a way for me to remove an uploaded image from the system

Here is an example of my HTML code: <input type='file' multiple/> <?php for($i=0;$i<5; $i++) { ?> <div class="img-container" id="box<?php echo $i ?>"> <button style="display: none;" type="submit" cl ...

What is the best way to position the left sidebar on top of the other components and shift them to the

Currently, I am working on a project to display NBA data fetched from an API. I am aiming to recreate the design showcased here: Dribbble Design My main challenge lies in overlaying the left sidebar onto the main box and shifting the components sligh ...

SASS : Loop not producing the most efficient CSS output

Is there a way to optimize CSS generation using a @for loop in SASS? An example speaks louder than words: Here is my SASS code: @for $i from 1 through 2 { .table-lg-#{$i}, .table-md-#{$i}, .table-sm-#{$i}, .table-xs-#{$i} { background: red; ...

Resizing the Bootstrap carousel

My website has a custom-sized image that I want to display using Bootstrap's carousel, but I'm struggling with styling the carousel. Here is the result: https://i.sstatic.net/bs9HG.png I'm trying to align the picture in the center. Th ...

Unusual CSS rendering hiccup

Using jQuery, I am manipulating the display of an <a> element. Depending on certain keypress events, it adds or removes a class from an <input> element (which controls the display) that is related as a sibling to the mentioned <a>. The i ...

Adjust the top spacing in relation to the paragraph tag using jQuery

I wish to create multiple div elements with nested p tags that are initially hidden. Upon hovering over each div, I want the corresponding p tag to move up by the negative value of its own height. For instance: The first p tag has a height of 60px and a m ...

Adjusting height dynamically with CSS based on font size

Below is a link followed by a span element <a href="#" style="display:inline-block;font-size:900%">12</a> <span display="block">ACTIVE</span> The issue arises when the 'a' element does not have a specific height, causing ...

Locator for finding compound text within a div class using Selenium WebDriver

I am struggling to select a specific button in the UI that shares similarities with other elements. Below is the code snippet for the button in question: <div class="ui green ok inverted button"> <i class="checkmark icon"></i> Yes </d ...