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

What could be causing the PAGE CSS to malfunction?

I am looking to export HTML text as a word document with A4 size and portrait orientation. My JavaScript currently allows me to export the text, but it appears like a webpage format rather than in A4 or portrait mode. I have tried adding @page CSS styling ...

Displaying limited items based on conditions in CSS

If there are 10 <p> tags, is it possible to limit them to only 5 by hiding the other 5 using CSS? Do I need to add a class five times with nth-child? Can CSS accommodate conditions for this purpose? Considering that the number of <p> tags is ...

The use of backdrop-filter results in artifacts appearing in rounded corners

I am experiencing an issue with a CSS animation where I have an element that fades and child elements with a backdrop-filter to blur the background in Safari. There are two specific problems: The element's rounded borders are showing dark shadow ...

Loading message displayed in web browsers by banner rotation system

I'm currently working on a banner rotator function that seems to be showing a "loading" message continuously while running. Here is the code snippet for reference: var banners = ['../Graphics/adv/1.gif','../Graphics/adv/2.jpg']; / ...

Discover the name of a color using its HEX code or RGB values

Is there a way to retrieve the color name from RBG or HEX code using JavaScript or JQuery? Take for instance: Color Name RGB black #000000 white #FFFFFF red #FF0000 green #008000 ...

On mobile devices, a height of 100vh exceeds the visible screen area

Struggling to cover the entire visible part of the browser window using 100vh due to issues on certain devices and mobile browsers. For example, in Safari, the URL section hides a top part, and similarly on some Android devices using Chrome. https://i.stac ...

Ensuring Navigation Elements Remain Aligned in a Single Line

I'm in the process of developing an interactive project that will be showcased on a touch screen. One of its key features is a fixed footer navigation. Currently, I am using floats to position the main navigation elements and within each element, ther ...

Which takes longer to render: individually drawn images placed via absolute positioning, or one complete image?

I currently have a collection of "cards" on my website that are jpg images, but I am considering switching to an HTML/CSS solution. This change would involve placing numerous small icons on a background and adding stylish text on top. My concern is determ ...

Can Angular Material Tabs be customized to have a different style?

I need help styling my mat-tabs to achieve a specific look. Here is the design I am trying to replicate: https://i.stack.imgur.com/tg6XC.png https://i.stack.imgur.com/tth0z.png However, I'm encountering an issue where the white border under the curr ...

Conditional Statements in jQuery

Trying to implement a check for the CSS 'display' property being set to "none", then slideDown the element "menuBK". If not, slideUp "menuBK" but encountering an error in my IF statement. $(document).ready(function(){ $("#burger").click(func ...

The Image Slider functions smoothly in Chrome, but encounters issues in IE11

Here is a link to the code I've been working on: http://jsfiddle.net/wf32jbhx/ I attempted to host images on my SharePoint site, but they ended up stacking on top of each other instead of formatting properly. Oddly enough, everything appears fine in ...

What is the best way to align HTML elements in a single row?

I have the following code snippet... <div class="header"> <div class="mainh"> <div class="table"> <ul> <li><a>smth</a></li> ...

Notification with jQuery

I am looking to display an alert message when val = -1. However, the issue I am facing is that this message always appears at the bottom of the page regardless of the value of val. if ($val == -1) echo ' <script> $( ...

Exploring the differences between pseudo-elements when styled as display:block versus display:inline

I'm currently honing my CSS skills by following a Youtube tutorial from freecodecamp.org Here's the code I've been working on: p::before{ content: "hello "; color: white; font-size: 60px; background: salmon; margin-bottom: 20px ...

Accessing .js and .css libraries from shared views in ASP.NET Core can be restricted

I am currently developing a simple application on ASP.NET core 3.1, and I'm facing a basic problem that I can't seem to solve. The menu of the application is located in a Shared view called _Layout.cshtml. I load .js and .css libraries in this v ...

Determine the dimensions of a div element in IE after adjusting its height to auto

I am currently working on a JavaScript function that modifies the size of certain content. In order to accomplish this, I need to obtain the height of a specific div element within my content structure. Below is an example of the HTML code I am dealing wit ...

Manipulate sibling elements using jQuery's addClass and remove methods

I have implemented a function that adds the class active to elements when they reach the top of the browser, ensuring that the next element will scroll in over the top. While this works smoothly in Chrome, I am encountering some jumping behavior when testi ...

Is there a way to modify the hover border effect of a TextField in material-ui?

In Persian language, the text direction is set to rtl. I am looking for a way to adjust this input field to suit my language for the project. Please refer to the following image: Image Link I want to achieve something similar, but I am unsure how to ch ...

What are the steps to create a scrolling effect for the menu buttons on the mobile version of

When accessing the mobile version of Facebook on a handheld device, you will notice a menu with various options like "About," "Photos," "Friends," "Subscriptions," "Map," and more. This menu is designed to be slideable on your mobile device. Using HTML, ...

The positioning of Material UI InputAdornment icons is located beyond the boundaries of the TextField input area

I am struggling to understand why my InputAdornment is not positioned correctly. There doesn't seem to be any style in my code that would affect the location of the icon within the TextField (such as padding or flex properties). Currently, the calen ...