Trying to understand the relationship between these CSS transforms - html

I have the following CSS transforms and I am trying to understand the relationship between them so I can figure out how to compensate for one of them.
Here is my code:
.background {
height: 720px;
position: absolute;
width: 1280px;
background-color: rgb(205, 163, 163);
}
.text {
transform: matrix(0, 1, -1, 0, 700, 206.66071428571);
width: 306px;
position: absolute;
height: 120px;
font-size: 120px;
color: rgb(255, 0, 0);
}
.top-left {
transform-origin: top left;
}
.center-center {
transform-origin: center center 0px;
}
<div>
<div class="background"></div>
<div class="top-left text">
<p>TEXT</p>
</div>
<div class="center-center text">
<p>TEXT</p>
</div>
</div>
transforms-origin: center center;
and
transform-origin: top left;
Are the CSS properties in question.
You will see two pieces of text, both with the same properties, except for their transform-origin.
I am trying to understand why the text with the "center center" origin is being placed where it is. I would assume the central point of its "bounding box" would be at the same spot of the top left corner of the bounding box of the other piece of text?
I am looking to figure out the relationship between the two so I can potentially shift the text with the "center center" origin to be in place of the other text.

To make it easier to understand, I have changed your matrix for an aproximate composite transform. You have a translate and a rotate 90 deg. And I have reduced the translate amount so that it fits more easily in the snippet. But the math remains unchanged.
Now notice that the center of transforms (wait for the animation to take place) are not where you might expect, the divs are not in the same place than the ps. I have added a border to the div so that it is easier to see.
About the translation involved, you are rotating around the center of the div. or around the top left corner. The distance between those points is half the width of the div, and half the height:
153px 60px
Now, when you rotate that 90deg, the equivalent translation is the sum and the rest of those values:
213px 93px
Hover on the snippet to see those values applied, and the 2 ps aligned
.background {
height: 99%;
position: absolute;
width: 100%;
background-color: rgb(205, 163, 163);
}
.text {
transform: translate(300px, 100px) rotate(90deg);
width: 306px;
position: absolute;
height: 120px;
font-size: 120px;
color: rgb(255, 0, 0);
border: solid 1px black;
animation: show 4s infinite;
}
.top-left {
transform-origin: top left;
}
.center-center {
transform-origin: center center 0px;
}
#keyframes show {
0% { transform: translate(300px, 100px) rotate(90deg);}
5% { transform: translate(300px, 100px) rotate(90deg);}
100% { transform: translate(300px, 100px) rotate(0deg);}
}
body:hover .text {
animation: none;
}
body:hover .center-center p {
transform: translate(93px, 213px);
}
p {transition: 1s; }
<div>
<div class="background"></div>
<div class="top-left text">
<p>TEXT</p>
</div>
<div class="center-center text">
<p>TEXT</p>
</div>
</div>
To understand that in the general case. Changing the transform origin is equivalent, in a mathematical sense, to do a translate (from one origin to the other), apply the transform, and do the inverse of the translate.
This translate, since you are moving from center center to top left, is half the width, half the height.
when you apply to this value the matrix, the uniform terms are negligible, and you end with x2 = x1 * a00 + y1 * a01 and y2 = x1 * a10 + y1 * a11.
The final movement is that last calculus minus the first translate. Just check that my original answer is the special case for the matrix provided in your question

The transform-origin property allows you to change the position of transformed elements.
2D transformations can change the x- and y-axis of an element. 3D transformations can also change the z-axis of an element.
to better understnad. Here is the live demo from W3schools.com :http://www.w3schools.com/cssref/trycss3_transform-origin_inuse.htm
Example CSS with browser prefixes.
CSS
div {
-ms-transform: rotate(45deg); /* IE 9 */
-ms-transform-origin: 20% 40%; /* IE 9 */
-webkit-transform: rotate(45deg); /* Chrome, Safari, Opera */
-webkit-transform-origin: 20% 40%; /* Chrome, Safari, Opera */
transform: rotate(45deg);
transform-origin: 20% 40%;
}

Related

Scale arrow made of 2 spans with margin and rotation

I have an arrow that I want to scale with screen size, But I don't understand why negative margin doesn't scale properly for different screens is it because of the rotation? http://jsfiddle.net/1yab70ns/:
Edit: I realized it is because of the space from the inline-block and I fixed it wiht float, but why doesn't it also scale with screen?
.pointer span {
width: 1.7vh;
height: 4.5vh;
background: #41291B;
border-radius: 5vh;
display: inline-block;
position: relative;
}
.pointer span:nth-child(1) {
transform: rotate(45deg);
}
.pointer span:nth-child(2) {
transform: rotate(-45deg);
margin-left: -0.3vh;
}
<div class="pointer" id="pointer">
<span></span>
<span></span>
</div>
By using transform: rotate without specifying the point around which rotation will take place, the system defaults to rotating about the mid point of each element. That, coupled with the natural distance between two block elements can cause a problem.
If we make the rotation about the mid point of the top of each element and just enough down so that when they turn they overlap at the top and start them off overlapping each other exactly you get a more aligned result. We don't need to compensate by using margin left, we absolutely know that they are rotating about the same point.
.pointer{
position:absolute;
top: 50%;
left:50%;
}
.pointer span {
margin: 0;
padding: 0;
width: 1.7vh;
height: 4.5vh;
background: #41291B;
border-radius: 5vh;
display: inline-block;
position: absolute;
transform-origin: 0.85vh 0.85vh;
}
.pointer span:nth-child(1) {
transform: rotate(
45deg
);
}
.pointer span:nth-child(2) {
transform: rotate(
-45deg
);
}
<div class="pointer" id="pointer">
<span></span>
<span></span>
</div>

Why perspective isn't giving the same result when some styles are updated?

I have two boxes with 3d transformation (rotationY). Each have almost the same values, one perspective looks fine, the other kinda wrong but still have some correct perspective.
The first box at the top side does not protrude, but it have a perspective view yet. Also the 3° the container is 200% bigger
The second box do a beautiful 3d effect.
Here I made the example of what I'm trying to explain.
$(".eye").on('click', function () {
$( '.man' ).toggleClass('open');
})
* { padding: 0; margin: 0; }
.eye { padding: 6px 8px; }
.universe {
background: rgb(0 0 255 / 0.3);
position: absolute;
height: 100%;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.dark {
background: rgb(0 255 0 / 0.3);
width: 25%;
height: 25%;
}
.god {
background: rgb(255 0 0 / 0.3);
transform-style: preserve-3d;
transform: perspective(800px);
}
.man {
position: absolute;
transform-origin: top left;
transition: 1s all linear;
}
.man.open {
transform: rotateY(-60deg);
}
.life {
background: rgb(255 255 0 / 0.36);
width: 25vw;
height: 25vh;
}
.no.god {
height: 100%;
}
.no.man {
position: relative;
}
.yes.god {
height: 200%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="universe">
<div class="dark">
<div class="god">
<div class="man">
<div class="life">Nothing happens until something moves.</div>
</div>
</div>
</div>
<button class="eye"> OPEN </button>
<div class="dark no">
<div class="god no">
<div class="man no">
<div class="life no">Nothing happens until something moves.</div>
</div>
</div>
</div>
<button class="eye"> OPEN </button>
<div class="dark yes">
<div class="god yes">
<div class="man yes">
<div class="life yes">Nothing happens until something moves.</div>
</div>
</div>
</div>
</div>
I think something is working wrong, but can't figure it out why. Can someone explain me why it's behaving like that?
Each have almost the same values, one perspective looks fine,
No they don't have the same values. One is using position:absolute and the the other one position:relative and this make a big difference. If you inspect the god element you will notice that its height is 0 when using the position:absolute (the first case) which is creating the issue.
Here is a simplified code to better show your issue:
.box {
display: inline-block;
vertical-align:top;
width: 100px;
perspective: 200px;
position: relative;
margin: 20px;
background:blue;
}
.box>div {
position: relative;
padding: 10px;
background: red;
color: #fff;
transition: 1s all linear;
transform-origin: top left;
}
body:hover .box > div {
transform: rotateY(-40deg);
}
<div class="box">
<div>Some text here</div>
</div>
<div class="box">
<div style="position:absolute;">Some text here</div>
</div>
For a more accurate explanation we need to refer to the specification
Perspective can be used to add a feeling of depth to a scene by making elements higher on the Z axis (closer to the viewer) appear larger, and those further away to appear smaller. The scaling is proportional to d/(d − Z) where d, the value of perspective, is the distance from the drawing plane to the the assumed position of the viewer’s eye.
Second, the perspective and perspective-origin properties can be applied to an element to influence the rendering of its 3d-transformed children, giving them a shared perspective that provides the impression of them living in the same three-dimensional scene.
Then we can see the math part:
The perspective matrix is computed as follows:
Start with the identity matrix.
Translate by the computed X and Y values of perspective-origin
Multiply by the matrix that would be obtained from the perspective() transform function, where the length is provided by the value of the perspective property
Translate by the negated computed X and Y values of perspective-origin
The trick is within the steps (1)(4) related to perspective-origin. If we check the definition we can read:
The values for perspective-origin represent an offset of the perspective origin from the top left corner of the reference box.
Note the reference box which is the key here because this is the variable in out case (the god element). If we add to this the fact that the default value is 50% 50% we get our answer:
<percentage>
A percentage for the horizontal perspective offset is relative to the width of the reference box. A percentage for the vertical offset is relative to height of the reference box. The value for the horizontal and vertical offset represent an offset from the top left corner of the reference box.
Now we have all the information to understand what is happening. In the first case where the element has 0 height, the origin is on the top center (we only consider 50% of the width) while in the second case the origin is the center since our element has a height different from 0 and more precisely a height equal to the one of the transformed element which give us a perfect result.
If we change the perspective-origin and we consider pixel values we will have the same result for both:
.box {
display: inline-block;
vertical-align:top;
width: 100px;
perspective: 200px;
perspective-origin:50px 30px;
position: relative;
margin: 20px;
background:blue;
}
.box>div {
position: relative;
padding: 10px;
background: red;
color: #fff;
transition: 1s all linear;
transform-origin: top left;
}
body:hover .box > div {
transform: rotateY(-40deg);
}
<div class="box">
<div>Some text here</div>
</div>
<div class="box">
<div style="position:absolute;">Some text here</div>
</div>
To conclude, the default value of perspective-origin is 50% 50% and percentage is based on the size of the element where we apply the perspective. It's now clear that if the size is changed the origin will no more be the same which will logically give us a different rendring.
To avoid this we either set a pixel value for the origin OR we consider the use of perspective() directly on the concerned element (the one we want to transform) and in this case we are sure the result will be the same since both elements are the same:
.box {
display: inline-block;
vertical-align:top;
width: 100px;
position: relative;
margin: 20px;
background:blue;
}
.box>div {
position: relative;
padding: 10px;
background: red;
color: #fff;
transition: 1s all linear;
transform-origin: center left;
transform: perspective(200px) rotateY(0);
}
body:hover .box > div {
transform: perspective(200px) rotateY(-40deg);
}
<div class="box">
<div>Some text here</div>
</div>
<div class="box">
<div style="position:absolute;">Some text here</div>
</div>
<div class="box" style="height:500px;">
<div style="position:absolute;">Some text here</div>
</div>
You should note in the above that perspective-origin is irrelevant and the transform-origin with define the origin since we are using the transform-function version of perspective.
Related questions:
perspective and translateZ moves diagonally
How to calculate angle of rotation to make width fit desired size in perspective mode?
CSS 3d transform doesn't work if perspective is set in the end of property

CSS ring with background and percentage border width

I'm seeking for some advice on creating a "ring" shape in CSS. Here's some important detailed goals I need to achieve:
the ring border thickness must be a percentage number, not rm or absolute pixel number, so that the ring shape can be fully responsive based on container size;
The ring border need to have a background, for my scenario, the background could be sometimes a combination of 3-4 solid colors, or a gradient color;
The filling of the ring must be transparent so user can see the background through it.
Here's a quick example:
Here are a few attempts I used:
Make a border-radius: 50% div with only border width but soon I noticed the border width cannot be a percentage number;
SVG clipping a round div to a ring shape. so far I was not able to successfully make it working... If this is the right approach, please share some snippet.
You can achieve this considering mask where the idea is to use a radial-gradient to create the hole and use fixed value which will make the visible part (the thickness) to be responsive.
.box {
border-radius:50%;
background:linear-gradient(red,purple,orange);
-webkit-mask: radial-gradient(transparent 89px,#000 90px);
mask: radial-gradient(transparent 89px,#000 90px);
}
.box:before {
content:"";
display:block;
padding-top:100%;
}
.container {
margin:0 auto;
max-width:200px;
animation:change 3s linear alternate infinite;
}
#keyframes change{
to {
max-width:400px;
}
}
body {
background:linear-gradient(to right,yellow,pink);
}
<div class="container">
<div class="box">
</div>
</div>
Making responsive rings in CSS is tough. The best I've found is to simply create two circles stacked on top of each other where the top circle's background is the same as the container background. You could do this with 2x elements like in my example or with a pseudo-class.
Pros:
You get lots of control
Easily add other content (like pie charts) since the content is "masked"
Cons:
Background needs to be a flat color and nothing will show through the ring
.outer {
width: 200px;
height: 200px;
border-radius: 50%;
position: relative;
background-color: #9273B0;
margin: 10px;
cursor:pointer;
}
.inner {
position: absolute;
width: 50%;
height: 50%;
border-radius: 50%;
background-color: #ffffff;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
transition: all 0.5s ease-out;
}
.outer:hover .inner {
width: 90%;
height: 90%;
}
<div class="outer">
<div class="inner"></div>
</div>
If you MUST see the background through the ring, I'd look into a SVG clip path but that gets really complicated pretty quick.
In order to maintain percentage values you can try using a radiel-gradient. However the borders tend to get a little choppy looking.
.circle {
display: flex;
justify-content: center;
align-items: center;
font-family: sans-serif;
color: #fff;
height: 100%;
width: 100%;
border-radius: 50%;
background: radial-gradient(ellipse at center,
rgba(255,113,12,0) 60%,
rgba(255,113,12,1) 51.5%);
}
Example: https://codepen.io/SROwl/pen/BMEJzj
You could use vw or vh as a metric. The border-width will be calculated based on the viewport width or height depending what you choose. You'll have to do some calculation of what value you want to use:
.ring {
border: 10vw solid red;
border-radius: 50%;
height: 100%;
position: absolute;
width: 100%;
}
https://codepen.io/anon/pen/ErJbxN?editors=1100
With JS: https://codepen.io/anon/pen/rPbYvm

CSS make cube with skew and rotate

I am trying to make cube with 3 square divs and CSS.
The problem appears with the top side: I can't find a way to give it a proper shape.
Of course, it should be an easy way to do it with matrix or other stuff, but if there's a way to solve this problem using only skew and rotate, please provide it.
Thanks in advance.
html:
<div id='box' class='top'></div>
<div id='box' class='left'></div>
<div id='box' class='right'></div>
CSS:
#box {
width: 50px;
height: 50px;
}
.top {
background: #bbf;
margin: 40px 0 0 24px;
transform: rotate(-30deg) skewX(30deg); /* ??! */
}
.left {
background: #fbb;
transform: rotate(30deg) skewX(30deg);
display: inline-block;
margin: -11px 0 0 0;
}
.right {
background: #bfb;
transform: rotate(60deg) skewY(30deg);
display: inline-block;
margin: -11px 0 0 -11px;
}
EDIT: thanks to #rby, I reordered the layers a bit
See at jsfiddle
Here's one way to do it using only skew and rotate as you specified, but with a few additional modifications. First, and most important, arrange the order of your divs so that the div for the top box is first, followed by the two sides. The way you have it now, the top div (class third) is last. Then, the other modifications I did was to use a block display for the top div and add a left margin to it so that it is pushed in towards the side divs and reduce the top margin on the side divs from 50px to 10px. With these changes and your existing rotate, skewX transforms, you get a cube.
Here's the modified code - not to disrupt your code too much I simply created a new id called boxTop for the top box but it'd be best to restructure the rules.
The divs:
<div id='boxTop' class='third'></div>
<div id='box' class='first'></div>
<div id='box' class='second'></div>
The CSS:
#box {
width: 100px;
height: 100px;
margin-top: 10px;
display: inline-block;
}
#boxTop {
width: 100px;
height: 100px;
display: block;
}
.third {
background: #bbf;
margin-left: 50px;
transform: rotate(-30deg) skewX(30deg); /* ??! */
}
Hope this solves your problem.

css3 3D rotation move slightly outside it's transform origin when adding a transition?

I made a css3 cube, and I'm trying to make one side of the cube to open, in the same way a door or a window would open. So I used the transformY and sat transform origin to right along with a 2s transition. Ultimately it reaches the point I expected it to. but during the transition there's a slight turn to the left before it start moving to the right. I want to stop that.
This is my code simplified as much as possible
<div class="parent">
<div></div>
</div>
css
.parent {
perspective: 1000px;
}
.parent div {
background: #ff6b6b;
width: 300px;
height: 300px;
margin: 30px;
}
.parent div:hover {
transform: rotateY(180deg);
transform-origin: right center;
transition: all 2s;
}
I need this div to rotate in a horizontal path, with the right side being the transform origin and not changing place. But it slightly does, to the left. How do I fix that? Sorry if this isn't very clear.
You have set transform-origin in the hover state.
But not in the base state.
That means that in your transition you are not only rotating, but also changing the rotation point.
It should be
.parent div {
background: #ff6b6b;
width: 300px;
height: 300px;
margin: 0px;
-webkit-transform-origin: right center;
transform-origin: right center;
}
.parent div:hover {
-webkit-transform: rotateY(180deg);
transform: rotateY(180deg);
transition: all 2s;
}
fiddle