drake035 drake035 - 4 months ago 14
CSS Question

Why is my simple CSS transition animation jerky on smartphones?

After being advised to switch from jQuery animation to CSS animation in order to make my mobile menu animation smoother, I still find that the animation of the mobile menu opening on a smartphone is jerky and I don't know what to do to improve it.

JS:

$( '.header__menu_icon' ).click(function() {
var allClasses = 'right_0 right_280 right_minus_280';
$( '.mobile_menu, .wrap' ).removeClass( allClasses );
if ( $('.wrap' ).css( 'right' ) == '0px' ) {
$( '.wrap' ).addClass( 'right_280' );
}
else {
$( '.wrap' ).addClass( 'right_0' );
}
});


CSS:

.mobile_menu {
width: 280px;
position: absolute;
right: -280px;
}
.right_0 {
right: 0 !important;
}
.right_280 {
right: 280px !important;
}
.right_minus_280 {
right: -280px !important;
}
body,
.wrap,
.mobile_menu {
transition: all 0.2s ease-in-out;
}


HTML:

<body>
<div class='wrap'>
<div class='header'>
<div class='mobile_menu'>
...
</div>
</div>
</div>
</body>


EDIT:
New code based on @GaijinJim's answer below.

JS:

$( '.header__menu_icon' ).toggle( function() {
$( '.wrap' ).removeClass( 'mobile_menu_opening mobile_menu_closing' );
$( '.wrap' ).addClass( 'mobile_menu_opening' );
}, function() {
$( '.wrap' ).removeClass( 'mobile_menu_opening mobile_menu_closing' );
$( '.wrap' ).addClass( 'mobile_menu_closing' );
});


CSS:

@keyframes mobile_menu_opening {
0% {
transform: translateX(0px);
}
100% {
transform: translateX(-280px);
}
}
@keyframes mobile_menu_closing {
0% {
transform: translateX(-280px);
}
100% {
transform: translateX(0px);
}
}
.mobile_menu_opening {
animation-name: mobile_menu_opening;
animation-duration: 2s;
}
.mobile_menu_closing {
animation-name: mobile_menu_closing;
animation-duration: 1s;
}
.wrap {
position: relative;
}

Answer

It sounds more like of a performance issue due to the fact that you're not using transforms.

Has mentioned in A Tale of Animation Performance by Chris Coyier

It has become common generic advice that using translate() to move elements has better performance than using top/right/bottom/left.

[...]Paul talked to another Paul on the Chrome Team, Paul Lewis, who agreed that it is "smarter to use translate() for design-y motion." But went on to say that there is more to this than frame rate. With translate(), you get sub-pixel animation which is a kind of blurring between pixels that usually leads to smoother animation

You can also read this great article about All you need to know about CSS Transitions:

Hardware acceleration
Transitioning certain properties, such as left and margin causes the browser to recalculating styles every frame. This is fairly expensive, and can lead to unnecessary re-paints, especially if you have a lot of elements on the screen. This is especially noticeable in less powerful devices, such as mobiles.

This solution is to offload the rendering to the GPU using CSS transformations. In simple terms, this turns the element into an image during the transition, avoiding any style recalculations which greatly increases performance.

So in your case I would change your whole classes that are a bit of a mess in all due respect to a simple animation like:

@keyframes buttonInOutAnimation{
    0%{transform:translateX(-280px);}
    50%{transform:translateX(280px);}
    100%{transform:translateX(-280px);}
}

Then you can assign this animation to your button class like so:

.YourClassToAnimate{
    animation: buttonInOutAnimation 1s ease-in-out 0s 1;
}

Then you can assign this class with jquery like you've been doing already.

One last thing: In your case you need to add the fill-mode to freeze the animation state at the end of the animation.

-webkit-animation-fill-mode: forwards; 

forwards leaves the animation in the state of the last frame.
backwards leaves the animation at the start