How to animate height with transform without squishing content - css

In my project I have been using jQuery slideUp() to slide up an element in a 200 item list when the user clicks a button. But, as everyone knows, animating CSS height requires a reflow, making the animation jerky. This animation is an integral part of the application and I am willing to go to extensive work to make it work AND work smoothly.
I have decided that CSS transform is the way to make it work smoothly because of the fact that it is handled on the gpu and on some modern browsers, it is even off the main thread and heavy JS work won't affect the transform. (I do have heavy JS work).
I am looking for a clever solution with CSS transition: transform to replicate jQuery slideUp, which just animates the height property. Below was my attempt, but it seems scale and translate do not sync as expected.
$("button").on("click", function() {
$(".collapsible").addClass("collapsed")
setTimeout(function() {
$(".collapsible").removeClass("collapsed")
}, 5000);
});
.list-item {
width: 400px;
height: 100px;
background-color: blue;
margin-top: 10px;
overflow-y: hidden;
}
.collapsible.collapsed .content {
transition: transform 3s linear;
transform: scale(1, 1) translate(0, -100%);
}
.collapsible.collapsed {
transition: transform 3s linear;
transform: translate(0, -50%) scale(1, 0);
}
.collapsible.collapsed ~ .list-item {
transition: transform 3s linear;
transform: translate(0, -100%);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.3/jquery.min.js"></script>
<button>Collapse</button>
<div class="list-item collapsible">
<div class="content">
Just an example <br>
Just an example <br>
Just an example <br>
Just an example <br>
Just an example <br>
</div>
</div>
<div class="list-item">
</div>
<div class="list-item">
</div>
I played with the values some and got it closer by changing the content transform to transform: scale(1, 3) translate(0, -50%);.
It seems I am so close to achieving, but never quite succeeding. Is there any cut and dried trick out there for this?
Requirements:
Preferably no JS
Off the main thread

Related

Switching backgrounds of multiple divs in a single keyframe

I am having trouble running a single animation using different divs. I just want to switch between different backgrounds using opacity in animation but i can't run it on different divs in a single animation
I have tried to make this animation on section container it was done but it does not give me transition with it I also want some transition so that's why I want to run this animation using different divs just like the one made on fivers homepage...!!
You can apply same animation on different elements by just giving all the elements(on which wanted animation) a same class and then selecting that class by dot operator and then in css rule define the animation property. Now, all elements will get this animation.
Suppose in html we have many divs on which we had applied some background color or image. And we want that there is a animation on all of them that firstly the background is light and with time background becomes dark(its original color).
So defining a single keyframe:-
HTML CODE
<div class="red same-animation"></div>
<div class="blue same-animation"></div>
<div class="green same-animation"></div>
<div class="yellow same-animation"></div>
CSS CODE
div{
width: 100px;
height: 100px;
margin: 10px;
padding: 10px;
opacity: 0 ;
}
.red{
background: red;
}
.green{
background: green;
}
.blue{
background: blue;
}
.yellow{
background: yellow;
}
.same-animation{
animation: change-opacity 5s ease-in-out 0s 1;
}
div:hover{
transition: all 5s;
opacity: 1;
}
#keyframes change-opacity {
0%{
opacity: 0;
}
100%{
opacity: 1;
}
}
LINK OF CODE - https://codepen.io/aryansharma-2002/pen/mdpRoqW
So in this example firstly animation will be done in 5second to all divs such that there opacity will increase then all will disappear. Then if hover on any div then it will visible with transition. So tried to explain both concepts.
If any doubt or suggestion, please comment

Choppy transition: transform scalling animation on Safari

I have hard time making scaling animation that is not choppy in Safari (mobile). By choppy I mean you can clearly see that it's not 60FPS fluid animation. I need to say that element is absolutely positioned so it should not affect layout, should it?
First thing that I tried is animating element by creating CSS animation with 2 keyframes, going from transform: scale(0); to transform: scale(1);. For the effect I want to achieve I also used transform-origin: top right;. I also tried to optimize it by setting will-change: transform; but it was choppy.
I scrapped it and reworked it so it uses transition: transform. So by default element has transform: scale(0);, when certain class attaches to it, it gets transform: scale(1); which transition is animating, but it is still choppy.
After some research I found out that you want to avoid animating properties that require browsers to recalculate layout. I found out this site which says that WebKit (which Safari is using as far as I know) pretty much recalculates layout for every property change. Is that true, and if it is how do you make 60FPS fluid animations on mobile Safari (on other platforms using Safari it's not that noticeable because they have much more resources to recalculate everything and it appears a bit smoother than on mobile)?
There are 2 things that you can try:
avoid using scale(0). In some browsers this gives problems. scale(0.01) is almost the same, and will be better for the browser.
Try to make the animation handled by the GPU instead of the CPU. this can be done with the following code
from: {transform: scale(0.01) translateZ(1px);}
to: {transform: scale(1) translateZ(1px);}
In reference to this article section Animate Changes in CSS Properties, you can animate with keyframes, but need use a percentage like this:
More examples in w3s article.
PD: if you add an example, i'll try to help you.
function animateStart(){
document.getElementById('ball').classList.add('bounce');
}
function animationPause(){
document.getElementById('ball').style.webkitAnimationPlayState='paused';
}
function animationContinue(){
document.getElementById('ball').style.webkitAnimationPlayState='running';
}
#-webkit-keyframes bounce {
0% {top: 100px; left: 1px; -webkit-animate-timing-function: ease-in;}
25% {top: 150px; left: 76px; -webkit-animate-timing-function: ease-out;}
50% {top: 100px; left: 151px -webkit-animate-timing-function: ease-in;}
75% {top: 150px; left: 226px -webkit-animate-timing-function: ease-out;}
100% {top:100px; left: 301px;}
}
.bounce {
-webkit-animation-name: bounce;
-webkit-animation-duration: 2s;
-webkit-animation-timing-function: linear;
-webkit-animation-iteration-count: infinite;
-webkit-animation-direction: alternate;
}
.ball-style {
position:absolute; top: 100px; left: 10px;
height:100px; width:100px; border-radius:50px;
background:-webkit-radial-gradient(30% 30%, white, red 10%, black);;
}
.wall {
position:absolute; left: 400px; top: 100px;
height: 150px;
background: black;
}
<input type="button" value="Animate alternative"
onclick="document.getElementById('ball').classList.add('bounce');">
<input type="button" value="Animate" onclick="animateStart()">
<input type="button" value="Pause" onclick="animationPause()">
<input type="button" value="Continue" onclick="animationContinue()">
<div id="ball" class="ball-style"></div>
<div class="wall"> </div>
I don't know if this helps but are you using javascript to add a class and start the animation with the class change?
jQuery for example can cause long depth analysis in the dom when manipulating elements, this is likely to interfere with the performance you need for achieving the desired fps.

Jump at the end of a CSS transform scale transition

Here is my code to apply a scale effect (like a zoom) on a figure element while hovering a parent.
CSS (SCSS)
.card-link {
display: block;
overflow: hidden;
}
figure.card-figure {
will-change: transform;
transform-origin: center;
transform: scale3d(1, 1, 1) translateZ(0) rotate(0.001deg);
transition: transform var(--transition-easing) var(--transition-duration);
transition-delay: 0.1s;
perspective: 1000px; // Helps the jump in chrome
backface-visibility: hidden; // Helps the jump in chrome
// Hover
.card-link:hover & {
transform: scale3d(1.1, 1.1, 1) translateZ(0) rotate(0.001deg);
}
}
HTML
<a href="#" class="card-link">
<figure class="card-figure">
<img src="...">
</figure>
</a>
It obviously works, but at the end of the transition it makes a little jump, like the figure goes some pixels outside the parent (that it has overflow hidden).
This seems to only happens on Chrome (v 89.0.4389.114) and only at certain viewports (window sizes).
I tried all (I mean all) the solutions I found (you'll recognize them in the CSS above) but none of them worked. Please, anyone could give some ideas? Thanks

CSS3 transitions, using translateX + translateY set to positive values, do not appear to transition properly

I'm currently trying to implement some CSS transition effects on a set of different panels, to give the appearance that they are "sliding in" from off screen. I want them each to appear in a different direction (e.g. top to bottom, left to right, etc.)
Presumably, to do bottom to top and right to left, I would just want to set translateX or translateY, but with a positive value, rather than negative. And then for all of them, I would just translate the value to 0 when I want them to appear on screen.
Here is a really simplified version of what I am trying to do:
HTML:
<div class="container">
<div id="about" class="panel">
<h2>About</h2>
<p>I'm Mike and I don't know!</p>
Close
</div>
<div id="projects" class="panel">
<h2>Projects</h2>
<p>Here are some projects I have worked on.</p>
Close
</div>
<div id="contact" class="panel">
<h2>Contact</h2>
<p>You can find me all over the Internet!</p>
Close
</div>
<div id="blog" class="panel">
<h2>Blog</h2>
<p>Here are some blog posts.</p>
Close
</div>
projects
blog
about
contact
</div>
CSS:
.container {
overflow:hidden;
position:relative;
width:100vw;
height:100vh;
}
.panel{
min-width: 100%;
height: 100%;
position: absolute;
box-shadow: 0px 4px 7px rgba(0,0,0,0.6);
z-index: 2;
-webkit-transition: transform .8s ease-in-out;
-moz-transition: transform .8s ease-in-out;
-o-transition: transform .8s ease-in-out;
transition: transform .8s ease-in-out;
}
.panel:target {
-webkit-transition: transform .8s ease-in-out;
-moz-transition: transform .8s ease-in-out;
-o-transition: transform .8s ease-in-out;
transition: transform .8s ease-in-out;
}
.panel#contact {
transform: translateX(110%);
background-color:whitesmoke;
}
.panel#about {
transform: translateX(-110%);
background-color: red;
}
.panel#projects {
transform: translateY(110%);
background-color: blue;
}
.panel#blog {
transform: translateY(-110%);
background-color: gold;
}
.panel#contact:target{
transform: translateX(0%);
}
.panel#about:target{
transform: translateX(0%);
}
.panel#projects:target{
transform: translateY(0%);
}
.panel#blog:target{
transform: translateY(0%);
}
Note that the "About" and "Blog" links are transitioning as expected, whereas the "Projects" and "Contact" links appear to cause weirdness. Those are the two with positive values.
I'm having a really difficult time:
a.) understanding what the problem is exactly
b.) how can I fix it (if at all)
If someone could both explain what the browser is doing right now with those erroneous transitions and provide a solution, I would be much obliged. AFAIK, this is happening in every browser (that supports the :target pseudo element, anyway).
Let me know if you need clarification. Thanks a lot!
UPDATE: Not fixed, but I noticed that if you set the values to <98% for the offending element panels (Projects + Contact), the page transitions properly, though it isn't hidden from the screen. Not sure what that means, but if it helps...
UPDATE 2.0: Thanks for the comments, folks! I tried adding a container, and have updated the HTML and CSS to reflect that. The changes in question are a container div that wraps the panels, as well as the following CSS for that container:
.container {
overflow:hidden;
position:relative;
width:100vw;
height:100vh;
}
I'm still having similar behavior -- this is also updated in the Codepen. I think I understand now the issue, but I'm not quite sure why the page is still jumping to that element in question before applying the transformation. I can imagine there is some hacky way to get around this, but I would rather do it the "right" way.
I decided that using :target and trying to have this be pure CSS wasn't really the way to go, and wound up just using Javascript in a fairly straightforward manner.
Lesson learned: Don't try to do things w/ Pure CSS unless you really have to.

CSS3: Animate opacity and scale by applying class, and animate back by removing class

I have been experimenting and trying but have not been able to achieve the following. I'm sure the solution is simple, but I haven't hit it yet.
Let's say I want to animate an element (eg. div) when I apply a class (eg. active). And I want to reverse the animation when I remove the class (or toggle with another).
The properties I would like animate are scale (transform) and opacity.
Also, when entering the page, the element will not have any class, and should snap to its state, and not animate. It should only animate when explicitely adding or removing the class.
jsfiddle: http://jsfiddle.net/bertvan/9r98w/
HTML:
<div id="the-div"></div>
Trigger
JS:
$(function(){
$("a").click(function(){
$("#the-div").toggleClass("active");
});
});
CSS:
#the-div{
width: 200px;
height: 200px;
background-image: url("http://placeimg.com/200/200/any");
-webkit-transform: scale(0.7);
opacity: 0.5;
}
#the-div.active{
/* animate scale & opacity */
-webkit-transform: scale(1);
opacity: 1;
}
You are missing a transition property on the div selector:
running demo
code added:
#the-div{
transition: all 2s;
}
This is an example of a code to toggle class for changing size of a div with the transition-animation.
The HTML:
input-button with id="setRemoveClassBtn"
a div-tag with id="div1"
You will need a CSS-class-definition (notice that this is with the moz-prefix made to work in Firefox):
div{
width: 150px;
height: 150px;
background-color: rgb(250, 250, 150);
-moz-transition: width 5s, height 5s;
}
div.bigSizeDivs{
width: 250px;
height: 250px;
}
You will have to use javascript/jQuery to add/remove class. Here is an example with jQuery
$("#setRemoveClassBtn").click(function(){
$("#div1").toggleClass("bigSizeDivs");
});

Resources