css3 transform-origin issue in 3D cube - html

I have created css-cube and its rotating on :hover.
But Its rotation is based on one side of cube!
I want to rotate it from its center, like in this example. I was trying transform-origin property but not got desired result.
I have also tried placing one middle plane inside cube but hover is not working in that situation!
.contain {
width: 300px;
height: 300px;
-webkit-perspective: 500px;
perspective: 500px;
position: absolute;
}
.main {
position:relative;
width:100px;
height:100px;
margin:100px 100px;
background:#07a;
overflow:visible;
transition: all linear,transform cubic-bezier(0.4, 0.25, 0.14, 1.5),background cubic-bezier(0.4, 0.25, 0.14, 1.5);
transition-duration: 700ms;
-moz-transform-style: preserve-3d;
-webkit-transform-style: preserve-3d;
transform-style: preserve-3d;
transform-origin: center center;
}
.main:hover{
transform:rotateY(180deg);
}
.top, .right, .left, .bottom,.lid{
position:absolute;
width:100px;
height:100px;
z-indexd:999;
transition: all 1s ease;
}
.top {
background:crimson;
top:-100px;
transform-origin : 50% 100%;
transform:rotateX(-90deg);
}
.bottom {
background:crimson;
bottom:-100px;
transform-origin :100% 0%;
transform:rotateX(90deg);
}
.left {
background:#ccc;
left:-100px;
transform-origin :100% 0%;
transform:rotateY(90deg);
}
.right {
background:#ccc;
right:-100px;
transform-origin : 0% 0%;
transform:rotateY(-90deg);
}
.lid {
background:#07a;
transform: translateZ(170px);
transform-origin : 0% 0%;
transform:translateZ(100px);
}
<div class="contain">
<div class="main">
<div class="lid"></div>
<div class="top"></div>
<div class="right"></div>
<div class="left"></div>
<div class="bottom"></div>
</div>
</div>

The problem is that you need to set the transform origin in the center of the cube, and the cube is a 3d element. You are missing the 3rd dimension !
So it should be
transform-origin: center center 50px;
since your cube side is 100px
.contain {
width: 300px;
height: 300px;
-webkit-perspective: 500px;
perspective: 500px;
position: absolute;
}
.main {
position:relative;
width:100px;
height:100px;
margin:100px 100px;
background:#07a;
overflow:visible;
transition: all linear,transform cubic-bezier(0.4, 0.25, 0.14, 1.5),background cubic-bezier(0.4, 0.25, 0.14, 1.5);
transition-duration: 700ms;
-moz-transform-style: preserve-3d;
-webkit-transform-style: preserve-3d;
transform-style: preserve-3d;
transform-origin: center center 50px;
}
.main:hover{
transform:rotateY(180deg);
}
.top, .right, .left, .bottom,.lid{
position:absolute;
width:100px;
height:100px;
z-indexd:999;
transition: all 1s ease;
}
.top {
background:crimson;
top:-100px;
transform-origin : 50% 100%;
transform:rotateX(-90deg);
}
.bottom {
background:crimson;
bottom:-100px;
transform-origin :100% 0%;
transform:rotateX(90deg);
}
.left {
background:#ccc;
left:-100px;
transform-origin :100% 0%;
transform:rotateY(90deg);
}
.right {
background:#ccc;
right:-100px;
transform-origin : 0% 0%;
transform:rotateY(-90deg);
}
.lid {
background:#07a;
transform: translateZ(170px);
transform-origin : 0% 0%;
transform:translateZ(100px);
}
<div class="contain">
<div class="main">
<div class="lid"></div>
<div class="top"></div>
<div class="right"></div>
<div class="left"></div>
<div class="bottom"></div>
</div>
</div>

I added a translateZ to move the rotation axis, it looks a little more centered but still not like Desandro's ex.,
I read the documentation and I think you should checkout this! it explains a little bit about orgins and perspectives...
EDIT1:
integrated translateZ instead of transform origin (now it's perfect!!)
.contain {
width: 300px;
height: 300px;
-webkit-perspective:666px;
perspective: 666px;
position: absolute;
}
.main {
position:relative;
width:100px;
height:100px;
margin:100px 100px;
background:#07a;
overflow:visible;
transition: all 1s ease;
-moz-transform-style: preserve-3d;
-webkit-transform-style: preserve-3d;
transform-style: preserve-3d;
transform:translateZ(-50px)
}
.main:hover{
transform: translateZ(-50px) rotateY(180deg);
}
.top, .right, .left, .bottom,.lid,.front{
position:absolute;
width:100px;
height:100px;
z-index:999;
transition: all 1s ease;
}
.front{
background:yellow;
transform:rotateY( 0deg ) translateZ( 50px );
}
.left {
background:red;
transform:rotateY(90deg) translateZ( 50px );
}
.right {
background:purple;
right:-100px;
//transform-origin : 0% 0%;
transform:rotateY(-90deg) translateZ( 150px );
}
.lid {
background:green;
transform:rotateY(180deg) translateZ( 50px );
}
<div class="contain">
<div class="main">
<div class="front"></div>
<div class="lid"></div>
<div class="right"></div>
<div class="left"></div>
</div>
</div>
BTW CSS-transformations rock!!

I tried Adding "translateZ(-70px)" in the ".main:hover" and I think it's rotating centered.
With this will make when your cube is rotating make the translate some pixels to left and make feeling it's centered.

Related

Why does rotate transition work differently in animation?

In this code when, when rotating along top left point , the bar sways a lil bit down , while rotating
have seen this multiple time , sometimes it gets fixed , but i cannot spot the diff.
#keyframes rightup{
100%{
transition:transform 1s linear;
transform-origin:top left;
transform:rotate(-50deg);
}
}
.box{
position:absolute;
height:10px;
width:150px;
background-color:black;
top:50%;
left:50%;
border-radius:5px;
animation-name:rightup;
animation-duration:5s;
}
<div class="box"></div>
wanted something like this
.box{
position:absolute;
height:10px;
width:150px;
background-color:black;
top:50%;
left:50%;
border-radius:5px;
animation-name:rightup;
animation-duration:5s;
}
.box:hover{
transition:transform 1s linear;
transform-origin:top left;
transform:rotate(-50deg);
}
<div class="box"></div>
In the animation you aren't setting the transform-origin to the left top right from the start, but you are doing so in the transition/transform example.
Also, a minor point for such a slim bar but it makes a slight difference, you are rotating about the top of the bar rather than the (vertical) center.
This snippet changes both these things:
#keyframes rightup {
0% {
transform-origin: center left;
}
100% {
transform-origin: center left;
transform: rotate(-50deg);
}
}
.box {
position: absolute;
height: 10px;
width: 150px;
background-color: black;
top: 50%;
left: 50%;
border-radius: 5px;
animation-name: rightup;
animation-duration: 5s;
}
<div class="box"></div>

CSS Animation: `backface-visibility` causing cross-browser problems?

I'm developing a flip animation to show new numbers; it's much like an analog clock or calendar with the hinge in the middle.
The approach is straight forward: have a div with:
The bottom half of the first number on one side
The top half of the second number rotated 180 degrees so it's on the back
In order to show the new number, I rotate that whole div around the center of the container, revealing the back of the rotating div:
Number flip animation in latest Firefox
However, in Chrome, the animation doesn't always work. Sometimes half disappears completely until the transition animation is complete and sometimes the old number doesn't render: Number flip animation in latest Chrome with the bottom of the number not appearing till after animation is complete
In Safari 12, it's worse, it doesn't seem to respect backface-visibility, even with the -webkit- prefix:
Safari 12 Number animation, the bottom half of the first number is inverted after animation is complete
Pre-Chromium Edge handles this fine, but new (checked in v83) Edge has the same issue as Chrome.
I've tried messing around with the properties and have looked through other backface-visibility questions here.
Here's the code, hover over the numbers to see the flip:
body {
background: #2e517d;
}
.container {
width: 175px;
height: 192px;
background: #4e9bfa;
position: relative;
left: 50%;
transform: translate(-50%, 50%);
perspective: 1000px;
}
.cover {
width: 175px;
height: 50%;
position: absolute;
top: 96px;
background-color: #34b58c;
transform: rotateX(0deg);
transform-style: preserve-3d;
transform-origin: top;
transition: all 0.5s ease-out;
}
.container:hover .cover {
transform: rotateX(180deg);
}
.flip {
margin: 0;
display: block;
position: absolute;
width: 100%;
height: 100%;
backface-visibility: hidden;
}
.container p {
font-size: 1000%;
margin: 0;
}
.container>p {
height: 96px;
overflow: hidden;
}
.front-number-bottom {
position: relative;
height: 96px;
overflow: hidden;
background-color: red;
}
.front-number-bottom p {
margin: 0;
position: relative;
top: -96px;
}
.back-number-top {
position: relative;
height: 96px;
overflow: hidden;
}
.back-number-bottom {
height: 96px;
overflow: hidden;
position: relative;
z-index: -1;
}
.back-number-bottom p {
margin: 0;
position: relative;
top: -96px;
}
div.front {
background: red;
}
div.back {
background: green;
transform: rotateX(180deg);
}
<body>
<div class="container">
<p>76</p>
<div id="cover" class="cover">
<div class="flip front">
<div class="front-number-bottom">
<p>76</p>
</div>
</div>
<div class="flip back">
<div class="back-number-top">
<p>77</p>
</div>
</div>
</div>
<div class="back-number-bottom">
<p>77</p>
</div>
</div>
</div>
</body>
Is this a sound approach that can be easily fixed in Chromium browsers and Safari?
Would a different approach be better?
I guess your code is a bit complex. I would simplify your logic like below where you no more need backface-visibility: hidden;
Note the usage of two important things:
the mask that allow me to cut the element and show only 50% of the height (top or bottom). This will make the animation more realistic since each number will have both top and bottom part seperated.
the z-index trick where I apply a transtion that change the z-index exactly at the middle of the animation (when the rotations are at 90deg)1.
.card {
width: 175px;
height: 192px;
position: relative;
z-index:0;
left: 50%;
transform: translate(-50%, 50%);
font-size: 160px;
}
.card span,
.card span::before,
.card span::after {
position:absolute;
top:0;
left:0;
right:0;
bottom:0;
}
.card span {
position:absolute;
z-index:2;
perspective: 1000px;
}
.card span:first-child {
z-index:3;
transition:0s 0.25s all linear;
}
.card span::before,
.card span::after{
content:attr(data-number);
-webkit-mask:linear-gradient(#fff,#fff) top/100% 50% no-repeat;
mask:linear-gradient(#fff,#fff) top/100% 50% no-repeat;
background:red;
transition:0.5s all linear;
transform-style: preserve-3d;
}
.card span::after {
-webkit-mask-position:bottom;
mask-position:bottom;
background:green;
}
.card span:first-child::after {
transform: rotateX(0deg);
}
.card span:last-child::before {
transform: rotateX(-180deg);
}
/* Hover */
.card:hover span:first-child {
z-index:1;
}
.card:hover span:first-child::after {
transform: rotateX(180deg);
}
.card:hover span:last-child::before {
transform: rotateX(0deg);
}
<div class="card">
<span data-number="76"></span>
<span data-number="77"></span>
</div>
The mask can be replaced with clip-path too:
.card {
width: 175px;
height: 192px;
position: relative;
z-index:0;
left: 50%;
transform: translate(-50%, 50%);
font-size: 160px;
}
.card span,
.card span::before,
.card span::after {
position:absolute;
top:0;
left:0;
right:0;
bottom:0;
}
.card span {
z-index:2;
perspective: 1000px;
}
.card span:first-child {
z-index:3;
transition:0s 0.25s all linear;
}
.card span::before,
.card span::after{
content:attr(data-number);
clip-path:polygon(0 0,100% 0,100% 50%,0 50%);
background:red;
transition:0.5s all linear;
transform-style: preserve-3d;
}
.card span::after {
clip-path:polygon(0 50%,100% 50%,100% 100%,0 100%);
background:green;
}
.card span:first-child::after {
transform: rotateX(0deg);
}
.card span:last-child::before {
transform: rotateX(-180deg);
}
/* Hover */
.card:hover span:first-child {
z-index:1;
}
.card:hover span:first-child::after {
transform: rotateX(180deg);
}
.card:hover span:last-child::before {
transform: rotateX(0deg);
}
<div class="card">
<span data-number="76"></span>
<span data-number="77"></span>
</div>
Another optimization using counter and without setting an explicit width/height
.card {
margin:0 5px;
font-family:monospace;
display:inline-block;
text-align:center;
position: relative;
z-index:0;
font-size: 150px;
counter-reset:num calc(var(--n,1) - 1);
}
/* this will defined the height/width*/
.card::after {
content:counter(num);
visibility:hidden;
}
/**/
.card span,
.card span::before,
.card span::after {
position:absolute;
top:0;
left:0;
right:0;
bottom:0;
}
.card span {
z-index:2;
perspective: 1000px;
counter-increment:num;
}
.card span:first-child {
z-index:3;
transition:0s 0.25s all linear;
}
.card span::before,
.card span::after{
content:counter(num);
clip-path:polygon(0 0,100% 0,100% 50%,0 50%);
background:red;
transition:0.5s all linear;
transform-style: preserve-3d;
}
.card span::after {
clip-path:polygon(0 50%,100% 50%,100% 100%,0 100%);
background:green;
}
.card span:first-child::after,
.card:hover span:last-child::before{
transform: rotateX(0deg);
}
.card span:last-child::before {
transform: rotateX(-180deg);
}
.card:hover span:first-child::after {
transform: rotateX(180deg);
}
.card:hover span:first-child {
z-index:1;
}
<div class="card" style="--n:75">
<span></span><span></span>
</div>
<div class="card" style="--n:5">
<span></span><span></span>
</div>
<div class="card" style="--n:100">
<span></span><span></span>
</div>
1 When using linear it's pretty easy but it's more trick with other ease functions. Here is a related question that can help you identify the middfle of ease functions: When exactly does an ease animation reach its midpoint?

Enlarging Images on Flip Cards

I have been learning about flip cards and I have run into an issue. I have two cards that when they are flipped, there is a picture on the back. By moving the mouse a second time, the picture should enlarge. The issue I am having is when the image on the card to the left is enlarged, it is hidden behind the second card to the right. However, when the image on the card to the right is enlarged, it appears in front the first card. What I would like is for the picture to appear in front regardless as to what card is flipped. I have read that this is an issue with the z-index but I have been unsuccessful in figuring out where and how to set it.
The code for the web page is:
<div class = "center">
<div class="flip3d">
<div class="back"> <img class="exampleGif enlargeRight" src="https://images.pexels.com/photos/45170/kittens-cat-cat-puppy-rush-45170.jpeg?auto=compress&cs=tinysrgb&h=350"></div>
<div class="front" style="width:400% height:300%">
<p>Cats</p>
</div>
</div>
<div class="flip3d">
<div class="back"> <img class = "exampleGif enlargeLeft" src="https://images.pexels.com/photos/66898/elephant-cub-tsavo-kenya-66898.jpeg?auto=compress&cs=tinysrgb&h=350"></div>
<div class="front" style="width:400% height:300%">
<p>Elephant</p>
</div>
</div>
The css file is:
.flip3d{
width:400px;
height:300px;
margin: 10px;
float: left;
}
.flip3d > .front{
position: absolute;
transform: perspective(600px) rotateY(0deg);
width:400px;
height:300px;
border-radius: 7px;
background: #267326; /* was #FCO */
backface-visibility: hidden;
transition: transform .5s linear 0s;
font-size:1.5vw;
font-family: Arial, Helvetica, sans-serif;
font-style: oblique;
color: white;
text-align: center;
}
.flip3d > .back{
position: absolute;
transform: perspective(600px) rotateY(180deg);
width:400px;
height:300px;
border-radius: 7px;
background: #80BFFF;
backface-visibility: hidden;
transition: transform .5s linear 0s;
}
.flip3d:hover > .front{
transform: perspective(600px) rotateY(-180deg);
}
.flip3d:hover > .back{
transform: perspective(600px) rotateY(0deg);
}
img.exampleGif {
width:400px;
height:300px;
z-index:0;
position:relative;
}
.center {
margin: auto;
width: 50%;
border: none;
padding: 10px;
}
.enlargeRight:hover {
transform:scale(2,2);
transform-origin:0 0;
transition: all .5s;
}
.enlargeLeft:hover {
transform:scale(2,2);
transform-origin:right top;
transition: all .5s;
}
The code that I am using is located here.
Than you for your help.
Add z-index in the class below with the same value as I did. There is little doc to increase your knowledge.
EDIT: This doesn't need the value to equal 1 or 2. Just z-index in .back must be greater than in .front.
.flip3d > .back{
position: absolute;
transform: perspective(600px) rotateY(180deg);
width:400px;
height:300px;
border-radius: 7px;
background: #80BFFF;
backface-visibility: hidden;
transition: transform .5s linear 0s;
z-index: 1;
}

CSS transition width and height from center of div

I have elements with position: absolute; that I want to transition in certain situations. However, the origin of the width and height transition seems to depend on the top/bottom left/right values.
Is there any way to have more control over this?
I am specifically looking to transition from the center of the div.
Is there any solution that doesn't rely on transitioning also the top/bottom left/right values?
Edit:
I want to keep the width and height transitioning.
Thank you for the answers but using Transform scale is not a solution in this case. Percentages in the Transform property refer to the size of the element's border box, not the container. See for example this JSFiddle, how the end result of hovering over the two elements is different.
JSFiddle
div, span {
width:30%;
height:30%;
background:pink;
transition:all 1s ease;
position:absolute;
}
*:hover{
width:10%;
height:10%;
}
div{
top:10%;
left:10%;
}
span{
bottom:10%;
right:10%;
}
<div></div>
<span></span>
Well, you can always use transform - scale for that matter:
div {
background:pink;
width:200px;
height:200px;
transition:all 1s ease;
}
div:hover{
-webkit-transform: scale(0.1);
-ms-transform: scale(0.1);
transform: scale(0.1);
}
<div></div>
I would suggest using transform: translate when animating positions since it's better for performance and you can then control its origin with transform-origin.
And if you want to change the width or height you can similarly use transform: scale.
Say you want to double something in size from the center outwards. Then you'd just need to write transform: scale(2.0), since the default value of transform-origin is 50% 50%.
See example here: https://jsfiddle.net/ydpx284g/
You could change the position at the same time to simulate the effect. But I'm with the others: transform: scale is a better approach to this.
div, span {
width:30%;
height:30%;
background:pink;
transition:all 1s ease;
position:absolute;
}
div{
top:10%;
left:10%;
}
div:hover{
width:10%;
height:10%;
top: 20%;
left: 20%;
}
span{
bottom:10%;
right:10%;
}
span:hover{
width:10%;
height:10%;
bottom: 20%;
right: 20%;
}
<div></div>
<span></span>
Version with transforms:
div, span {
width:30%;
height:30%;
background: red;
transition: all 1s ease;
position: absolute;
}
div{
top:10%;
left:10%;
}
div:hover{
transform-origin: 50% 50%;
transform: scale(0.3);
}
span{
bottom:10%;
right:10%;
}
span:hover{
transform-origin: 50% 50%;
transform: scale(0.3);
}
<div></div>
<span></span>
The best way is using matrix, this allows you to combine transitions and transformations.
The matrix takes 6 arguments
transform: matrix(a, b, c, d, e, f);
Where
a= scale X axis
b= skewX
c= skewY
d= scale Y axis
e= position X
f= position Y
In this case, I set the scale on the X axis (which is going to alter the width) to the double of the initial width after hover. (the value 2 means initial scale times 2)
The scale on the Y axis is not altered (the value 1 means initial scale times 1, so the height won't change) The rest of the arguments are 0 because you don't need to use them in this case.
.example {
width: 30%;
height: 30%;
background-color: pink;
position: absolute;
top: 35%;
left: 35%;
transition: width, height, transform 1s;
}
.example:hover {
transform: matrix(2, 0, 0, 1, 0, 0);
}
<div class="example"></div>
Not sure if this is exactly what you're looking for, but this solution is not based on the transform: scale and you can manually set the desired width and height of your div on hover even in percentage.
And the percentage is relative to the width of the container.
HTML
<div id="container">
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
</div>
CSS
#container{
width: 400px;
height: 400px;
background: #eee;
position: relative;
}
.box{
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: #000;
width: 20%;
height: 20%;
transition: 0.3s;
cursor: pointer;
}
.box:hover{
width: 7%;
height: 10%;
}
.box:nth-child(2){
left: 20%;
}
.box:nth-child(3){
top: 20%;
}
.box:nth-child(4){
top: 20%;
left: 20%;
}
#container {
width: 400px;
height: 400px;
background: #eee;
position: relative;
}
.box {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: #000;
width: 20%;
height: 20%;
transition: 0.3s;
cursor: pointer;
}
.box:hover {
width: 7%;
height: 10%;
}
.box:nth-child(2) {
left: 20%;
}
.box:nth-child(3) {
top: 20%;
}
.box:nth-child(4) {
top: 20%;
left: 20%;
}
<div id="container">
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
</div>
You can try using transform: translate(-50%, -50%); see if that helps.
.example {
width: 30%;
height: 30%;
background: pink;
transition: all 1s ease;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.example:hover {
width: 10%;
height: 10%;
}
<div class="example"></div>
Is this what are you trying to achieve? using translate will change the position of the element based on the width and height of the element itself.
div, span {
width:30%;
height:30%;
background:pink;
transition:all 1s ease;
position:absolute;
transform: translate(-50%, -50%);
}
*:hover{
width:10%;
height:10%;
}
div{
top:25%;
left:25%;
}
span{
top:75%;
left:75%;
}
<div></div>
<span></span>

How to zoom in to a specific point smoothly with CSS?

I want to zoom in onto the pencil when hovered over the image.
HTML:
<div>
</div>
CSS:
div {
width: 400px;
height: 267px;
border: 1px solid black;
background-image: url('http://i.imgur.com/n9q7jhm.jpg');
background-size: 400px 267px;
transition: all 1s ease;
}
div:hover{
background-size: 500px 333px;
background-position: -60px -60px;
}
JSFiddle: http://jsfiddle.net/AX59Y/
My naive attempt was to increase the size and change the position of the image, however as you can see from the jsfiddle, the transition is very jagged as it tries to accomplish both transitions at the same time.
Is there a better way?
Take the answer from SW4 and change the left and top changes for a transform origin
#image {
background-image: url('http://i.imgur.com/n9q7jhm.jpg');
background-size: 400px 267px;
background-position:center;
transition: all 1s ease;
width:100%;
height:100%;
transform: scale(1);
position:relative;
left:0;
top:0;
-webkit-transform-origin: 75% 75%;
transform-origin: 75% 75%;
}
#wrapper:hover #image {
-webkit-transform: scale(2);
transform: scale(2);
}
The 75% 75% is more or less the position of the pencil , but you can set it to whatever you want.
fiddle
You can use the transition to scale up and reposition the image on hover, in order to do this you'll need to wrap the image div within a parent with overflow hidden.
Demo Fiddle
HTML
<div id='wrapper'>
<div id='image'></div>
</div>
CSS
#wrapper {
width: 400px;
height: 267px;
border: 1px solid black;
overflow:hidden;
position:relative;
}
#image {
background-image: url('http://i.imgur.com/n9q7jhm.jpg');
background-size: 400px 267px;
background-position:center;
transition: all 1s ease;
width:100%;
height:100%;
transform: scale(1);
position:relative;
left:0;
top:0;
}
#wrapper:hover #image {
transform: scale(2);
-webkit-transform: scale(2);
left:-150px;
top:-100px;
}
If you change your transition to linear it looks much less 'jagged'.
transition: all 1s linear;
Not sure if this is the behaviour that you want.