CSS DIV 上的动画渐变边框

CSS animated gradient border on a DIV

我正在尝试创建一个加载 DIV,它的边框看起来像一个不确定的进度环微调器。

根据 https://css-tricks.com/gradient-borders-in-css/

上的示例之一,我非常接近

当边框不旋转时这很好。当您在 :before 元素中设置 border 以匹配 gradient-box 元素中的透明 border 时,静态渐变边框看起来很完美。

但是,添加动画后,由于整个 :before 元素旋转,您会得到非常奇怪的效果 - 如下例所示。

.gradient-box {
  
  display: flex;
  align-items: center;
  width: 90%;
  margin: auto;
  max-width: 22em;

  position: relative;
  padding: 30% 2em;
  box-sizing: border-box;

  border: 5px solid blue;
  color: #FFF;
  background: #000;
  background-clip: padding-box; /* !importanté */
  border: solid 5px transparent; /* !importanté */
  border-radius: 1em;

}

.gradient-box:before {
    content: '';
    position: absolute;
    top: 0; right: 0; bottom: 0; left: 0;
    z-index: -1;
    margin: -35px; /* !importanté */
    border-radius: inherit; /* !importanté */
    background: conic-gradient(#0000ff00, #ff0000ff);
    -webkit-animation: rotate-border 5s linear infinite;
    -moz-animation: rotate-border 5s linear infinite;
    -o-animation: rotate-border 5s linear infinite;
    animation: rotate-border 3s linear infinite;
}

@keyframes rotate-border {
    to {
        transform: rotate(360deg);
    }
}

html { height: 100%; background: #000; display: flex; }
body { margin: auto; }
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Loading DIV Test</title>
</head>
<body>

<div id="loadingBox" class="gradient-box">
<p>Loading.</p>
</div>

</body>

我试过 overflow: hidden;但是边框就消失了.. 有什么办法可以 'mask' :before 元素以某种方式使此加载 Div 后面的任何内容在其后面仍然可见,从而使边框保持其预期宽度?

基本上,我的目标是 border 中的颜色渐变旋转以产生 spinning/rotating 边缘的效果。

嗨,这是你要找的吗?

我所做的是我添加了一个新的 div 这将是“掩码”以及用于掩码和 loadingBox 的容器 div。

然后我将蒙版的大小设置为比您的可见区域大一点,使其成为透明背景,然后给它一个与背景颜色相同的大轮廓以有效地遮盖边框。然后我摆弄了蒙版、加载框和之前的 z-index。我还在 mask 上添加了一些实际边框以将其框出一个漂亮的形状。

看看:

.gradient-box {
  
  display: flex;
  align-items: center;
  width: 90%;
  margin: auto;
  max-width: 22em;

  position: relative;
  padding: 30% 2em;
  box-sizing: border-box;

  border: 5px solid blue;
  color: #FFF;
  background: #000;
  background-clip: padding-box; /* !importanté */
  border: solid 5px transparent; /* !importanté */
  border-radius: 1em;

}

.gradient-box:before {
    content: '';
    position: absolute;
    top: 0; right: 0; bottom: 0; left: 0;
    z-index: -3;
    margin: -35px; /* !importanté */
    border-radius: inherit; /* !importanté */
    background: conic-gradient(#0000ff00, #ff0000ff);
    -webkit-animation: rotate-border 5s linear infinite;
    -moz-animation: rotate-border 5s linear infinite;
    -o-animation: rotate-border 5s linear infinite;
    animation: rotate-border 3s linear infinite;
}

@keyframes rotate-border {
    to {
        transform: rotate(360deg);
    }
}

html { height: 100%; background: #000; display: flex; }
body { margin: auto; }

.mask {
  position: absolute;
   box-sizing: border-box;
background-color: transparent; 
 outline: 65px solid black;
height: 100%;
  width: 100%;
  border: 2px solid black;
  border-left: 7px solid black;
  border-right: 7px solid black;
  z-index: -1;
}

.container {
  position: relative;
}
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Loading DIV Test</title>
</head>
<body>
<div class="container">
<div class="mask"></div>
<div id="loadingBox" class="gradient-box">
<p>Loading.</p>
</div>
  </div>
</body>

我喜欢你最初使用 overflow: hidden 的想法,但为了让它发挥作用,我必须包含一个额外的包装器 div。

  • 外包装定义了一个padding作为渐变边框的显示区域
  • 里面的div就是黑色背景的内容框

.loading-box-container {
  --size: 200px;
  --radius: 10px;
  position: relative;
  width: var(--size);
  height: var(--size);
  padding: var(--radius);
  border-radius: var(--radius);
  overflow: hidden;
}

.loading-box {
  position: relative;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  background: #000;
  border-radius: var(--radius);
}

.loading-box-container::before {
  content: '';
  width: 150%; /* The upscaling allows the box to fill its container even when rotated */
  height: 150%;
  position: absolute;
  top: -25%; left: -25%;
  background: conic-gradient(#0000ff00, #ff0000ff);
  animation: rotate-border 5s linear infinite;
}

@keyframes rotate-border {
    to {
        transform: rotate(360deg);
    }
}
<div class="loading-box-container">
  <div class="loading-box">
    <p>Loading</p>
  </div>
</div>

另一种方法:使用@属性

使用 @property 有一个更优雅的解决方案,但不幸的是它只适用于 Chrome。我在这里包括在内,以防万一有一天它得到更广泛的支持,或者对其他浏览器的支持对您的用例来说并不重要。

conic-gradient 函数有一个参数,允许您指定渐变开始的角度。如果我们可以仅使用 那个参数 设置动画,也许使用 CSS 变量,那么我们可以只使用一个 div 设置边框动画,而无需实际旋转任何东西。

不幸的是,如果没有一些提示,浏览器不知道如何转换 CSS 变量。所以,我们用@property表示这个变量是一个角度,告诉浏览器如何过渡。

@property --rotation {
  syntax: '<angle>';
  initial-value: 0deg;
  inherits: false;
}

.loading-box {
  --size: 200px;
  --radius: 10px;
  position: relative;
  width: var(--size);
  height: var(--size);
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  background: #000;
  border-radius: var(--radius);
  margin: var(--radius);
}

.loading-box::before {
  --rotation: 0deg;
  content: '';
  width: calc(100% + 2 * var(--radius));
  height: calc(100% + 2 * var(--radius));
  border-radius: var(--radius);
  position: absolute;
  top: calc(-1 * var(--radius)); left: calc(-1 * var(--radius));
  background: conic-gradient(from var(--rotation), #0000ff00, #ff0000ff);
  animation: rotate-border 5s linear infinite;
  z-index: -1;
}

@keyframes rotate-border {
    to {
        --rotation: 360deg;
    }
}
<div class="loading-box">
  <p>Loading</p>
</div>

CanIUse for @property 表示这 仅适用于 Chrome 和 Edge post。