css transform without overwriting previous transform? - css

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

Related

Wired Rotatation in Tailwind

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

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>

After Css:Hover [duplicate]

This question already has answers here:
Make CSS Hover state remain after "unhovering"
(11 answers)
Closed 8 years ago.
Out of curiosity I want to know, If I hover over a div and change its size is there a way to keep it like that after its already been hovered?
here is a useful jsfiddle
#div {
width: 100px;
height: 100px;
background: red;
-webkit-transition: width 2s; /* For Safari 3.1 to 6.0 */
transition: width 1s, height 1s;
}
#div:hover {
width: 300px;
height: 300px;
enter code here
Fiddle
There may be several solutions.
If you wish to use only CSS (and no JavaScript), you could do something like
#div {
...
transition:width 3600s,height 3600s;
}
#div:hover {
...
transition:width 1s,height 1s;
}
It may be a cheap solution, but it's CSS-only and it works well. The transition to set width/height back to the starting values is set to a high amount of time. So the transition happens, but it's not visible to the eye. Example via jsfiddle
With JavaScript I'd prefer giving a class to the element instead of setting the values via JS. I believe things like width/height should be set in CSS-files not in JS.
CSS:
.hovered {
width:300px;
height:300px;
}
JS:
document.querySelector("#div").addEventListener("hover",function() {
this.classList.add("hovered");
});
JS/jQuery:
$("#div").hover(function() {
$(this).addClass("hovered");
});
You could read this to use variables in css3, i think it would do the trick!
https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_variables
After that you could set a variable --var = 100px; and use it, after hover you reset it to --var = 300px; so it would keep it.
With jQuery:
$("#div").hover(function() {
$( this ).width("300px");
$( this ).height("300px");
});
Is easy made, we can complicate it if you want! ;)
Here goes the example in Fiddle: http://jsfiddle.net/UzM7U/24/
I think you can't create variables in CSS right now.
If you want this store your value onhover, you will need to use a CSS preprocessor like SASS or LESS. which are CSS Dynamic languages. In these language written on top of CSS which make use of variables like you asked
$var_width:100px;
$var_height:100px;
#div {
width: $var_width;
height: $var_height;
background: red;
-webkit-transition: width 2s; /* For Safari 3.1 to 6.0 */
transition: width 1s, height 1s;
}
#div:hover {
width: 300px;
height: 300px;
$var_width:300px;
$var_height:300px;
}
SASS
http://sass-lang.com/#variables
or Less
http://lesscss.org/
Note: I don't have a great knowledge about these language. But i think it's helpful to achieve the aim of OP. So if i am wrong at some where please correct me.
#div:hover:after {
width: 300px;
height: 300px;
content:"";
position:absolute;
border:1px solid red;
}

CSS: Animation vs. Transition

So, I understand how to perform both CSS3 transitions and animations. What is not clear, and I've googled, is when to use which.
For example, if I want to make a ball bounce, it is clear that animation is the way to go. I could provide keyframes and the browser would do the intermediates frames and I'll have a nice animation going.
However, there are cases when a said effect can be achieved either way. A simple and common example would be implement the facebook style sliding drawer menu:
This effect can be achieved through transitions like so:
.sf-page {
-webkit-transition: -webkit-transform .2s ease-out;
}
.sf-page.out {
-webkit-transform: translateX(240px);
}
http://jsfiddle.net/NwEGz/
Or, through animations like so:
.sf-page {
-webkit-animation-duration: .4s;
-webkit-transition-timing-function: ease-out;
}
.sf-page.in {
-webkit-animation-name: sf-slidein;
-webkit-transform: translate3d(0, 0, 0);
}
.sf-page.out {
-webkit-animation-name: sf-slideout;
-webkit-transform: translateX(240px);
}
#-webkit-keyframes sf-slideout {
from { -webkit-transform: translate3d(0, 0, 0); }
to { -webkit-transform: translate3d(240px, 0, 0); }
}
#-webkit-keyframes sf-slidein {
from { -webkit-transform: translate3d(240px, 0, 0); }
to { -webkit-transform: translate3d(0, 0, 0); }
}
http://jsfiddle.net/4Z5Mr/
With HTML that looks like so:
<div class="sf-container">
<div class="sf-page in" id="content-container">
<button type="button">Click Me</button>
</div>
<div class="sf-drawer">
</div>
</div>
And, this accompanying jQuery script:
$("#content-container").click(function(){
$("#content-container").toggleClass("out");
// below is only required for css animation route
$("#content-container").toggleClass("in");
});
What I'd like to understand is what are the pros and cons of these approaches.
One obvious difference is that animating is taking a whole lot more code.
Animation gives better flexibility. I can have different animation for sliding out and in
Is there something that can be said about performance. Do both take advantage of h/w acceleration?
Which is more modern and the way going forward
Anything else you could add?
It looks like you've got a handle on how to do them, just not when to do them.
A transition is an animation, just one that is performed between two distinct states - i.e. a start state and an end state. Like a drawer menu, the start state could be open and the end state could be closed, or vice versa.
If you want to perform something that does not specifically involve a start state and an end state, or you need more fine-grained control over the keyframes in a transition, then you've got to use an animation.
I'll let the definitions speak for themselves (according to Merriam-Webster):
Transition: A movement, development, or evolution from one form, stage, or style to another
Animation: Endowed with life or the qualities of life; full of movement
The names appropriately fit their purposes in CSS
So, the example you gave should use transitions because it is only a change from one state to another
A shorter answer, straight on point:
Transition:
Needs a triggering element (:hover, :focus etc.)
Only 2 animation states (start and end)
Used for simpler animations (buttons, dropdown menus and so on)
Easier to create but not so many animation/effect possibilities
Animation #keyframes:
It can be used for endless animations
Can set more than 2 states
No boundaries
Both use CPU acceleration for a much smoother effect.
Animation takes a lot more code unless you're using the same transition over and over, in which case an animation would be better.
You can have different effects for sliding in and out without an animation. Just have a different transition on both the original rule and the modified rule:
.two-transitions {
transition: all 50ms linear;
}
.two-transitions:hover {
transition: all 800ms ease-out;
}
Animations are just abstractions of transitions, so if the transition is hardware accelerated, the animation will be. It makes no difference.
Both are very modern.
My rule of thumb is if I use the same transition three times, it should probably be an animation. This is easier to maintain and alter in the future. But if you are only using it once, it is more typing to make the animation and maybe not worth it.
Animations are just that - a smooth behavior of set of properties. In other words it specifies what should happen to a set of element's properties. You define an animation and describe how this set of properties should behave during the animation process.
Transitions on the other side specify how a property (or properties) should perform their change. Each change. Setting a new value for certain property, be it with JavaScript or CSS, is always a transition, but by default it is not smooth. By setting transition in the css style you define different (smooth) way to perform these changes.
It can be said that transitions define a default animation that should be performed every time the specified property has changed.
Is there something that can be said about performance. Do both take
advantage of h/w acceleration?
In modern browsers, h/w acceleration occurs for the properties filter, opacity and transform. This is for both CSS Animations and CSS Transitions.
.yourClass {
transition: all 0.5s;
color: #00f;
margin: 50px;
font-size: 20px;
cursor: pointer;
}
.yourClass:hover {
color: #f00;
}
<p class="yourClass"> Hover me </p>
CSS3 Transitions brought frontend developers a significant ability to modify the appearance and behavior of an element as relative to a change in his state. CSS3 animations extends this ability and allow to modify the appearance and behavior of an element in multiple keyframes, so transitions provides us the ability to change from one state to another, while that animations can set multiple points of transition within different keyframes.
So, let's look at this transition sample where applied a transition with 2 points, start point at left: 0 and an end point at left: 500px
.container {
background: gainsboro;
border-radius: 6px;
height: 300px;
position: relative;
}
.ball {
transition: left 2s linear;
background: green;
border-radius: 50%;
height: 50px;
position: absolute;
width: 50px;
left: 0px;
}
.container:hover .ball{
left: 500px;
}
<div class="container">
<figure class="ball"></figure>
</div>
The above can be also created via animation like so:
#keyframes slide {
0% {
left: 0;
}
100% {
left: 500px;
}
}
.container {
background: gainsboro;
border-radius: 6px;
height: 200px;
position: relative;
}
.ball {
background: green;
border-radius: 50%;
height: 50px;
position: absolute;
width: 50px;
}
.container:hover .ball {
animation: slide 2s linear;
}
<div class="container">
<figure class="ball"></figure>
</div>
And if we would like another in-between point, it would be possible to achieve only via animation, we can add another keyFrame to achieve this and this is the real power of animation over transition:
#keyframes slide {
0% {
left: 0;
}
50% {
left: 250px;
top: 100px;
}
100% {
left: 500px;
}
}
.container {
background: gainsboro;
border-radius: 6px;
height: 200px;
position: relative;
}
.ball {
background: green;
border-radius: 50%;
height: 50px;
position: absolute;
width: 50px;
}
.container:hover .ball {
animation: slide 2s linear;
}
<div class="container">
<figure class="ball"></figure>
</div>
transition can go reverse from middle of the way, but animation replay the keyframes from start to end.
const transContainer = document.querySelector(".trans");
transContainer.onclick = () => {
transContainer.classList.toggle("trans-active");
}
const animContainer = document.querySelector(".anim");
animContainer.onclick = () => {
if(animContainer.classList.contains("anim-open")){
animContainer.classList.remove("anim-open");
animContainer.classList.add("anim-close");
}else{
animContainer.classList.remove("anim-close");
animContainer.classList.add("anim-open");
}
}
*{
font: 16px sans-serif;
}
p{
width: 100%;
background-color: #ff0;
}
.sq{
width: 80px;
height: 80px;
margin: 10px;
background-color: #f00;
display: flex;
justify-content: center;
align-items: center;
}
.trans{
transition: width 3s;
}
.trans-active{
width: 200px;
}
.anim-close{
animation: closingAnimation 3s forwards;
}
.anim-open{
animation: openingAnimation 3s forwards;
}
#keyframes openingAnimation {
from{width: 80px}
to{width: 200px}
}
#keyframes closingAnimation {
from{width: 200px}
to{width: 80px}
}
<p>Try click them before reaching end of movement:</p>
<div class="sq trans">Transition</div>
<div class="sq anim">Animation</div>
in addition, if you want the javascript to listen for end of transition, you'll get one event for each property that you change.
for example transition: width 0.5s, height 0.5s. the transitionend event will trigger two times, one for width and one for height.
Just a summary, thanks to this post, there are 5 main differences between CSS transitions vs CSS animations:
1/ CSS transitions:
Animate an object from one state to another, implicitly by browser
Cannot loop
Need a trigger to run (:hover, :focus)
Simple, less code, limited powerful
Easy to work in JavaScript
2/ CSS animations:
Freely switch between multiple states, with various properties and time frame
Can loop
Don’t need any kind of external trigger
More complex, more code, more flexible
Hard to work in JavaScript due to syntax for manipulating keyframes
I believe CSS3 animation vs CSS3 transition will give you the answer you want.
Basically below are some takeaways :
If performance is a concern, then choose CSS3 transition.
If state is to be maintained after each transition, then choose CSS3 transition.
If the animation needs to be repeated, choose CSS3 animation. Because it supports animation-iteration-count.
If a complicated animation is desired. Then CSS3 animation is preferred.
Don't bother yourself which is better. My give away is that, if you can solve your problem with just one or two lines of code then just do it rather than writing bunch of codes that will result to similar behavior.
Anyway, transition is like a subset of animation. It simply means transition can solve certain problems while animation on the other hand can solve all problems.
Animation enables you to have control of each stage starting from 0% all the way to 100% which is something transition cannot really do.
Animation require you writing bunch of codes while transition uses one or two lines of code to perform the same result depending on what you are working on.
Coming from the point of JavaScript, it is best to use transition. Anything that involve just two phase i.e. start and finish use transition.
Summary, if it is stressful don't use it since both can produce similar result

CSS3 animation: inherit property for keyframe at 0%

I have following problem:
I want to use CSS3 animation with keyframe rules (#keyframes myname {})
Problem is, I want to use SINGLE at-rule keyframe animation for multiple elements, but these elements have different position each. So, #keyframes animation should inherit original properties of selector at 0% (or from {}) rule, so animation would originate at original position and size of selector.
like this one:
#keyframes myanim {
0% {
left: inherit;
top: inherit;
width:inherit;
height:inherit;
}
100% {
top: 50%;
left:50%;
width: 100%;
height: 60%;
}
}
And selector:
.myselector-one {
top:10em;
left:0em;
width:10em;
height:5em;
animation: myanim 1s;
}
.myselector-two {
top:20em;
left:30em;
width: 15em;
height: 8em;
animation: myanim 1s;
}
Goal is to get original properties of each selector, put them to 0% keyframe as originating position and size and animate to 100% with same properties for every selector.
Is this possible or I have to create animation for each selector? Problem is, that I wouldn't know their position as it's going to be dynamically calculated.
Please, no jQuery solution, just pure CSS3 one! I DON't want to use jQuery animate method.
Hmmm, I have been looking into this problem for a little while and I don't think it is possible using CSS Animations. I've been trying with this JSFiddle a number of different things and running through tutorials about CSS Animations (seeing if anyone mentions the same issue) and also other information about it.
I did then come to the realization of what you are trying to accomplish and I think perhaps there is an easier solution. IF the locations are being dynamically calculated, I would assume you are indeed using some level of Javascript (or some crazy advanced CSS calc method) so I would at least think you would be setting the style of the DOM element with new left or top positions. While I'm not talking about jQuery animation, what you can do instead is use CSS3 Transitions in conjunction with Javascript. This means you get some of the benefits of CSS Animations like the computation being more native (hardware accelerated) as opposed to being done in Javascript but you do lose out on a few things.
Most importantly, there are no transition events for the browser like there is for CSS Animations nor can you have as fine-grain control over keyframes but you do get to work with it dynamically. I only suggest it as your question only refers to a keyframe of 0% and one of 100%.
The issue with what you were trying to do is that using CSS Animations needs to be static and won't pull the values that were currently set to do the animation (unlike transitions). When you are using inherit, you are actually trying to make it use the top and left etc. from it's parent.
Again, this doesn't meet your requirement of pure CSS but using CSS Transitions does mean only limited DOM manipulation via Javascript rather than what jQuery animate does.
Here is another JSFiddle using no jQuery (only very basic javascript to set a class or inline-styles) and CSS Transitions.
HTML
<div class="myselector-one" id="a">Click Me</div>
<div class="myselector-two" id="b">Click Me</div>
Javascript
document.getElementById("a").onclick = function()
{
if (this.className.indexOf("animate-complete")!=-1)
{
this.className = this.className.replace(/animate\-complete/g,"");
}
else
{
this.className += " animate-complete";
}
}
var bIsTransitioned = false;
document.getElementById("b").onclick = function()
{
if (!bIsTransitioned)
{
this.style.top = "50%";
this.style.left = "50%";
this.style.width = "100%";
this.style.height = "60%";
}
else
{
this.style.top = "";
this.style.left = "";
this.style.width = "";
this.style.height = "";
}
bIsTransitioned = !bIsTransitioned;
}
CSS
.myselector-one {
top:10em;
left:0em;
width:10em;
height:5em;
transition:all 2s;
background-color:#ffaa99;
position:absolute;
}
.myselector-two {
top:4em;
left:30em;
width: 15em;
height: 8em;
transition:all 2s;
background-color:#aaff99;
position:absolute;
}
.animate-complete
{
top: 50%;
left:50%;
width: 100%;
height: 60%;
}
An update for anyone who lands on this thread. According to MDN omitting the 0% / from selector would have the desired behaviour. https://developer.mozilla.org/en-US/docs/Web/CSS/#keyframes
Valid keyframe lists
If a keyframe rule doesn't specify the start or
end states of the animation (that is, 0%/from and 100%/to), browsers
will use the element's existing styles for the start/end states. This
can be used to animate an element from its initial state and back.

Resources