I'm trying to create a "3 photo cube" with cube effect rotating.
Found a very helpful codepen which created the cube with 4 sides and did the rotating and stopping by side which was exactly what i wanted.
Issue here is that i need the cube to show just 3 sides not 4 and when i remove one of them, it still rotates on that side.
I thought i should just "match" top side bottom side so the back side doesn't show but it seems i am having a little knowledge at understanding how the positioning works.
see the below snippet.
.scene {
width: 416px;
height: 500px;
margin: 75px auto 0;
perspective: 1200px;
}
.cube {
position: relative;
width: 416px;
height: 500px;
transform-style: preserve-3d;
transform: translateZ(0px) rotateX(150deg);
animation: example 15s linear infinite;
}
.side {
position: absolute;
width: 416px;
height: 500px;
box-sizing: border-box;
background-color: #999;
background-size: 100% 100%;
background-repeat: no-repeat;
padding: 120px 0;
font: 50px/1 'Trebuchet MS', sans-serif;
color: #fff;
text-transform: uppercase;
text-align: center;
}
.side::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.15);
}
.side span {
position: relative;
}
.guides {
position: absolute;
top: 0;
left: 50px;
width: 200px;
height: 100%;
border-style: dotted;
border-width: 0 1px;
color: rgba(255, 255, 255, 0.6);
}
.guides::before {
content: '';
position: absolute;
top: 0;
left: 50%;
width: 0;
height: 100%;
border-left: 1px dotted;
}
.back {
transform: translateZ(-250px) rotateX(180deg);
}
.bottom {
transform: translateY(250px) rotateX(270deg);
}
.front {
transform: translateZ(250px);
}
.top {
background-image: url(https://askd.github.io/codepen/top.jpg);
}
.back {
background-image: url(http://keit.rezsolution.com/wp-content/uploads/2019/07/rimodelimi-i-hundes-galeri.jpg);
}
.bottom {
background-image: url(http://keit.rezsolution.com/wp-content/uploads/2019/07/foto-galeri-zmadhimi-i-gjoksit.jpg);
}
.front {
background-image: url(http://keit.rezsolution.com/wp-content/uploads/2019/07/barku-home.jpg);
}
#keyframes example {
0% { transform: translateZ(-150px) rotateX(0deg); }
15% { transform: translateZ(-150px) rotateX(90deg); }
25% { transform: translateZ(-150px) rotateX(90deg); }
40% { transform: translateZ(-150px) rotateX(180deg); }
50% { transform: translateZ(-150px) rotateX(180deg); }
65% { transform: translateZ(-150px) rotateX(270deg); }
75% { transform: translateZ(-150px) rotateX(270deg); }
90% { transform: translateZ(-150px) rotateX(360deg); }
100% { transform: translateZ(-150px) rotateX(360deg); }
}
<div class="scene">
<div class="cube">
<div class="side back">
<span>BACK</span>
</div>
<div class="side bottom">
<span>BOTTOM</span>
</div>
<div class="side front">
<span>FRONT</span>
</div>
</div>
</div>
Anybody can give me some directions on how to approach this issue?
Any help is appreciated
I made a working exemple of a rotating prism (Y axis) here.
And the same on the X axis here.
There are 2 things to figure out, the distance to translate each face and the rotation angle.
The distance bring back to trigonometry which made my brain hurt a little, but to make it simple, in this case you get it by doing : translationDistance = (faceWidth/2) / tan(30).See this article by David DeSandro for more explanations.
In my code :
--cotetriangle: 200px;
/* r = 100 / tan(30) = 57.7 */
--translationDistance: 58px;
The angle of rotation is easy, 3 faces, 360deg -> 120deg for each rotation.
Which gives you :
.triangle-face-front {
background: rgb(71, 71, 136);
transform: translate3d(0, 0, var(--translationDistance));
}
.triangle-face-left {
background: rgb(90, 233, 77);
transform: rotateY(-120deg) translate3d(0, 0, var(--translationDistance));
}
.triangle-face-right {
background: black;
transform: rotateY(120deg) translate3d(0, 0, var(--translationDistance));
}
I did a little 'pausing' animation as you suggested :
#keyframes rotateTriangle {
0% {
transform: rotateY(0deg);
}
24%,34%{
transform: rotateY(120deg);
}
58%,67%{
transform: rotateY(240deg);
}
91%, 100% {
transform: rotateY(360deg);
}
To switch between a lateral rotation (code above) and a frontal rotation, you just need to replace rotateY by rotateX
To understand better CSS 3D, I encourage you to read those 2 articles :
Intro to CSS 3D transforms by David DeSandro
Creating a 3D Cube Image Gallery by Kushagra Gour
Related
I want to transition an image, then rotate it from the bottom right corner.
.Wrapper {
width: 200px;
height: 200px;
background- color: yellow;
position: relative;
}
.Bottle {
position: absolute;
left: 0;
bottom: 0;
background-color: yellow;
animation: MoveBottle 10s infinite;
transform-origin: bottom right;
}
#keyframes MoveBottle {
10% {
transform: translate(50px, -90px);
color: green;
}
30% {
transform: rotate(90deg);
color: red;
}
80% {
transform: rotate(90deg);
color: red;
}
95% {
transform: rotate(0deg);
color: black;
}
100% {
transform: translate(0px, 0px);
}
}
<br><br><br><br><br>
<div class="Wrapper">empty the glass
<img class="Bottle" src="http://segalandassociates.com/images/eglass.png" alt="Champagne Bottle">
</div>
<br>
At the 30%, I want to rotate the image with the center rotation being the bottom right of the image.
Can anyone help me do that?
W3 Schools Tryit
So I have a rotating pyramid which has text and a button on each face, but the buttons are unresponsive due to overlapping divs.
Is there any way to make the buttons clickable in the rotating pyramid ?
-sorry for the ugly code, it's only a prototype.
body {
padding-top: 230px;
}
.tetra {
position: relative;
transform-origin: 50% 56%;
width: 700px;
padding-bottom: 606.21px;
/* height of equilateral triangle = sin60° * width */
margin: 0 auto;
transform-style: preserve-3d;
transition: transform 1s;
}
.tetra div {
display: block;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: transparent;
transform-style: preserve-3d;
-webkit-clip-path: polygon(50% 0, 100% 100%, 0% 100%);
clip-path: polygon(50% 0, 100% 100%, 0% 100%);
}
/* Rotation of –109.5° is angle(C, M[AB], D), per http://www.f-lohmueller.de/pov_tut/geo/geom_200e.htm, 180° – atan(2 * sqrt(2)) ≈ 109.5° */
.tetra .face2 {
transform-origin: 0% 100%;
transform: rotate(-60deg) rotatex(-109.5deg);
background: rgb(190, 0, 0);
}
.tetra .face3 {
transform-origin: 100% 100%;
transform: rotate(60deg) rotatex(-109.5deg);
background-color:blue;
}
.tetra .face4 {
transform-origin: 50% 100%;
transform: rotate(180deg) rotatex(-109.5deg);
background: rgb(76, 190, 0);
}
.tetra .face4 p {
transform: scale(-1, 1);
top: 60%;
left: 50%;
text-align: center;
width: 93px;
margin: 0px;
position: relative;
font-size: 30px;
position: relative;
font-weight: bold;
}
.tetra .face2 p {
transform: scale(-1, 1);
font-size: 30px;
text-align: center;
top: 60%;
left: 50%;
position: relative;
width: 93px;
margin: 0px;
font-weight: bold;
}
.tetra .face3 p {
transform: scale(-1, 1);
font-size: 30px;
text-align: center;
top: 60%;
left: 45%;
position: relative;
width: 40%;
font-weight: bold;
}
#keyframes rotate {
0% {
transform: rotatex(90deg) rotateY(0deg) rotatez(0deg);
}
100% {
transform: rotatex(90deg) rotateY(0deg) rotatez(360deg);
}
}
<html>
<head>
<link rel="stylesheet" href="pyramid.css">
</head>
<div style="height:100px;" onmousedown="spin()">
<div class="tetra">
<div class="face1"></div>
<div class="face2">
<p>TEXTFACE2<button onclick="window.Open(cube.html)">buttonface2</button></p>
</div>
<div class="face4">
<p>TEXTFACE3<button onclick="window.Open(cube.html)">buttonface4</button></p>
</div>
<div class="face3" "><p>TEXTFACE3<button onclick="window.Open(cube.html) "">buttonface3</button>
</p>
</div>
</div>
</div>
<script>
let rotation = 0;
function spin() {
const collection = document.getElementsByClassName("tetra");
collection[0].style.transform = "rotatex(90deg) rotateY(0deg) rotateZ(" + rotation + "deg)";
rotation = rotation + 120;
}
</script>
<style>
.tetra {
transform: rotatex(90deg) rotateY(0deg) rotatez(0deg);
}
</style>
</html>
I try to do an animated box that will unfold itself on the page load.
I have a panel lifted up. I try to rotate it 90deg to the ground, and after, I would want to lift it up again based on the other edge 90deg.
I tried to change the origin (transform-origin: top) but it change the origin to the original origin. I had to add 2 translation to position it at the right place but it create a bump. The edge on the ground don't stick on the ground.
Here's my current fiddle: https://jsfiddle.net/hbnta1uj/2/
I also tried without modifying the origin but I still get a bump:
#keyframes slideFront2 {
0% {
transform: rotateX(-0deg);
}
50% {
transform: rotateX(-90deg);
}
100% {
transform: rotateX(-180deg) translateZ(-100px) translateY(100px);
}
}
I have another idea where I position the second panel flat already and I hide it (opacity 0) and at 50% when the first panel is flat I show the second and just 90deg it.
But I would want to know for more complex animations if there's a way to do it the way I describe it, by always start at the new position the new transformation?
Thanks
I would consider an animation on the container to make it easier where you only need one keyframe:
* {
box-sizing: border-box;
}
body {
margin: 0;
}
.container {
width: 90%;
margin: auto;
height: 100vh;
background-color: rgb(194, 194, 194);
}
.progressbar-wrapper {
width: 300px;
height: 100px;
top: calc(50% - 50px);
left: calc(50% - 150px);
position: absolute;
transform-style: preserve-3d;
transform: rotateX(-20deg) rotateY(-30deg);
}
.progressbar {
width: 100%;
height: 100%;
transform-origin: bottom;
animation: 0.5s ease-out 1 slideFront forwards;
transform-style: preserve-3d;
}
.side {
width: 100%;
height: 100%;
background-color: rgba(254, 254, 254, 0.3);
position: absolute;
top: 0;
left: 0;
}
#keyframes slideFront {
100% {
transform: rotateX(-90deg);
}
}
.bottom {
box-shadow: 10px 10px 50px 5px rgba(90, 90, 90, 0.7);
}
.back {
animation: 1s ease-out 0.5s 1 slideFront forwards;
transform-origin: top;
}
<div class="container">
<div class="progressbar-wrapper">
<div class="progressbar">
<div class="side back">
</div>
<div class="side bottom">
</div>
</div>
</div>
</div>
The thing I discover is that the order matter in the translate function. Everything is executed left to right so the origin of the rotation will be relative to the current position of the element if you execute everything left to right (Here's 2 boxes getting the same rotation translation but the order differ: https://codepen.io/anon/pen/oOQGPp)
So in my example, if you do:
50.001% {
transform: rotateX(90deg) translateZ(00px) translatey(100px) ;
transition-timing-function: linear;
}
100% {
transform: rotateX(0deg) translateZ(100px) translatey(00px) ;
transition-timing-function: linear;
}
The rotation will be applied before the translation so the rotation origin will not be the bottom line after the translation but it will be the position without the origin based on the translated part (So it will be the 0% position origin.) CSS will fail to make the path of the animation and it will add a little bump.
But if you do transform: TRANSLATE ROTATE, the rotation will be applied after the translation so the origin of the rotation will be related to the position with the translation. This is how I was able to rotate the item without getting the little bump bug.
Here's the full fixed css. You can run it in my original jsfiddle to see the result
* {
box-sizing: inherit;
margin: 0;
}
html {
box-sizing: border-box;
}
.container {
width: 90%;
margin: auto;
height: 100vh;
background-color: rgb(194, 194, 194);
}
.progressbar-wrapper {
width: 300px;
height: 100px;
top: 50%;
left: 50%;
position: absolute;
transform: translate(-50%, -50%);
}
.progressbar {
width: 100%;
height: 100%;
transform-style: preserve-3d;
transform: rotateX(-20deg) rotateY(-30deg);
}
.side {
width: 100%;
height: 100%;
background-color: rgba(254, 254, 254, 0.3);
position: absolute;
top: 0;
left: 0;
}
#keyframes slideBottom {
0% {
transform: rotateX(-0deg);
}
100% {
transform: rotateX(-90deg);
}
}
#keyframes slideFront {
0% {
transform: rotateX(-0deg);
}
50% {
transform: rotateX(-90deg);
}
50.001% {
transform: translateZ(100px) rotateX(90deg);
transition-timing-function: linear;
}
100% {
transform: translateZ(100px) rotateX(0deg) ;
transition-timing-function: linear;
}
}
.bottom {
animation: 0.5s ease-out 0s 1 slideBottom forwards;
box-shadow: 10px 10px 50px 5px rgba(90, 90, 90, 0.7);
transform-origin: bottom;
}
.back {
animation: 1s ease-out 0s 1 slideFront forwards;
transform-origin: bottom;
}
I'm trying to get this circle loader working properly but having difficulty. I can do some basic animations, but this code which I found on CodePen is a bit above my pay-grade. I'm trying to use it to understand what's happening.
My objective is that the loader doesn't go all the way around the circumference of the circle. Say, only 68% of the way and stops. Or 98%. But I'm thus far unable to locate the property/value which determines how far the loader goes around the circle.
I've tried manipulating the keyframes on the right loader class to no avail as well as the transform-origin property. No dice.
Code:
#circle-loader-wrap {
overflow: hidden;
position: relative;
margin-top: -10px;
width: 200px;
height: 200px;
box-shadow: 0px 0px 20px rgba(0, 0, 0, 0.1) inset;
background-color: blue;
border-radius: 200px;
-ms-transform: rotate(180deg);
-webkit-transform: rotate(180deg);
-moz-transform: rotate(180deg);
transform: rotate(180deg);
}
#circle-loader-wrap:after {
content: '';
position: absolute;
left: 15px;
top: 15px;
width: 170px;
height: 170px;
border-radius: 50%;
background-color: green;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
}
#circle-loader-wrap div {
overflow: hidden;
position: absolute;
width: 50%;
height: 100%;
}
#circle-loader-wrap .loader {
position: absolute;
left: 100%;
top: 0;
width: 100%;
height: 100%;
border-radius: 1000px;
background-color: pink;
}
#circle-loader-wrap .left-wrap {
left: 0;
}
#circle-loader-wrap .left-wrap .loader {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
transform-origin: 0 50% 0;
-webkit-transform-origin: 0 50% 0;
animation: loading-left 20s infinite linear;
-webkit-animation: loading-left 20s infinite linear;
}
#circle-loader-wrap .right-wrap {
left: 50%;
}
#circle-loader-wrap .right-wrap .loader {
left: -100%;
border-bottom-right-radius: 0;
border-top-right-radius: 0;
transform-origin: 100% 50% 0;
-webkit-transform-origin: 100% 50% 0;
animation: loading-right 20s infinite linear;
-webkit-animation: loading-right 20s infinite linear;
}
#keyframes loading-left {
0% {
transform: rotate(0deg);
}
25% {
transform: rotate(180deg);
}
50% {
transform: rotate(180deg);
}
75% {
transform: rotate(180deg);
}
100% {
transform: rotate(180deg);
}
}
#-webkit-keyframes loading-left {
0% {
-webkit-transform: rotate(0deg);
}
25% {
-webkit-transform: rotate(180deg);
}
50% {
-webkit-transform: rotate(180deg);
}
75% {
-webkit-transform: rotate(180deg);
}
100% {
-webkit-transform: rotate(180deg);
}
}
#keyframes loading-right {
0% {
transform: rotate(0deg);
}
25% {
transform: rotate(0deg);
}
50% {
transform: rotate(180deg);
}
75% {
transform: rotate(180deg);
}
100% {
transform: rotate(180deg);
}
}
#-webkit-keyframes loading-right {
0% {
-webkit-transform: rotate(0deg);
}
25% {
-webkit-transform: rotate(0deg);
}
50% {
-webkit-transform: rotate(180deg);
}
75% {
-webkit-transform: rotate(180deg);
}
100% {
-webkit-transform: rotate(180deg);
}
}
<div class="container mt-5">
<div class="row">
<div class="col-3">
<div id="circle-loader-wrap">
<div class="left-wrap">
<div class="loader"></div>
</div>
<div class="right-wrap">
<div class="loader"></div>
</div>
</div>
</div>
</div>
</div>
I am pasting a snippet below which does what you want.
I have written my explanation of what's going on directly into the code comments next to the css rules that are doing the corresponding animation.
In case anything is still unclear, post a comment.
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<style>
#circle-loader-wrap {
overflow: hidden;
position: relative;
margin-top: -10px;
width: 200px;
height: 200px;
box-shadow: 0px 0px 20px rgba(0, 0, 0, 0.1) inset;
background-color: blue;
border-radius: 200px;
transform: rotate(180deg);
}
#circle-loader-wrap:after {
content: '';
position: absolute;
left: 15px;
top: 15px;
width: 170px;
height: 170px;
border-radius: 50%;
background-color: green;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
}
#circle-loader-wrap div {
overflow: hidden;
position: absolute;
width: 50%;
height: 100%;
}
#circle-loader-wrap .loader {
position: absolute;
left: 100%;
top: 0;
width: 100%;
height: 100%;
border-radius: 1000px;
background-color: pink;
}
#circle-loader-wrap .left-wrap {
left: 0;
}
#circle-loader-wrap .left-wrap .loader {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
transform-origin: 0 50% 0;
animation: loading-left 5s infinite linear;
}
#circle-loader-wrap .right-wrap {
left: 50%;
}
#circle-loader-wrap .right-wrap .loader {
left: -100%;
border-bottom-right-radius: 0;
border-top-right-radius: 0;
transform-origin: 100% 50% 0;
animation: loading-right 5s infinite linear;
}
#keyframes loading-left {
0% {
transform: rotate(0deg);
}
25%, 100% {
transform: rotate(180deg);
}
}
#keyframes loading-right {
0%, 25% {
transform: rotate(0deg);
}
50%, 100% {
/* the following is for the second half of the cicrle */
/* 180deg means one half of the cicle or 50% of the cicle */
/* So, 1% is gonna be 180/50 = 3.6deg */
/* If you want 68%, then you have 18% left for the second half of the circle */
/* To get 18%: 18x3.6 = 64.8deg */
transform: rotate(64.8deg);
/* Note: The transformation will happen between 25% and 50% of the total time which is 5 seconds in this case; So, it's gonna take 1.25 seconds. */
/* In other words, it will take the same amount of time as for the first half of the circle which will make the transformation in the second half appear to be slower because it has the same time to cover a much shorter distance */
/* Between 50% and 100% nothing happens. */
/* That's your "pause" in this animation although technically it's not a pause. */
}
}
</style>
<div class="container mt-1">
<div class="row">
<div class="col">
<p>68% in this case:</p>
<div id="circle-loader-wrap">
<div class="left-wrap">
<div class="loader"></div>
</div>
<div class="right-wrap">
<div class="loader"></div>
</div>
</div>
<p>The comments next to the corresponding css rules show how to adjust.</p>
</div>
</div>
</div>
Also note: I ripped out the vendor prefixes because you don't really need those nowadays for those css rules.
I am running into an issue in my mobile media query - anything under a 640px viewport. I have a circle that comes together and forms a full circle (see snippet), but for some reason in my media query, the circle doesn't quite line up, and I am unsure why as I am using the same math that makes it work in a desktop version.
Here is what it looks like within the 640 media query:
So how this works is I give .circle the same height and width. So let's say 200px for both height and width.
Then the class of .spinner, I divide the height and width of the .circle by two. So I would have 125px for height and width.
Then I set the border size, so lets use 5px. What I do is add that border size to the height and width numbers of .spinner and use that figure, which would be 130px to everything else ranging from .top, .bottom, q2, mask, etc.
That is how I get this to work and my math in my media query is not wrong. Does anyone see why this isn't lining up?
.blue {
background-color: blue;
width: 100%;
height: 600px;
}
.circle {
z-index: 99;
width: 500px;
height: 500px;
position: absolute;
background: inherit;
top: 50%;
left: 50%;
-webkit-transform: translate(-50%, -50%) rotate(0deg);
transform: translate(-50%, -50%) rotate(0deg);
}
.spinner {
width: 250px;
height: 250px;
position: absolute;
border: 5px solid #b5f2ff;
z-index: 10;
}
.top {
top: 255px;
left: 255px;
border-radius: 0 0 255px 0;
border-left: none;
border-top: none;
-webkit-transform-origin: top left;
transform-origin: top left;
}
.bottom {
border-radius: 255px 0 0 0;
border-bottom: none;
border-right: none;
-webkit-transform-origin: bottom right;
transform-origin: bottom right;
}
.topright,
.bottomleft {
-webkit-animation: rotate90 4s linear forwards;
animation: rotate90 4s linear forwards;
}
.topleft,
.bottomright {
-webkit-animation: rotate180 4s linear forwards;
animation: rotate180 4s linear forwards;
}
.mask {
width: 255px;
height: 255px;
position: absolute;
opacity: 1;
background: inherit;
z-index: 15;
-webkit-animation: mask 4s linear forwards;
animation: mask 4s linear forwards;
}
.q2 {
top: 0;
left: 0;
}
.q4 {
top: 255px;
left: 255px;
}
#-webkit-keyframes rotate90 {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
20%,
80% {
-webkit-transform: rotate(-90deg);
transform: rotate(-90deg);
}
100% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
}
#keyframes rotate90 {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
20%,
80% {
-webkit-transform: rotate(-90deg);
transform: rotate(-90deg);
}
100% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
}
#-webkit-keyframes rotate180 {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
40%,
60% {
-webkit-transform: rotate(-180deg);
transform: rotate(-180deg);
}
100% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
}
#keyframes rotate180 {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
40%,
60% {
-webkit-transform: rotate(-180deg);
transform: rotate(-180deg);
}
100% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
}
#-webkit-keyframes mask {
0% {
z-index: 15
}
40%,
60% {
z-index: 4
}
100% {
z-index: 15
}
}
#keyframes mask {
0% {
z-index: 15
}
40%,
60% {
z-index: 4
}
100% {
z-index: 15
}
}
#circle-text {
display: none;
position: absolute;
color: #FFF;
font-size: 2.3em;
text-align: center;
top: 50%;
left: 50%;
-webkit-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
z-index: 100;
}
#media screen and (max-width:640px) {
.circle {
z-index: 100;
width: 250px;
height: 250px;
-webkit-transform: translate(-50%, -50%) rotate(0deg);
transform: translate(-50%, -50%) rotate(0deg);
}
.spinner {
width: 125px;
height: 125px;
z-index: 10;
}
.top {
top: 130px;
left: 130px;
border-radius: 0 0 130px 0;
}
.bottom {
border-radius: 130px 0 0 0;
}
.mask {
width: 130px;
height: 130px;
}
.q4 {
top: 130px;
left: 130px;
}
}
<div class="blue">
<div class="circle">
<div class="spinner top topright"></div>
<div class="spinner top topleft"></div>
<div class="spinner bottom bottomleft"></div>
<div class="spinner bottom bottomright"></div>
<div class="mask q2"></div>
<div class="mask q4"></div>
</div>
</div>
You have an inconsistent use of box-sizing:border-box in your CSS. It's being used in media queries, so that it doesn't apply to all screen sizes. And it would mess up your calculations.