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 onclip
.$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
andheight
.Then the need came to again adapt the script so it accepts
percent
values, not onlypixels
. 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>