Как сделать с помощью CSS лоадер, соответствующий размеру контейнера

Хочу использовать следующий индикатор загрузки для контейнеров во время загрузки данных. Проблема в том, что лоадер имеет фиксированную ширину и высоту 300 х 300 пикселей, но я хочу, чтобы он динамически помещался в контейнер.

Когда я пытаюсь добавить width: 140px; и height: 140px; для класса основного контейнера, индикатор загрузки выглядит отвратительно, а пропорции неверны.

Я добавил ползунок и границу, чтобы было легче увидеть, что происходит не так.

let sliderElement = document.getElementById("slider");
let mainContainerDiv = document.querySelector(".main-container");

sliderElement.oninput = function() {
  changeContainerSize(this.value);
}

function changeContainerSize(value) {
  mainContainerDiv.style.width = value + "px";
  mainContainerDiv.style.height = value + "px";
}

changeContainerSize(sliderElement.value);
.main-container {
  border: 1px solid; 
}
.reverse-spinner {
  position: relative;
  height: 100px;
  width: 100px;
  border: 4px solid transparent;
  border-top-color: #1976d2;
  border-left-color: #1976d2;
  border-radius: 50%;
  -webkit-animation: spin 1.5s linear infinite;
  animation: spin 1.5s linear infinite;
}
.reverse-spinner::before {
  position: absolute;
  top: 15px;
  left: 15px;
  right: 15px;
  bottom: 15px;
  content: "";
  border: 4px solid transparent;
  border-top-color: #03a9f4;
  border-left-color: #03a9f4;
  border-radius: 50%;
  -webkit-animation: spinBack 1s linear infinite;
  animation: spinBack 1s linear infinite;
}

.flexbox {
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  -ms-flex-wrap: wrap;
  flex-wrap: wrap;
}

.flexbox > div {
  width: 140px;
  height: 140px;
  -webkit-box-flex: 0;
  -ms-flex: 0 0 25%;
  flex: 0 0 25%;
  border: 1px solid rgba(255, 255, 255, 0.1);
  border: 2px dashed green; 
  -webkit-box-sizing: border-box;
  box-sizing: border-box;
  margin: 0;
  position: relative;
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  -webkit-box-pack: center;
  -ms-flex-pack: center;
  justify-content: center;
  -webkit-box-align: center;
  -ms-flex-align: center;
  align-items: center;
  overflow: hidden;
}

-webkit-@keyframes spin {
  -webkit-from {
    -webkit-transform: rotate(0deg);
    -ms-transform: rotate(0deg);
    transform: rotate(0deg);
  }
  -webkit-to {
    -webkit-transform: rotate(360deg);
    -ms-transform: rotate(360deg);
    transform: rotate(360deg);
  }
}

@-webkit-keyframes spin {
  from {
    -webkit-transform: rotate(0deg);
    transform: rotate(0deg);
  }
  to {
    -webkit-transform: rotate(360deg);
    transform: rotate(360deg);
  }
}

@keyframes spin {
  from {
    -webkit-transform: rotate(0deg);
    transform: rotate(0deg);
  }
  to {
    -webkit-transform: rotate(360deg);
    transform: rotate(360deg);
  }
}

@-webkit-keyframes spinBack {
  from {
    -webkit-transform: rotate(0deg);
    transform: rotate(0deg);
  }
  to {
    -webkit-transform: rotate(-720deg);
    transform: rotate(-720deg);
  }
}

@keyframes spinBack {
  from {
    -webkit-transform: rotate(0deg);
    transform: rotate(0deg);
  }
  to {
    -webkit-transform: rotate(-720deg);
    transform: rotate(-720deg);
  }
}
<input id="slider" type="range" min="100" max="500" value="500">

<div class="main-container">
  <div class="flexbox">
    <div>
      <div class="reverse-spinner"></div>
    </div>
  </div>
</div>


Ответы (3 шт):

Автор решения: Alexandr_TT

Вы можете изменить код лоадера, как показано ниже, чтобы полагаться на процентные значения и трюк заполнения, чтобы сохранить соотношение сторон, тогда вы можете легко настроить размер, изменив только ширину:

.reverse-spinner {
  width: 100px;
  border: 4px solid transparent;
  border-top-color: #1976d2;
  border-left-color: #1976d2;
  border-radius: 50%;
  animation: spin 1.5s linear infinite;
}

.reverse-spinner::before {
  content: "";
  display:block;
  padding-top:calc(70% - 8px);
  margin:15%;
  border: inherit;
  border-top-color: #03a9f4;
  border-left-color: #03a9f4;
  border-radius: inherit;
  animation: spin 0.5s linear infinite reverse;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}
<div class="reverse-spinner"></div>


<div class="reverse-spinner" style="width:80px;"></div>

<div class="reverse-spinner" style="width:40px;"></div>

<div class="reverse-spinner" style="width:40%;"></div>

Другая версия с radial-gradient и без использования calc()

.reverse-spinner {
  width: 100px;
  background:
    radial-gradient(farthest-side at bottom,transparent 95%,#1976d2 96%) 
    top/100% 50% no-repeat;
  border-radius: 50%;
  overflow:hidden;
  animation: spin 1.5s linear infinite;
}

.reverse-spinner::before {
  content: "";
  display:block;
  padding-top:70%;
  margin:15%;
  background:
      radial-gradient(farthest-side at bottom,transparent 93%,#03a9f4 94%) 
      top/100% 50% no-repeat;
  border-radius: inherit;
  animation: spin 0.5s linear infinite reverse;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}
<div class="reverse-spinner"></div>


<div class="reverse-spinner" style="width:80px;"></div>

<div class="reverse-spinner" style="width:40px;"></div>

<div class="reverse-spinner" style="width:40%;"></div>

Еще одна версия с меньшим количеством кода:

.reverse-spinner {
  width: 100px;
  background: top/100% 50% no-repeat;
  background-image:
    radial-gradient(farthest-side at bottom,transparent 95%,#1976d2 96% 99%,transparent 100%);
  animation: spin 1.5s linear infinite;
}

.reverse-spinner::before {
  content: "";
  display:block;
  padding-top:100%;
  background:inherit;
  background-image:
    radial-gradient(farthest-side at bottom,transparent 73%,#03a9f4 74% 79%,transparent 80%);
  animation: spin 0.5s linear infinite reverse;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}
<div class="reverse-spinner"></div>


<div class="reverse-spinner" style="width:80px;"></div>

<div class="reverse-spinner" style="width:50px;"></div>

<div class="reverse-spinner" style="width:40%;"></div>

Источник ответа: @Temani Afif

→ Ссылка
Автор решения: Alexandr_TT

SVG решение

Лоадер в SVG сделать проще и он всегда будет адаптивным, то есть будет изменять размеры пропорционально изменению размеров родительского контейнера.

В примере ниже используется один и тот же лоадер id="loader" в трёх контейнерах
<div class="reverse-spinner"> c размерами: width:10%, width:20% , width:30%

.reverse-spinner {
display: inline-block;
}
.container {
width:100vw;
height:100vh; 
background:#EAEAEA;
}
<div class="container" >
     <!-- Ширина контейнера width:10% -->
<div class="reverse-spinner" style="width:10%; background-color:gold; " >
 <svg version="1.1" xmlns="http://www.w3.org/2000/svg" 
    xmlns:xlink="http://www.w3.org/1999/xlink"
	        viewBox="0 0 140 140" style="border:0px solid;" >  
 <g id="loader" >
    <circle cx="70" cy="70" r="60" fill="none" stroke="#1976D2" stroke-width="4" stroke-dashoffset="0" stroke-dasharray="188.4" stroke-linecap="round" >
      <animate attributeName="stroke-dashoffset" begin="0s" dur="1.5s" values="0;376.8" repeatCount="indefinite" />
 </circle>   
   <circle cx="70" cy="70" r="40" fill="none" stroke="#03A9F4" stroke-width="4" stroke-dashoffset="0" stroke-dasharray="125.5" stroke-linecap="round" >
      <animate attributeName="stroke-dashoffset" begin="0s" dur="1s" values="251.2;0" repeatCount="indefinite" />
 </circle>
  </g> 
</svg>	
</div> 
         <!-- Ширина контейнера width:20% -->
<div class="reverse-spinner" style="width:20%; background:#31C1B0;">
  <svg version="1.1" xmlns="http://www.w3.org/2000/svg" 
    xmlns:xlink="http://www.w3.org/1999/xlink"
	        viewBox="0 0 140 140" style="border:0px solid;" >  
     <use xlink:href="#loader" > </use> 
  </svg>	
</div>  
            <!-- Ширина контейнера width:30% -->
  <div class="reverse-spinner" style="width:30%; background:#FFA6E3;">
    <svg version="1.1" xmlns="http://www.w3.org/2000/svg" 
    xmlns:xlink="http://www.w3.org/1999/xlink"
	        viewBox="0 0 140 140" style="border:0px solid;" >  
       <use xlink:href="#loader" > </use> 
</svg>	
</div> 
</div>

Вариант со слайдером изменения ширины родительского блока

.container {
width: var(--w1,40px); 
background:#EAEAEA;
 
}
<div>
<input type="range" min="40" max="600" oninput="document.querySelector('.container').style.setProperty('--w1', this.value + 'px');" value="50"><span>width Container</span><br> 
</div>
<div class="container" >

<svg version="1.1" xmlns="http://www.w3.org/2000/svg" 
    xmlns:xlink="http://www.w3.org/1999/xlink"
	        viewBox="0 0 140 140" style="border:1px solid;" >  
 <g id="loader" >
<circle cx="70" cy="70" r="60" fill="none" stroke="#1976D2" stroke-width="4" stroke-dashoffset="0" stroke-dasharray="188.4" stroke-linecap="round" >
   <animate attributeName="stroke-dashoffset" begin="0s" dur="1.5s" values="0;376.8" repeatCount="indefinite" />
 </circle>   
   <circle cx="70" cy="70" r="40" fill="none" stroke="#03A9F4" stroke-width="4" stroke-dashoffset="0" stroke-dasharray="125.5" stroke-linecap="round" >
    <animate attributeName="stroke-dashoffset" begin="0s" dur="1s" values="251.2;0" repeatCount="indefinite" />
 </circle>
  </g> 
</svg>	
</div>

Как видно из примеров у SVG нет проблем с адаптивностью и встраиванием в HTML

UPDATE

Немного сложнее пример с обтекающим текстом вокруг родительского контейнера SVG

Изменяя размер родительского контейнера <div class="container" >, тем самым меняем размер, вложенного SVG лоадера и перекомпановку HTML текста.

.container {
width: var(--w1,60px); 
background:#EAEAEA;
 float:left;
 margin-right: 1em;
}
.txt {
font-size:1.2em;

}
<div class="cont" >
<input type="range" min="60" max="600" oninput="document.querySelector('.container').style.setProperty('--w1', this.value + 'px');" value="60">
</div>
<div class="container" >
<svg  viewBox="0 0 120 120">
	
<defs>
	 <mask id="msk1">
      <circle class="maskCircle" cx="60" cy="60" r="40" fill="none" stroke="white" stroke-width="8" stroke-dashoffset="0" stroke-dasharray="251.2">
	     <animate attributeName="stroke-dashoffset" dur="3s" values="251.2;0" fill="freeze" repeatCount="indefinite" />
	 </circle>
	 </mask>
</defs>
	   <circle class="background" cx="60" cy="60" r="40" fill="none" stroke="#E4E4E4" stroke-width="8" />
		<circle class="default" cx="60" cy="60" r="40" fill="none" stroke="black" stroke-width="8"
		stroke-dasharray="3.14" stroke-dashoffset="40"  mask="url(#msk1)">
		
		</circle>
</svg>
</div> 
<div class="txt">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus tincidunt dignissim nibh a facilisis. Phasellus pretium nisl vel turpis suscipit, quis posuere quam laoreet. Vestibulum fringilla porttitor felis, non lacinia dolor mattis vitae. Donec gravida et purus eu pellentesque. Nam consequat nisl id velit interdum eleifend. Mauris nulla turpis, sollicitudin in vestibulum nec, ornare quis lacus. Proin eros diam, lacinia id blandit a, efficitur eget mauris. Morbi ullamcorper velit non urna malesuada molestie. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec vehicula est non nunc ultricies, sit amet pharetra nulla congue. Vestibulum hendrerit ante ac rhoncus aliquet. Quisque vitae est turpis. Nulla sit amet felis nec nisl blandit mollis.

Nullam ornare turpis quis orci hendrerit, vitae varius est tincidunt. In quis malesuada tellus. Aliquam nunc mauris, aliquam quis lectus ut, ultrices lobortis ante. Maecenas tincidunt ligula felis, in consequat lectus finibus eget. Maecenas molestie placerat odio, at malesuada risus consequat id. Integer congue facilisis enim porta pretium. Morbi scelerisque tincidunt purus, vitae rhoncus tortor tristique nec. Nullam laoreet turpis nec felis vehicula finibus.

Fusce ultricies maximus ante vitae imperdiet. Proin magna orci, pretium nec pretium id, ultricies in risus. Praesent mauris ipsum, semper nec convallis sit amet, vestibulum non enim. Quisque volutpat nulla ac nulla venenatis, ac cursus nulla laoreet. Aliquam malesuada dui a orci tristique tristique. Phasellus fringilla, velit eget ultrices efficitur, justo lacus elementum lorem, nec convallis augue lacus non dui. Aliquam erat volutpat. Mauris sagittis, sapien in tristique mollis, ex libero laoreet magna, ut pellentesque dolor augue accumsan elit. Nunc ut augue at eros sollicitudin malesuada. Pellentesque urna felis, posuere sit amet odio et, vestibulum aliquet ex. Nulla eu dolor blandit, bibendum nibh in, tincidunt ex. Ut nisl justo, tincidunt a augue et, gravida sollicitudin neque. Donec id dignissim elit, a sollicitudin leo. Aliquam placerat orci justo, et faucibus nibh volutpat vel.

Nulla varius, est eu dictum vulputate, risus tellus euismod augue, ac tempus velit elit nec dui. Duis eleifend metus eu nisi mattis scelerisque. Proin finibus neque ut pellentesque porta. Etiam quis ligula non ligula sollicitudin efficitur at a turpis. Morbi tristique maximus quam eu imperdiet. Morbi dolor augue, sagittis non urna quis, fringilla scelerisque erat. Praesent et tempor tortor. In hac habitasse platea dictumst. Sed ut consectetur arcu, id vestibulum sapien. Integer in metus et lacus sollicitudin sollicitudin. Curabitur vulputate mollis odio, sit amet dictum mauris condimentum non. Vestibulum enim risus, luctus a tristique sit amet, semper non enim. Nunc laoreet hendrerit massa at blandit.

Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Mauris dapibus eu ipsum ac semper. Suspendisse turpis nunc, mattis sit amet lorem id, fringilla iaculis purus. Praesent egestas est id ullamcorper tristique. Nulla tempus sem rutrum, sagittis dui quis, condimentum massa. Nam efficitur purus sed sapien euismod imperdiet. Donec laoreet augue eget justo dapibus suscipit. Etiam eleifend neque iaculis, pellentesque elit vel, malesuada justo. Aenean efficitur eros quis mattis convallis. Quisque hendrerit libero ligula, vel fermentum orci ultrices vel. In porttitor congue interdum. Duis ac vehicula est. Nullam sollicitudin eros a tortor semper, laoreet facilisis neque hendrerit. Integer faucibus elementum nibh, id venenatis urna consectetur a.
</div>

Источник ответа: @Alexandr_TT

→ Ссылка
Автор решения: meine

Можно через font-size работать, думаю, что прелоадер не важен. Ради примера другой вставил, суть всё равно не меняется.

body {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100vh;
  margin: 0;
}

.container {
  position: relative;
  font-size: 30vw;
  width: 300px;
  height: 300px;
  background-color: #d3d3d3;
}

.loader {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

.loader__outer,
.loader__inner {
  border: .025em solid #333;
  border-radius: 50%;
  animation: spin 5s linear infinite;
}

.loader__outer {
  width: 1em;
  height: 1em;
  border-right-color: transparent;
  border-left-color: transparent;
  display: flex;
  align-items: center;
  justify-content: center;
}

.loader__inner {
  width: .7em;
  height: .7em;
  border-top-color: transparent;
  border-bottom-color: transparent;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}
<div class="container">
  <div class="loader">
    <div class="loader__outer">
      <div class="loader__inner"></div>
    </div>
  </div>
</div>

→ Ссылка