从剪辑迁移到剪辑路径以使用百分比值

Migrating from clip to clip-path to use percent values

我目前正在使用 scss 为“移动”边框编写的动画。动画主要以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));
        }
    }

起初动画只适用于方形块 (w === h),然后我调整它以便可以单独指定 withheight.

然后需要再次调整脚本,使其接受 percent 值,而不仅仅是 pixels。我做了一些研究,发现了这篇文章 Clipping and Masking in CSS。从文章看来:

我进行了更改,似乎以下应该有效:

$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));
        }
    }

但是,此更改后动画不起作用。我试着玩这个但没有很好的结果,似乎新的 api 也改变了它的工作方式。如何修改 keyframes 以便它们使用 % 值?

完整 scss 代码:

    $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;
    }

工作(编译为 css)演示:

.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>

我会为此使用面具

.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>

并且使用 clip-path 你可以像下面那样做:

.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>