Dyanmic resize a CSS3 loading spinner by pixels - css

I've been asked to take a pure CSS3 loading spinner and make it dynamically resizable by pixels to use in different places in a program.
My current code is: (Which apparently doesn't run well in SO's snippets)
.loader {
animation:spin 1s infinite linear;
border:solid 2vmin transparent;
border-radius:50%;
border-right-color:#71c491;
border-top-color:#f7941d;
box-sizing:border-box;
height:20vmin;
left:calc(50% - 10vmin);
position:fixed;
top:calc(50% - 10vmin);
width:20vmin;
z-index:1;
&:before {
animation:spin 2s infinite linear;
border:solid 2vmin transparent;
border-radius:50%;
border-right-color:#21409a;
border-top-color:#92278f;
box-sizing:border-box;
content:"";
height:16vmin;
left:0;
position:absolute;
top:0;
width:16vmin;
}
&:after {
animation:spin 3s infinite linear;
border:solid 2vmin transparent;
border-radius:50%;
border-right-color:#13b0e6;
border-top-color:#18244c;
box-sizing:border-box;
content:"";
height:12vmin;
left:2vmin;
position:absolute;
top:2vmin;
width:12vmin;
}
}
#keyframes spin {
100% {
transform:rotate(360deg);
}
}
<div class="loader"></div>
I googled and tried transform:scale() but as far as I can tell that only takes specific input and increases/decreases the size by percentage. (2 = 200% size)
I'm thinking I need some sort of wrapper, but I'm not too familiar with advanced CSS to get the effect. When I tried to create my own, only the top border of the spinner would be resized into a weird shape and not the inner borders. I'm just stumped. If you could point me in the right direction, I'd be appreciative. Thank you.

You could try a mix of CSS var() / calc() / clamp() / grid ... and relative/absolute positionning to lay the loader over the parent where you need it , if that inspire you :
demo with a few loader within a div sized and the possibility to set an average size to start from, % size based on the width of the parent.
value to reset in the demo is --size ; you may also tune the other --MyVarCss values to your needs.
* {
box-sizing: border-box;
}
:root { /* init for the var() values */
--size: 20;/* value used to set the loader's width and adjust border's width */
--width: calc(var(--size) * 1%);
--widthBorder: calc( clamp(20px, 6vw, 80px) * var(--size) * 0.005);
}
.a,/* for the demo , just a bunch of containers */
.b,
.c,
.d,
.d,
.e {
position: relative;
/* what the parent loader needs to be (absolute/fixed/sticky works too, static not) */
float: left;
border: solid;
margin: 1em;
}
div.a {
--size: 50; /* reset the value used to set the loader's width */
width: 50%;
padding-top: 50%;
}
.b {
--size: 10;/* reset the value used to set the loader's width */
width: 600px;
height: 200px;
}
.c {
--size: 15;/* reset the value used to set the loader's width */
width: 25%;
padding-top: 20%;
}
.d {
--size: 30;/* reset the value used to set the loader's width */
width: 800px;
height: 400px;
}
.e {
--size: 14;/* reset the value used to set the loader's width */
width: 90%;
min-height: 20vh;
}
div {
width: 20%;
padding-top: 20%;
}
/* loader styles */
.loader {
position: absolute;
top: 0;
left: 0;
display: flex;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
.loader b {
display: grid;
animation: rotate 3s -1s infinite linear;
border: solid var(--widthBorder) transparent;
padding: calc(var(--widthBorder) / 2);
border-radius: 50%;
border-right-color: #71c491;
border-top-color: #f7941d;
grid-row: 1;
grid-column: 1;
margin: 0;
}
.loader>b {
margin: auto;
width: var(--width);
}
.loader>b:before {
content: "";
padding-top: 100%;
grid-row: 1;
grid-column: 1;
}
.loader b b {
border-right-color: #21409a;
border-top-color: #92278f;
}
.loader b b b {
border-right-color: #13b0e6;
border-top-color: #18244c;
padding: 0;
}
#keyframes rotate {
100% {
transform: rotate(360deg);
}
}
<div class=a>
<div class="loader"><b><b><b></b></b>
</b>
</div>
</div>
<div class=b>
<div class="loader"><b><b><b></b></b>
</b>
</div>
</div>
<div class=c>
<div class="loader"><b><b><b></b></b>
</b>
</div>
</div>
<div class=d>
<div class="loader"><b><b><b></b></b>
</b>
</div>
</div>
<div class=e>
<div class="loader"><b><b><b></b></b>
</b>
</div>
</div>

Related

How to Circumscribe a square in a circle CSS [duplicate]

I've looked into this a fair bit but can't seem to find a good, solid answer to find how to make a responsive circle around a div element of variable height.
It's easy to make a simple responsive circle using vw units.
<div style="height:20vw; width:20vw"></div>
However, I'm looking to use a min-height of an element and have a circle around this div.
Another way to create a responsive circle is using something like the snippet below, but again I can't adapt this to work for a variable height (again, I can't use vh units as the div will change in height.
.square {
position: relative;
width: 10%;
background: gray;
border-radius: 50%;
}
.square:after {
content: "";
display: block;
padding-bottom: 100%;
}
.content {
position: absolute;
width: 100%;
height: 100%;
}
<div class="square">
<div class="content">
</div>
</div>
I am trying to create something like the below, where the circle will never cut into the corners of the div (with around a 10px padding). I personally was trying to avoid javascript and would have preferred a css only approach, but it seems it's unavoidable. Maybe the only solution is to use a jquery to calculate the height of the element in order to apply this to a wrapper element?
I was playing around with this:
.square {
position: absolute;
top: 50%;
display: inline-block;
left: 50%;
transform: translate(-50%, -50%);
min-height: 100px;
border-radius: 50%;
background: url('https://i.imgur.com/2dxaFs9_d.webp?maxwidth=640&shape=thumb&fidelity=medium');
background-size: 100% 100%;
padding: 20px;
}
.content {
width: 300px;
min-height: 100px;
background: tomato;
}
<div class="square">
<div class="content">
Hello!<br>
<br><br><br>This has a variable height but fixed width<br><br><br>Hello
</div>
</div>
Clip-path can easily do this if you consider solid coloration.
Resize the element and the circle will follow:
.box {
width: 200px;
height: 200px;
overflow: hidden;
resize: both;
background: blue;
box-shadow: 0 0 0 200vmax red;
clip-path: circle(71%);
margin: 100px auto;
}
<div class="box"></div>
Related question to understand the magic number 71%: clip-path:circle() radius doesn't seem to be calculated correctly
To use an image we can consider pseudo elements. You can also rely on calc() to add the offset:
.box {
width: 200px;=
resize: both;
clip-path: circle(calc(71% + 10px));
margin: 100px auto;
position: relative;
font-size:35px;
color:#fff;
}
/* the background layer */
.box::before {
content:"";
position: absolute;
z-index:-1;
top:0;
left:0;
right:0;
bottom:0;
background:blue;
}
/* the image layer */
.box::after {
content:"";
position: fixed; /* to make sure the image cover all the screen */
z-index:-2;
top:0;
bottom:0;
left:0;
right:0;
background:url(https://picsum.photos/id/1015/1000/1000) center/cover no-repeat;
}
<div class="box" contenteditable="true"> Edit this<br>text </div>
I tried my hardest to figure this out with pure css. Though the problem with css I could not figure out how to calculate the diameter of the circle based on the content div size; the length from top left corner to bottom right corner of the variable height div.
I'm not sure if can be done using the calc() css function.
But I did manage to do it with a little jquery (which could easily be changed to pure javascript if you are not using jquery).
See working resizable example below (follow my comments in code)
Note: If you are using internet explorer the resizable demo content div will not resize.
// circumscriber for variable size divs
function circumscriber() {
// for each variable size div on page
$(".variable-size").each(function() {
// get the variable size div content width and height
let width = $(this).outerWidth();
let height = $(this).outerHeight();
// get the diameter for our pefect circle based on content size
let diameter = Math.sqrt(width ** 2 + height ** 2);
// extra 15 pixel circle edge around variable size div
let edge = 15;
// add current circle size width css
$('.circle', this).css({
'width': (diameter + (edge * 2)) + 'px'
})
});
}
// run the circumscriber (you might wana do this on ready)
circumscriber();
// if the window is resized responsively
$(window).on('resize', function() {
circumscriber();
});
// for demo purpose to fire circumscriber when resizing content
// not needed for real thing
$('.content').on('input', function() {
this.style.height = "";
this.style.height = ( this.scrollHeight - 30 ) + "px";
circumscriber();
}).on('mouseup', function() {
circumscriber();
});
/* variable size container to be circumscribed by circle */
/* none of these styles are required, this just to center the variable size div in the window for demo purposes */
.variable-size {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
/* resizable text area for demo */
/* again not needed */
.variable-size .content {
padding: 15px;
background: #fff;
resize: both;
overflow: auto;
color: #000;
border: none;
width: 200px;
font-weight: bold;
}
.variable-size .content:focus {
outline: 0;
}
/* child circle div css */
.variable-size .circle {
position: absolute;
background-image: url('https://i.imgur.com/2dxaFs9_d.webp?maxwidth=640&shape=thumb&fidelity=medium');
background-position: center center;
z-index: -1;
border-radius: 50%;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
transition: all 0.5s ease;
width: 0;
}
/* fast way to make circle height the same as current width */
.variable-size .circle:before {
display: block;
content: '';
width: 100%;
padding-top: 100%;
}
/* demo window css */
HTML,
BODY {
height: 100%;
min-height: 100%;
background: black;
position: relative;
font-family: "Lucida Console", Courier, monospace;
}
<div class="variable-size">
<textarea class="content" rows="1" placeholder="TYPE TEXT OR RESIZE ME ↘"></textarea>
<div class="circle"></div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
See jsfiddle here... https://jsfiddle.net/joshmoto/6d0zs7uq/
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.beginPath();
ctx.arc(100, 75, 50, 0, 2 * Math.PI);
ctx.stroke();
Source: https://www.w3schools.com/
You could use flex display and insert empty flex-items around the inner div and use flex-basis to fix their width.
Try this
.square {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
min-height: 100px;
border-radius: 50%;
background: black;
background-size: 100% 100%;
padding: 20px;
}
.content {
width: 300px;
min-height: 100px;
background: tomato;
}
.emptyDiv {
flex-basis: 120px
}
<div class="square">
<div class="emptyDiv"></div>
<div class="content">
Hello!<br>
<br><br><br>This has a variable height but fixed width<br><br><br>Hello
</div>
<div class="emptyDiv"></div>
</div>

How to position a tilted/skewed shape of unknown height in CSS?

Problem
I need to code a design like in image below in CSS. In addition to pure CSS I also have Sass at my disposal.
Note that the solution needs to be responsive. The angle is fixed (14 degrees), while the distance x is not, because it depends on the height of the container (which will be different on different screen widths).
Fixed height
The fixed height version is not a problem:
HTML
<section class="container">
<p class="left">
Some text spanning multiple lines
</p>
<p class="right">
Some text spanning multiple lines
</p>
</section>
CSS
#use "sass:math";
$trapezium-skew-angle: -14deg;
#mixin orange-background {
position: relative;
// Needs to use pseudo-element to be able to render it
// below the trapezium's layer
&::before {
z-index: -1;
content: "";
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
background-color: orange;
}
}
#function trapezium-shift($trapezium-height) {
#return (-1 * math.tan($trapezium-skew-angle) * $trapezium-height) / 2;
}
#mixin trapezium($height, $width) {
overflow: hidden;
position: relative;
&::after {
z-index: -1;
content: "";
display: block;
position: absolute;
left: -1 * trapezium-shift($height);
top: 0;
bottom: 0;
width: $width;
background-color: blue;
transform: skew($trapezium-skew-angle);
}
}
#mixin column {
outline: 1px dashed black;
padding: 4rem;
width: 50%;
}
.container {
#include orange-background;
display: flex;
flex-flow: row nowrap;
height: 300px;
}
.left {
#include column;
#include trapezium($height: 300px, $width: 50%);
}
.right {
#include column;
}
Responsiveness
The problem is that my implementation needs to be responsive. When the screen height changes, the height of the container will change too and so will change the value of trapezium-shift (which is marked as x on the image). Sass runs at build-time, so there is no way for Sass to know the height of the container.
Question
Do you know about any possible solution to this?
One idea is to consider a big element with a triangular shape where the width/height is set considering the angle you want to use.
Here is an example to illustrate:
.container {
display: flex;
height: 80vh; /* dynamic height */
background:blue;
overflow:hidden; /* hide the overflow of the big shape */
color:#fff;
font-size:25px;
}
.container > * {
outline: 1px dashed black;
padding: 4rem;
width: 50%;
margin:0;
box-sizing:border-box;
}
.container > *:last-child {
background:orange;
position: relative;
}
.container > *:last-child::before {
content:"";
position:absolute;
right:calc(100% - 0.5px); /* a litte less than 100% to avoid a gap issue */
top:0;
height:130vh; /* a big height, 100vh should be enough but a bit bigger won't hurt */
width:calc(130vh * 0.249328); /* height x tan(14deg) */
background:inherit;
clip-path:polygon(0 100%,100% 100%,100% 0); /* triangle shape */
opacity:0.9; /* to illustrate the shape */
}
body {
margin:0;
}
<section class="container">
<p class="left">
Some text spanning multiple lines
</p>
<p class="right">
Some text spanning multiple lines
</p>
</section>
Another idea with the same trick but using box-shadow and less of code:
.container {
display: flex;
height: 80vh; /* dynamic height */
background:blue;
overflow:hidden; /* hide the overflow of the big shape */
color:#fff;
font-size:25px;
}
.container > * {
outline: 1px dashed black;
padding: 4rem;
width: 50%;
margin:0;
}
.container > *:last-child {
background:orange;
box-shadow:0 0 0 100vw orange; /* a very big box shadow*/
clip-path:polygon(0 0,100% 0, 100% 150vh,calc(-0.249328 * 150vh) 150vh);
/* the clip-path will cut a shape like below
(0 0) _______ (100% 0)
/ |
/ | <--- the real content end here, below is overflowing
(X 150vh) /_________| (100% 150vh)
X = 0 - tan(14deg)*150vh
*/
}
body {
margin:0;
}
<section class="container">
<p class="left">
Some text spanning multiple lines
</p>
<p class="right">
Some text spanning multiple lines
</p>
</section>
UPDATE
The first code without clip-path for better support:
.container {
display: flex;
height: 80vh; /* dynamic height */
background:blue;
overflow:hidden; /* hide the overflow of the big shape */
color:#fff;
font-size:25px;
}
.container > * {
outline: 1px dashed black;
padding: 4rem;
width: 50%;
margin:0;
box-sizing:border-box;
}
.container > *:last-child {
background:orange;
position: relative;
}
.container > *:last-child::before {
content:"";
position:absolute;
right:calc(100% - 0.5px); /* a litte less than 100% to avoid a gap issue */
top:0;
height:130vh; /* a big height, 100vh should be enough but a bit bigger won't hurt */
width:calc(130vh * 0.249328); /* height x tan(14deg) */
background:linear-gradient(to bottom right,transparent 49.5%,orange 50%); /* triangle shape */
opacity:0.9; /* to illustrate the shape */
}
body {
margin:0;
}
<section class="container">
<p class="left">
Some text spanning multiple lines
</p>
<p class="right">
Some text spanning multiple lines
</p>
</section>

Marching ant border using a GIF

I know there's a way to do marching ants with linear-gradients but it eats a huge amount of CPU (about 10% for each one). I am trying to make an alt solution but find border-image-slice confusing.
Here is the outdated tutorial I'm using: http://www.chrisdanford.com/blog/2014/04/28/marching-ants-animated-selection-rectangle-in-css/
I've removed the obsolete css but I'm not sure how to slice the image so that the ants are marching. The tut says:
We’ll start with a 10px x 10px animated gif that is composed of nine
tiles: 1×1 in the corners, 1×8 or 8×1 on the edges, and 8×8 in the
center.
body {
background-color: green;
}
.box {
width: 200px;
height: 200px;
background-color: black;
}
.marching {
border: 1px solid transparent;
border-image-source: url('https://i.imgsafe.org/e5bc19b03a.gif');
border-image-slice: 1;
border-image-repeat: stretch stretch;
}
<div class="box marching"></div>
Thanks
The background should repeat, not stretch. Is this what you're going for?
body {
background-color: green;
}
.box {
width: 200px;
height: 200px;
background-color: black;
}
.marching {
border: 1px solid transparent;
border-image-source: url('https://i.imgsafe.org/e5bc19b03a.gif');
border-image-slice: 1;
border-image-repeat: repeat repeat;
}
<div class="box marching"></div>
Here is CSS version I found, how does it compare with the others when it comes to CPU usage?
body { background: green; }
.box {
position: relative;
width: 90px;
height: 90px;
overflow: hidden;
margin: 10px;
background: black;
}
.box * {
position: absolute;
}
.box div {
width: 100%;
height: 100%;
}
.box div:nth-child(1) { transform: rotate( 0deg ); }
.box div:nth-child(2) { transform: rotate( 90deg ); }
.box div:nth-child(3) { transform: rotate( 180deg ); }
.box div:nth-child(4) { transform: rotate( 270deg ); }
.box i {
left: 0;
top: 0;
width: 200%;
border-bottom: 1px dashed white;
}
.box i {
animation: marching 4s infinite linear;
}
#keyframes marching {
from { transform: translateX( -50% ); }
to { transform: translateX( 0% ); }
}
<div class="box">
<div><i></i></div>
<div><i></i></div>
<div><i></i></div>
<div><i></i></div>
</div>
Src: https://jsfiddle.net/desandro/zm7Et/
Is there any reason why you're not using a canvas?
The Canvas element has a lineDashOffset property that is often used for marching ant effects:
https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineDashOffset
If you're planning on using a GIF it looks as though scaling isn't something you're worried about, so maybe a canvas might be a better way to do it. Buffer-canvases might also be used to improve performance.

Slide-in from the left with width: auto

I'm implementing a toolbar using flex boxes and want optional sections to appear by smoothly sliding in from the left.
At the start, the bar should look as if the new element didn't exist, and at the end as if the animation never took place
Animating 0% { margin-left: -<width> } works exactly as intended, but only when the element's width is known at that point; I need it to work with width: auto (i.e. unset width) or other non-lengths like when flexing.
#keyframes appear {
0% { margin-left: -30ch; }
}
div.a {
width: 30ch; /* << should work without that */
flex: none; /* << stretch: should work with flex */
animation: appear 1s ease-in-out infinite alternate;
}
.root {
margin: 10px;
border: 1px solid black;
display: flex;
overflow: hidden;
}
.a, .b, .c {
box-sizing: border-box;
padding: 5px;
flex: 1;
}
.b { flex: 2 }
.a { background: #88f }
.b { background: #8d8 }
.c { background: #f88 }
<div class="root">
<div class="a">Some new stuff</div>
<div class="b">Hello</div>
<div class="c">World</div>
</div>
<div class="root">
<div class="a">Some longer content sliding in</div>
<div class="b">Hello</div>
<div class="c">World</div>
</div>
Width:auto; is not animatable. Use max-width:1000px; or something larger than the width will ever need to be. So the code would be:
width:auto;
max-width:1000px;

CSS Heart with Text within

I would like to put a name into a heart made with CSS. And I can't seem to figure out how to do it.
I have this code already:
#heart {
position:relative;
width:100px;
height:100px;
}
#heart:before,#heart:after {
position:absolute;
content:"";
left:50px;
top:0;
width:50px;
height:80px;
background:#F00000;
-moz-border-radius:50px 50px 0 0;
border-radius:50px 50px 0 0;
-webkit-transform:rotate(-45deg);
-moz-transform:rotate(-45deg);
-ms-transform:rotate(-45deg);
-o-transform:rotate(-45deg);
transform:rotate(-45deg);
-webkit-transform-origin:0 100%;
-moz-transform-origin:0 100%;
-ms-transform-origin:0 100%;
-o-transform-origin:0 100%;
transform-origin:0 100%;
}
#heart:after {
left: 0;
-webkit-transform:rotate(45deg);
-moz-transform:rotate(45deg);
-ms-transform:rotate(45deg);
-o-transform:rotate(45deg);
transform:rotate(45deg);
-webkit-transform-origin:100% 100%;
-moz-transform-origin:100% 100%;
-ms-transform-origin:100% 100%;
-o-transform-origin:100% 100%;
transform-origin:100% 100%;
}
When I try to write the name directly into the div: "#heart", it just puts the text behind.
Thanks in advance for any help!
add a span element
<span id="text">Love</span>
with css
#text{
position:absolute;
z-index:3;
margin-left:35px;
margin-top:25px;
color:white;
display:block;
}
see this fiddle http://jsfiddle.net/FH9S7/
You can apply z-index: -1 to the :before and :after elements. It will move the heart shapes behind the text without needing an extra div.
After that, you can play around a bit with the paddings or text-align to align the text inside the heart:
http://jsfiddle.net/GolezTrol/hYEb6/1/
PS: In my fiddle I changed the id to a classname. By doing so, you can easily recycle the styling to add multiple hearts to the page.
-edit-
Maybe you'll like this one. If you are going to use an extra element, it's a bit easier to make the heart flexible in size as well:
The HTML can be (using classes again, of course):
<div class="heart">
<div class="inner">
Test
</div>
</div>
The CSS is a little bigger, but scalable:
.heart {
/* The only thing needed to change the size, are these numbers: */
width:200px;
height:200px;
}
.heart .inner {
/* Here is the styling and positioning for your text */
padding-top: 20%;
font-size: 3em;
color: white;
font-weight: bold;
}
/* The rest is default, and doesn't need to be modified, unless you want to change background color or other 'heart' properties. */
.heart .inner {
box-sizing: border-box;
width: 100%;
height: 100%;
}
.heart {
position:relative;
text-align: center;
box-sizing: border-box;
}
.heart:after,
.heart .inner:before,
.heart .inner:after {
z-index: -1;
content: "";
display: block;
position: absolute;
background-color: #F00000;
}
.heart:after {
width: 60%;
height: 60%;
left: 20%;
top: 25%;
-webkit-transform:rotate(-45deg);
-moz-transform:rotate(-45deg);
-ms-transform:rotate(-45deg);
-o-transform:rotate(-45deg);
transform:rotate(-45deg);
border-radius: 0 30% 0 0;
}
.heart .inner:before,
.heart .inner:after {
width:58%;
height:58%;
-moz-border-radius:50%;
border-radius: 50%;
top: 5.5%;
}
.heart .inner:before {
left: 0;
}
.heart .inner:after {
right: 0%;
}
And here's the fiddle showing 3 hearts of different sizes: http://jsfiddle.net/GolezTrol/hYEb6/4/
If you want to use position:absloute, you can do it that way:
http://fiddle.jshell.net/y9e58/
<div id="abs">name</div>
#abs{
position:absolute;
top: 30px;
left:40px;
}

Resources