Wired Rotatation in Tailwind - tailwind-css

The two blocks behave differently when applying tailwind's "rotate(**deg)" and vanilla css "transform: rotate(**deg)". Please just hover the blue blocks to reproduce.
https://play.tailwindcss.com/Rgf2GJ6mim
Since I sometimes use css in #layer utilities to write nested styles, so could someone please help me understand this? Big Thanks!!

Despite it looks like both examples do the same thing it's not quite true. Let's find out the difference. All classes in your example are same but the last one
hover:[transform:rotate(1020deg)] generates this
.hover\:\[transform\:rotate\(1020deg\)\]:hover {
transform: rotate(1020deg);
}
while hover:rotate-[1020deg] this
.hover\:rotate-\[1020deg\]:hover {
--tw-rotate: 1020deg;
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
Or if you fill in Tailwind variables with its values it all comes to comparison between
.hover\:\[transform\:rotate\(1020deg\)\]:hover {
transform: rotate(1020deg);
}
// and
.hover\:rotate-\[1020deg\]:hover {
transform: translate(0, 0) rotate(1020deg) skewX(0) skewY(0) scaleX(1) scaleY(1);
}
We're forgot about one VERY important class - rotate-0. It actually sets the starting point of CSS transition
.rotate-0 {
--tw-rotate: 0deg;
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
Just remove rotate-0 from both of your examples and now there is no difference in transition. So what is happening?
It all comes in CSS transition from state 1 to state 2. (Let's remove last
parts with skew and scale)
First example - from translate(0, 0) rotate(0deg) to rotate(1020deg)
Second - from translate(0, 0) rotate(0deg) to
translate(0, 0) rotate(1020deg)
MDN says
The transform functions are multiplied in order from left to right, meaning that composite transforms are effectively applied in order from right to left.
See example: red square just rotating. Yellow - rotates but returns back to default position even on hover we do NOT changing translate property. We're assuming it will left the same but this is not how CSS transition works. When there are multiple transform occurrence the last one will override previous. That's why translate is not applied anymore on hover - we're "erasing" it. In order to fix it we need to keep translate on hover (blue example)
.example {
width: 60px;
height: 60px;
margin: 40px;
transition: 1000ms;
}
.example-1 {
background-color: red;
transform: rotate(0);
}
.example-2 {
background-color: yellowgreen;
transform: translate(100px) rotate(0deg);
}
.example-3 {
background-color: blue;
transform: translate(100px) rotate(0);
}
.example-1:hover {
transform: rotate(45deg);
}
.example-2:hover {
transform: rotate(45deg);
}
.example-3:hover {
background-color: blue;
transform: translate(100px) rotate(45deg);
}
<div class="example example-1"></div>
<div class="example example-2"></div>
<div class="example example-3"></div>
And that's exactly what happening in your example - you are missing translate function in compiled CSS and changing the default state of transformed object (it is not transitioning anymore - it just places the new state). We need to keep the order of the chaining functions in transform property to ensure everything will work as expected
So, few ways to fix it in Tailwind keeping initial state (rotate-0 class), both requires to change hover:[transform:rotate(1020deg)] class
First - add missing translate function - change class into hover:[transform:translate(0,0)_rotate(1020deg)]
Second - not so obvious - change --tw-rotate variable value, basically convert class into hover:[--tw-rotate:1020deg]
And finally as I said - just remove initial state (rotate-0) but sometimes it is not an option
See examples
It's not the best explanation but I tried to point you in some direction where the difference comes from

Related

Transform on svg text element does not move the element [duplicate]

Using CSS, how can I apply more than one transform?
Example: In the following, only the translation is applied, not the rotation.
li:nth-child(2) {
transform: rotate(15deg);
transform: translate(-20px,0px);
}
You have to put them on one line like this:
li:nth-child(2) {
transform: rotate(15deg) translate(-20px,0px);
}
When you have multiple transform directives, only the last one will be applied. It's like any other CSS rule.
Keep in mind multiple transform one line directives are applied from right to left.
This: transform: scale(1,1.5) rotate(90deg);
and: transform: rotate(90deg) scale(1,1.5);
will not produce the same result:
.orderOne, .orderTwo {
font-family: sans-serif;
font-size: 22px;
color: #000;
display: inline-block;
}
.orderOne {
transform: scale(1, 1.5) rotate(90deg);
}
.orderTwo {
transform: rotate(90deg) scale(1, 1.5);
}
<div class="orderOne">
A
</div>
<div class="orderTwo">
A
</div>
I'm adding this answer not because it's likely to be helpful but just because it's true.
In addition to using the existing answers explaining how to make more than one translation by chaining them, you can also construct the 4x4 matrix yourself
I grabbed the following image from some random site I found while googling which shows rotational matrices:
Rotation around x axis:
Rotation around y axis:
Rotation around z axis:
I couldn't find a good example of translation, so assuming I remember/understand it right, translation:
[1 0 0 0]
[0 1 0 0]
[0 0 1 0]
[x y z 1]
See more at the Wikipedia article on transformation as well as the Pragamatic CSS3 tutorial which explains it rather well. Another guide I found which explains arbitrary rotation matrices is Egon Rath's notes on matrices
Matrix multiplication works between these 4x4 matrices of course, so to perform a rotation followed by a translation, you make the appropriate rotation matrix and multiply it by the translation matrix.
This can give you a bit more freedom to get it just right, and will also make it pretty much completely impossible for anyone to understand what it's doing, including you in five minutes.
But, you know, it works.
Edit: I just realized that I missed mentioning probably the most important and practical use of this, which is to incrementally create complex 3D transformations via JavaScript, where things will make a bit more sense.
You can also apply multiple transforms using an extra layer of markup e.g.:
<h3 class="rotated-heading">
<span class="scaled-up">Hey!</span>
</h3>
<style type="text/css">
.rotated-heading
{
transform: rotate(10deg);
}
.scaled-up
{
transform: scale(1.5);
}
</style>
This can be really useful when animating elements with transforms using Javascript.
You can apply more than one transform like this:
li:nth-of-type(2){
transform : translate(-20px, 0px) rotate(15deg);
}
Some time in the future, (now available, see updates below) we can write it like this:
li:nth-child(2) {
rotate: 15deg;
translate:-20px 0px;
}
This will become especially useful when applying individual classes on an element:
<div class="teaser important"></div>
.teaser{rotate:10deg;}
.important{scale:1.5 1.5;}
This syntax is defined in the in-progress CSS Transforms Level 2 specification, but can't find anything about current browser support other then chrome canary. Hope some day i'll come back and update browser support here ;)
Found the info in this article which you might want to check out regarding workarounds for current browsers.
UPDATE: feature has landed in firefox 72
UPDATE: now available in chrome 104, edge 104, safari 14.1, see https://web.dev/css-individual-transform-properties/#:~:text=support%20these%20properties.-,Browser%20support,-%3A
Lesson I`ve learnt.
If you are using style's React css, don't include semicolons, even in the end of it because it is included automatically and internally by React.
Just like:
style={
transform: "rotate(90deg) scaleX(-1)",
}
It's an old question but turned out to be very relevant to me recently. I found 'transform: matrix(..)' more convenient to use.
So for the issue in question, the below example applies ~15deg rotation and translation of 20px to the right (for convenience) on the x-axis.
.matrixTransform {
transform-origin: top left;
transform: matrix(0.965,0.258,-0.258,0.965,20,0);
font-size: 30px;
}
.combinedTransform {
transform-origin: top left;
transform: rotate(15deg) translateX(20px);
font-size: 30px;
}
<div class=matrixTransform>A</div>
<div class=combinedTransform>B</div>
From what I gather this particular transform uses initial 4 parameters to represent transformations e.g. scale, rotate. And seem to be in rows by columns order.
The remaining two are translations in the x and y axis respectively.
I found Wolfram Alpha cheat sheet (calculator) very useful.
Just start from there that in CSS, if you repeat 2 values or more, always last one gets applied, unless using !important tag, but at the same time avoid using !important as much as you can, so in your case that's the problem, so the second transform override the first one in this case...
So how you can do what you want then?...
Don't worry, transform accepts multiple values at the same time... So this code below will work:
li:nth-child(2) {
transform: rotate(15deg) translate(-20px, 0px); //multiple
}
If you like to play around with transform run the iframe from MDN below:
<iframe src="https://interactive-examples.mdn.mozilla.net/pages/css/transform.html" class="interactive " width="100%" frameborder="0" height="250"></iframe>
Look at the link below for more info:
<< CSS transform >>
Transform Rotate and Translate in single line css:-How?
div.className{
transform : rotate(270deg) translate(-50%, 0);
-webkit-transform: rotate(270deg) translate(-50%, -50%);
-moz-transform: rotate(270deg) translate(-50%, -50%);
-ms-transform: rotate(270deg) translate(-50%, -50%);
-o-transform: rotate(270deg) translate(-50%, -50%);
float:left;
position:absolute;
top:50%;
left:50%;
}
<html>
<head>
</head>
<body>
<div class="className">
<span style="font-size:50px">A</span>
</div>
</body>
</html>

Order of applying css

I'm working on CSS and web development,but just face a something which i really don't understand it:
.header{
position: absolute;
width:60%;
top: 20%;
left: 50%;
transform: translateX(-50%);<------ executed after animation
text-align: center;
animation: moveUp 2s;
animation-fill-mode: backwards;
}
#keyframes moveUp{
0%{
opacity: 0;
transform:translateY(2rem);
}
100%{
opacity: 1;
transform: translateY(0rem);
}
}
so my problem here is the indicated line doesn't apply on ".header" until the animation gets applied in other word it applies animation first then translate -50% ,is there a priority of execution here or it is different thing?
Usually the styles are parsed from top to bottom, however this isn't the issue here.
What is happening in your case is the transform is being applied initially, but then it is being overridden by the animation. Once the animation is over, the element is reverting back to its default style which has the transform.
Essentially, even though the transform is applied at first, you don't see it until the element reverts to it after the end of the animation.
The only solution if you want to have the transform during the animation, is to include it in the animation itself.
#keyframes moveUp {
0 % {
opacity: 0;
transform: translate(-50%, 2rem);
}
100 % {
opacity: 1;
transform: translate(-50%, 0);
}
}
EDIT: To clarify, the order at which the styles are applied does not matter. Whether the animation or the transform is applied first, the result will be the same.
I think a source of your confusion is that the first transform is a translateX while the animation only does translateY. In both cases what is changing is the value of the transform property of the element. Therefore which axis the translation is on doesn't matter. First you set transform: translateX(-50%), but then once the animation kicks in, transform becomes translateY(2rem). The translateX part is removed from the transform, unless you include it in the animation like I have shown.

Why css transform properties are not separate css properties?

Why was decided to add all transform properties to one css property?
Instead of:
.element {
transform: translateX(50%) rotateZ(45deg) scale(0.8);
}
could be written:
.element {
translate-x: 50%;
rotate-z: 45deg;
scale: 0.8;
}
Why was chosen first variant? Is this somehow connected with transformation matrix? Or prefixes?
When you write media queries for example, you need to copy the entire transform property (not DRY), in other case, it will be overwritten. The same problem when creating transitions, toggling states, etc... That's really uncomfortable.
An explanation of what is happening behind the scene is welcome.
Because it's not just a set of properties, it's a list, where the transformations are applied in order, and a single transform type can occur more than once in the list.
So you can for example do:
div {
height:100px;
width: 100px;
}
.one {
background-color:red;
transform:translate(50%,5%) scale(0.5)
rotate(45deg) translate(50px,10px)
}
.two {
background-color:blue;
transform:scale(0.5) translate(50%,5%)
rotate(45deg) translate(50px,10px)
}
<div class="one"></div>
<div class="two"></div>
And you can see that the X-position of the diamond is affected by the order in which the transforms are applied.

Combine multiple animations

I'm trying to combine several parts of animation together by clicking a button. Here's an example:
.element {
background-color: black;
display: block;
width: 160px;
height: 160px;
border-radius: 80%;
}
.one {
animation: one 1.5s ease 1 forwards;
}
.two {
animation: two 1s forwards;
}
#keyframes one {
from {
transform: scale(0.25);
opacity: 0;
}
25% {
opacity: 0.5;
}
to {
transform: scale(1);
opacity: 0.5;
}
}
#keyframes two {
from {
opacity: 0.5;
}
to {
opacity: 0;
}
}
I'm trying to combine these two animation: one and two. My way of doing this was to use JS: classList.add('.two') when I clicked the button. But the problem was: at the moment I added the class, the element changed to its default opacity which was 1.
To solve this, I added a new class contained styles which were actually clones of final styles of the first animation. And after the second part was finished, I had to remove the class list to prepared for the first animation to be played.
So my question is, is there a better way of doing this?
Here is a CodePen Demo
I just realised a problem with this: If I start the second animation before the first one was finished, there would be a discontinuity (the circle would just turns to a larger one all of a sudden).
The demo can be found from the above link, thanks!
Can I combine these two animations?
I assume by combine you mean producing forward (on click of add animation) and reverse (on click of remove animation) animations using the same keyframe rules. It's possible to achieve but for that both the forward and reverse animations should be exactly the same (but in opposite directions). When it is same, we can use animation-direction: reverse to achieve reverse effect with same keyframes.
Here, the forward animation has a transform change whereas the reverse doesn't and hence adding animation-direction: reverse would not produce the same effect as the original snippet. Moreover, coding it is not as easy as just adding a property also, a lot of work is needed like mentioned here.
What is the reason for the other two issues?
The reason for both the issues (that is, the element getting opacity: 1 immediately when the remove button is clicked and element getting full size when remove button is clicked while forward animation is still happening) are the same. When you remove the animation on an element (by removing the class) it immediately snaps to the size specified outside of the animation.
For the first case, the size is the one that is mentioned under .element (as .one is removed) and its opacity is default 1 because there is no opacity setting in it. For the second case, when the .one is removed and .two is added, the animation is removed and so the element's size is as specified in .element and the opacity is as specified in .two (because that is later in CSS file).
So what else is the alternate?
When both forward and reverse effects are required and the animation doesn't have any intermediate states (that is, there is only a start state and an end state) then it is better to use transitions instead of animations. The reason is because transitions automatically produce the reverse effect on removal of the class (unlike animations where the reverse animation needs to be written as a separate keyframe and added to the element).
Below is a sample snippet showing how you can achieve a similar effect using just one class without the need for writing keyframes.
var theBut = document.getElementById('butt');
var theBut2 = document.getElementById('butt2');
theBut.addEventListener('click', function a() {
document.querySelector('.element').classList.add('one');
});
theBut2.addEventListener('click', function b() {
document.querySelector('.element').classList.remove('one');
});
.element {
background-color: #d91e57;
display: block;
width: 160px;
height: 160px;
border-radius: 90%;
transform: scale(0.25);
opacity: 0;
transition: opacity 2s, transform .1s 2s;
}
.one {
transform: scale(1);
opacity: 0.5;
transition: all 2s;
}
<div class="element">
</div>
<button id="butt">add animation</button>
<button id='butt2'>remove animation</button>

css transform without overwriting previous transform?

I have an element:
elem
transform translateY(5px) scale(1.2)
Now on hover I want it to move down an additional 5px
elem:hover
transform translateY(5px)
Obviously this would overwrite the previous transform. Is there anyway to set it to move an additional 5 without knowing what the previous transform state is?
Thanks.
CSS custom properties aka CSS variables are the only answer to this (outside of using Javascript).
To add to a previous value:
div {
--translateX: 140;
--trans: calc(var(--translateX) * 1px);
transform: translateX(var(--trans)) scale(1.5);
}
div:hover {
--translatemore: calc(var(--translateX) + 25);
--trans: calc(var(--translatemore) * 1px);
}
div {
transition: .2s transform;
width: 50px;
height: 50px;
background: salmon;
}
All browsers now support the ability to set transform properties individually.
https://caniuse.com/mdn-css_properties_translate
https://caniuse.com/mdn-css_properties_scale
Use the WebKitCSSMatrix object or MozCSSMatrix (I think...) to set new values trough the original object without knowing the initial transform.
http://jsfiddle.net/Cx9hH/
In this case I have an initial translate of 100px on witch I add an extra 100px:
box.style.webkitTransform = matrix.translate(100, 0);
You just have to create another div or span wrapper and set transform to that intead
You could use nested elements, and increase the translation on each on interdependently for each variable.
Although, that would get ugly pretty quick.
CSS variables!
https://jsfiddle.net/91q0s5h0/4/
div {
--translateX: 10px;
transform: translateX(var(--translateX)) scale(1.5);
}
div:hover {
--translateX: 5px;
}

Resources