I am currently using 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>