CSS анимированная градиентная граница на DIV

#html #css #css-animations #css-gradients

Вопрос:

Я пытаюсь создать загрузку DIV с границей, которая выглядит как неопределенный счетчик колец прогресса.

Я довольно близок к этому, основываясь на одном из примеров на https://css-tricks.com/gradient-borders-in-css/

Это здорово, когда граница не вращается. Когда вы устанавливаете border в :before элементе соответствие прозрачному border в gradient-box элементе, граница статического градиента выглядит идеально.

Однако, как только анимация будет добавлена, поскольку весь :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> 

Я пробовал поиграть с переполнением: скрыто; но граница просто исчезает.. есть ли способ «замаскировать» элемент :перед таким образом, чтобы все, что находится за этим загрузочным Div, все еще было видно за ним, и чтобы граница оставалась такой, какой она была задумана?

В принципе, моя цель состоит в том, чтобы цветовой градиент в border поворотах создавал эффект вращающегося/вращающегося края.

Ответ №1:

Мне нравится ваша оригинальная идея с использованием overflow: hidden , но чтобы она сработала, мне пришлось включить дополнительный div-обертку.

  • Внешняя оболочка определяет заполнение, которое служит областью отображения границы градиента
  • Внутренний 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

Существует гораздо более элегантное решение с использованием @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 для @property указывает, что с этого поста это будет работать только в Chrome и Edge.

Комментарии:

1. Мило! Подход @property интересен, но в данный момент мне нужна совместимость с кросс-браузером, поэтому это дополнительный DIV — и он отлично смотрится. (Я также создал версию roudn, с помощью border-radius: 50%; которой можно избежать проблем, а не решать их.. и теперь мне нужно решить, что лучше всего смотрится с остальной частью дизайна страницы.

Ответ №2:

Привет, это то, что вы ищете?

Что я сделал, так это добавил новый раздел, который будет «маской», а также раздел контейнера как для маски, так и для загрузочного ящика.

Затем я определил размер маски так, чтобы она была немного больше вашей видимой области, сделал ее прозрачным фоном, а затем придал ей большой контур того же цвета, что и ваш фон, чтобы эффективно замаскировать границу. Затем я повозился с z-индексами маски, загрузочного ящика и предыдущего. Я также добавил некоторые фактические границы 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> 

Комментарии:

1. Тан Джон, я действительно задавался вопросом, понадобится ли упаковочный DIV. Я бы принял этот ответ (как и в первый раз), но почувствовал, что должен ответить @Auroratide из-за дополнительной информации об использовании @property.