Preserve 3d of child element, as its parent spins - html

I have an element that rotates, and inside of it I have .pop-out-item. This has translateZ(500px) on it.
When the rotating element (.rotator--child) spins, the .pop-out-item stays "attached" to the rotating div (see code snippet)
#keyframes spin {
0% {
transform: rotateY(0deg);
}
100% {
transform: rotateY(360deg);
}
}
.rotator {
position: relative;
perspective: 500px;
}
.rotator--element {
display: block;
width: 300px;
background: red;
aspect-ratio: 1/ 1;
animation: spin 10s infinite;
}
.pop-out-item{
width: 50px;
aspect-ratio: 1 / 1;
background: orange;
transform: translateZ(500px);
transform-style: preserve-3d;
}
body{
padding: 60px;
}
<div class="rotator">
<div class="rotator--element">
<div class="pop-out-item"></div>
</div>
</div>
I expect it to look different, something like this:
Sorry for terrible drawing.
What am I doing wrong? Thanks.

I assume what you want is for the orange square to move with the red one, but translated. I moved the transform-style: preserve-3d to the red rotator--element and made the translateZ value 50px as it's easier to see that way. As the transform-style MDN documentation states:
The transform-style CSS property sets whether children of an element are positioned in the 3D space or are flattened in the plane of the element.
#keyframes spin {
0% {
transform: rotateY(0deg);
}
100% {
transform: rotateY(360deg);
}
}
.rotator {
position: relative;
perspective: 500px;
}
.rotator--element {
display: block;
width: 300px;
background: red;
aspect-ratio: 1/ 1;
animation: spin 10s infinite;
transform-style: preserve-3d;
}
.pop-out-item{
width: 50px;
aspect-ratio: 1 / 1;
background: orange;
transform: translateZ(50px);
}
body{
padding: 60px;
}
<div class="rotator">
<div class="rotator--element">
<div class="pop-out-item"></div>
</div>
</div>

Related

TranslateZ() is ignored for span element

The span element; the child of the div element in the snippet below is behaving unexpectedly. The span element's background-color is set to #0dd and it's transform properties are set to scale( 0.5 ) translateZ( 5rem ). The scale value is respected and shrinks the element to half size; the translateZ value however is ignored and doesn't change the position of the element.
The desired result is for the span element to be pushed forwards away from it's parent div. Why is translateZ ignored here and how can we translate span in the Z direction?
* {
box-sizing: border-box;
transform-style: preserve-3d;
margin: 0;
padding: 0;
}
html, body {
height: 100%;
}
body, span {
display: flex;
justify-content: center;
align-items: center;
}
body {
perspective: 30rem;
}
section, div, span {
position: absolute;
width: 10rem;
height: 10rem;
}
div, span {
opacity: 0.5;
background-color: #000;
}
div span {
transform: scale( 0.5 ) translateZ( 5rem );
background-color: #0dd;
}
<style>
.rotate_y {
animation-name: rotateY;
animation-duration: 10s;
animation-timing-function: linear;
animation-iteration-count: infinite;
}
#keyframes rotateY {
0% {
transform: rotateY( 0deg );
}
100% {
transform: rotateY( 360deg );
}
}
</style>
<section class='rotate_y'>
<div>
<span></span>
</div>
</section>
UPDATE: Seems to behave properly on IOS. Issue is with desktop Chrome.
The opacity seems to be the culprit but don't know why. It's probably a bug so use transparent background instead:
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
height: 100vh;
margin: 0;
perspective: 30rem;
display: flex;
justify-content: center;
align-items: center;
}
.rotate_y * {
display: block;
background-color: rgba(0,0,0,0.5);
width: 10rem;
height: 10rem;
transform-style: preserve-3d;
}
.rotate_y span {
transform: scale( 0.5) translateZ( 5rem);
background-color: rgba(0,221,221,0.5);
}
.rotate_y {
transform-style: preserve-3d;
animation-name: rotateY;
animation-duration: 10s;
animation-timing-function: linear;
animation-iteration-count: infinite;
}
#keyframes rotateY {
0% {
transform: rotateY( 0deg);
}
100% {
transform: rotateY( 360deg);
}
}
<section class='rotate_y'>
<div>
<span></span>
</div>
</section>

Two divs orbiting around a center CSS animation

I am attempting to "orbit" two separate divs in circular motion around a center, however I am having trouble getting the two divs to follow the same circular path in my CSS animation.
.container {
width: 500px;
height: 500px;
display: flex;
align-items: center;
justify-content: center;
}
.box {
background-color: pink;
width: 100px;
height: 100px;
animation: battle 6s linear infinite;
position: absolute;
margin: 10px;
}
#keyframes battle {
from {
transform: rotate(0deg) translateX(150px) rotate(0deg);
}
to {
transform: rotate(360deg) translateX(150px) rotate(-360deg);
}
}
<div class="container">
<div class="box">
</div>
<div class="box">
</div>
</div>
Jsfiddle
Let your parent element be the guide;
When the goal is to rotate in a consistent spacing around a center (as opposed to say an "elliptical orbit" that is more of an oval pattern) than the easiest technique is to provide a parent to set a consistent boundary and attach children within it to use its position as their animation path. The goal is to just supply an illusion of individual elements orbiting in sync when in reality just one is rotating with its default transform-origin of center acting as the guide for the children "orbiting" within it.
In our case we took a parent whose equal circumference is roughly the size of the "orbit desired" and we gave it a border-radius of 50% to create a circle. This makes no point on the element less than or greater distance from the other. We make it a position: relative element so that we can apply position: absolute to any children of it. In this example we use pseudo elements but they could just as easily be additional DOM node elements like divs.
By fixing our children to specific points on the parent we create the equal distance from the X/Y of the parent's transform-origin center we desire and apply the rotate transform to spin the parent. However if we only did that then the children would also follow that path and not keep vertical (as it is assumed is desired) so we simply re-use the same animation applied to the parent but in reverse to offset its rotation. The result is a parent element spinning one direction, and the children in the other to create the effect seen in the example. Hope this helps!
#rotator {
position: relative;
width: 7rem;
height: 7rem;
animation: rotations 6s linear infinite;
border: 1px orange dashed;
border-radius: 50%;
margin: 3rem;
}
#rotator:before, #rotator:after {
position: absolute;
content: '';
display: block;
height: 3rem;
width: 3rem;
animation: inherit;
animation-direction: reverse;
}
#rotator:before {
background-color: red;
top: -.25rem;
left: -.25rem;
}
#rotator:after {
background-color: green;
bottom: -.25rem;
right: -.25rem;
}
#keyframes rotations {
to { transform: rotate(360deg) }
}
<div id="rotator"></div>
Something I did many years ago might be close to what you are looking for:
// Base
body {
background: #252525;
}
// Keyframes
#keyframes rotateClockwise {
100% {
transform: rotate(360deg);
}
}
#keyframes rotateCounterClockwise {
100% {
transform: rotate(-360deg);
}
}
// Ring
.ring {
position: relative;
left: 50%;
top: 50px;
margin-left: -100px;
height: 200px;
width: 200px;
border: 10px solid #666;
border-radius: 50%;
}
// Dots
.dot {
position: absolute;
height: 250px;
width: 40px;
top: -25px;
left: 50%;
margin-left: -20px;
&:before {
display: block;
content: '';
height: 40px;
width: 40px;
border-radius: 50%;
box-shadow: 0 2px 3px rgba(0,0,0,.1);
}
}
.dot--one {
animation: rotateClockwise 4s linear infinite;
&:before {
background: #e6a933;
}
}
.dot--two {
animation: rotateCounterClockwise 2s linear infinite;
&:before {
background: #e63348;
}
}
.dot--three {
animation: rotateClockwise 7s linear infinite;
&:before {
background: #70b942;
}
}
.dot--four {
animation: rotateCounterClockwise 12s linear infinite;
&:before {
background: #009ee3;
}
}
<div class="ring">
<div class="dot dot--one"></div>
<div class="dot dot--two"></div>
<div class="dot dot--three"></div>
<div class="dot dot--four"></div>
</div>
https://codepen.io/seanstopnik/pen/93f9cbcbcf9b38684bfc75f38c9c4db3

How to do 2+ rotations while resetting the origin between each during a keyframe?

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;
}

Can a nested div ignore the hover of a parent

If a div is nested inside of another, can the nested div ignore the hover of the parent. Here's an example
.Box {
width: 50px;
height: 50px;
background: red;
}
.Circle {
width: 20px;
height: 20px;
background: blue;
border-radius: 20px;
}
.Box:hover {
animation: expand .5s normal forwards;
}
#keyframes expand {
0% {
transform: scale(1);
}
100% {
transform: scale(1.6);
}
}
<div class="Box">
<div class="Circle"></div>
</div>
In this example would there be a way to make the Box expand but not the Circle
Technically the parent hover event doesn't get applied to the child.
But in your case the child is still effected, because you're scaling the parent. And thus everything inside of the parent is being scaled too.
In order to counter the scaling of the nested div, you can apply a reverse scaling effect when the parent div is hovered.
.Box{
width: 50px;
height: 50px;
background: red;
}
.Circle{
width: 20px;
height: 20px;
background: blue;
border-radius: 20px;
}
.Box:hover{
animation: expand .5s normal forwards;
}
.Box:hover .Circle {
animation: contract .5s normal forwards;
}
#keyframes expand {
0% {
transform: scale(1);
}
100% {
transform: scale(1.6);
}
}
#keyframes contract {
0% {
transform: scale(1);
}
100% {
transform: scale(0.625); /* 1 / 1.6 */
}
}
<div class="Box">
<div class="Circle"></div>
</div>
Because you are scaling the parent, everything inside it will be impacted. An alternative solution is to have a different sibling to the circle and apply the animation on that.
CSS:
.Box {
width: 50px;
height: 50px;
background: red;
}
.Circle {
width: 20px;
height: 20px;
background: blue;
border-radius: 20px;
position: absolute;
top: 10px;
left: 10px;
}
.Container {
position: relative;
}
.Box:hover {
animation: expand .5s normal forwards;
}
#keyframes expand {
0% {
transform: scale(1);
}
100% {
transform: scale(1.6);
}
}
HTML:
<div class="Container">
<div class="Box">
</div>
<div class="Circle"></div>
</div>
Demo: http://jsfiddle.net/lotusgodkk/GCu2D/2157/
Here, the circle is positioned so that it's position is not affected by the box

Place one div under another div in this code sample [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I am playing around with this code on codepen and I am trying to place text, under the animated circle and centered in the viewport, but I cannot seem to find a way to do it. I have set background: yellow; on the text for visibility.
If you know why the solution works, it would be immensely helpful if you could explain it here for me to understand/learn.
Try this: https://codepen.io/Lansana/pen/ezvVYR
HTML:
<div class="spinner-wrapper">
<div class='spinner'>
<div class='quadrant'></div>
<div class='quadrant'></div>
<div class='quadrant'></div>
<div class='quadrant'></div>
</div>
<div class='text'>test</div>
</div>
CSS:
html,
body {
height: 90%;
}
body {
background: #c2c2c2;
display: flex;
align-items: center;
justify-content: center;
}
.text {
background: yellow;
text-align: center;
}
.spinner-wrapper {
width: auto;
height: auto;
position: relative;
}
.spinner {
width: 300px;
height: 300px;
min-width: 300px;
position: relative;
animation: spin 60s linear infinite;
//border-radius: 300px;
.quadrant {
position: absolute;
top: 0%;
left: 0%;
width: 100%;
height: 100%;
//z-index: 10;
mix-blend-mode: multiply;
//opacity: .5;
&:after {
content: "";
color: none;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border-radius: 100%;
}
&:nth-child(1) {
animation: slide_horiz_neg 12s linear alternate infinite;
&:after {
//mix-blend-mode: multiply;
//opacity: .5;
background: cyan;
}
}
&:nth-child(2) {
animation: slide_vert_neg 8s linear alternate infinite;
&:after {
//mix-blend-mode: multiply;
//opacity: .5;
background: yellow;
}
}
&:nth-child(3) {
animation: slide_horiz_pos 10s linear alternate infinite;
&:after {
//mix-blend-mode: multiply;
//opacity: .5;
background: magenta;
}
}
/* &:nth-child(4) {
// animation: slide_vert_pos 3.5s linear alternate infinite;
&:after {
mix-blend-mode: normal;
//opacity: .5;
background: #000000;
}
} */
}
}
#keyframes spin {
to {
transform: rotate(360deg);
}
}
#keyframes slide_vert_pos {
0% {
transform: translateY(0%);
}
100% {
transform: translateY(1%);
}
}
#keyframes slide_vert_neg {
0% {
transform: translateY(0%);
}
100% {
transform: translateY(-1%);
}
}
#keyframes slide_horiz_pos {
0% {
transform: translateX(0%);
}
100% {
transform: translateX(1%);
}
}
#keyframes slide_horiz_neg {
0% {
transform: translateX(0%);
}
100% {
transform: translateX(-1%);
}
}
I created a wrapper, which contains your spinner and the text.
The wrapper has an auto height/width, based on it's child elements.
The text can be positioned any way you want within that wrapper, and it is not effected at all by the spinner except for the order in which the two are placed within the dom.
if you use flex, then apply it on HTML so body can shrink on content. margin-left:-50%; will be efficient and can be used to center one element.
For vertical-align, you may use (either) display : inline-block/inline-table + vertical-align:middle in order to center 2 elements being side by sides.
basicly, your CSS template can become
html {
height: 90%;
background: #c2c2c2;
display: flex;
align-items: center;
justify-content: center;
}
body {margin:0;}
.text {
background: yellow;
display: inline-table;/* or inline-block to vertical align */
vertical-align: middle;
margin-left: -50%;/* body flex-child takes width of content, not window ;) */
position: relative;/* bring it up front , add z-index too if needed */
}
.spinner {
display: inline-block;/* not a flex-child anymore & float doesn't allow vertical-align */
vertical-align: middle;/* says itself */
width: 300px;
height: 300px;
min-width: 300px;
position: relative;
animation: spin 60s linear infinite;
}
... and render -> https://codepen.io/anon/pen/qNrxPQ
Modified your code you can have look at codepen
https://codepen.io/anon/pen/KMWZOM
HTML
<div class="wrapper">
<div class='spinner'>
<div class='quadrant'></div>
<div class='quadrant'></div>
<div class='quadrant'></div>
<div class='quadrant'></div>
</div>
<div class='text'>test</div>
</div>
CSS
html,
body {
height: 90%;
}
.wrapper{
position:relative;
margin:0 auto;
}
body {
background: #c2c2c2;
display: flex;
align-items: center;
justify-content: center;
}
.text {
position: absolute;
background: yellow;
top:50%;
left:50%;
}
.spinner {
width: 300px;
height: 300px;
min-width: 300px;
position: relative;
animation: spin 60s linear infinite;
//border-radius: 300px;
.quadrant {
position: absolute;
top: 0%;
left: 0%;
width: 100%;
height: 100%;
//z-index: 10;
mix-blend-mode: multiply;
//opacity: .5;
&:after {
content: "";
color: none;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border-radius: 100%;
}
&:nth-child(1) {
animation: slide_horiz_neg 12s linear alternate infinite;
&:after {
//mix-blend-mode: multiply;
//opacity: .5;
background: cyan;
}
}
&:nth-child(2) {
animation: slide_vert_neg 8s linear alternate infinite;
&:after {
//mix-blend-mode: multiply;
//opacity: .5;
background: yellow;
}
}
&:nth-child(3) {
animation: slide_horiz_pos 10s linear alternate infinite;
&:after {
//mix-blend-mode: multiply;
//opacity: .5;
background: magenta;
}
}
/* &:nth-child(4) {
// animation: slide_vert_pos 3.5s linear alternate infinite;
&:after {
mix-blend-mode: normal;
//opacity: .5;
background: #000000;
}
} */
}
}
#keyframes spin {
to {
transform: rotate(360deg);
}
}
#keyframes slide_vert_pos {
0% {
transform: translateY(0%);
}
100% {
transform: translateY(1%);
}
}
#keyframes slide_vert_neg {
0% {
transform: translateY(0%);
}
100% {
transform: translateY(-1%);
}
}
#keyframes slide_horiz_pos {
0% {
transform: translateX(0%);
}
100% {
transform: translateX(1%);
}
}
#keyframes slide_horiz_neg {
0% {
transform: translateX(0%);
}
100% {
transform: translateX(-1%);
}
}
You can also try this:
.text {
position: relative;
background: yellow;
right: 150px;
top: 200px;
}
Since your spinner's circumference was 300px, to center it directly below, I divided it by half and assigned that position to the text to center it as well as any number above 150px in order to settle below the circle. Remember, these positions act inverted. Right moves it left and so on.