CSS transition effect makes image blurry / moves image 1px, in Chrome? - css

I have some CSS that on hover, a CSS transition effect will moves a div.
The problem, as you can see in the example, is that the translate transition has the horrible side effect of making the image in the div move by 1px down/right (and possibly resize ever so slightly?) so that it appears out of place and out of focus...
The glitch seems to apply the whole time the hover effect is applied, and from a process of trial and error I can safely say only seems to occur when the translate transition moves the div (box shadow and opacity are also applied but make no difference to the error when removed).
The problem only seems to happen when the page has scrollbars. So the example with just one instance of the div is fine, but once more identical divs are added and the page therefore requires a scrollbar the problem strikes again...

2020 update
If you have issues with blurry images, be sure to check answers from below as well, especially the image-rendering CSS property.
For best practice accessibility and SEO wise you could replace the background image with an <img> tag using object-fit CSS property.
Original answer
Try this in your CSS:
.your-class-name {
/* ... */
-webkit-backface-visibility: hidden;
-webkit-transform: translateZ(0) scale(1, 1);
}
What this does is it makes the division to behave "more 2D".
Backface is drawn as a default to allow flipping things with rotate
and such. There's no need to that if you only move left, right, up, down, scale or rotate (counter-)clockwise.
Translate Z-axis to always have a zero value.
Chrome now handles backface-visibility and transform without the -webkit- prefix. I currently don't know how this affects other browsers rendering (FF, IE), so use the non-prefixed versions with caution.

You need to apply 3d transform to the element, so it will get its own composite layer.
For instance:
.element{
-webkit-transform: translateZ(0);
transform: translateZ(0);
}
or
.element{
-webkit-transform: translate3d(0,0,0);
transform: translate3d(0,0,0);
}
More about layer creation criteria you can read right here: Accelerated Rendering in Chrome
An explanation:
Examples (hover green box):
Problem: Transition may cause blink effect on sibling elements (OSx Lion, Chrome 30)
Solution: An element on its own composite layer
When you use any transition on your element it cause browser to recalculate styles, then re-layout your content even if transition property is visual (in my examples it is an opacity) and finaly paint an element:
The issue here is re-layout of the content that can make an effect of "dancing" or "blinking" elements on the page while transition happens.
If you will go to settings, check "Show composite layers" checkbox and then apply 3d transform to an element, you will see that it gets it's own layer which outlined with orange border.
After element gets its own layer, browser just needs to composite layers on transition without re-layout or even paint operations so problem have to be solved:

Had the same problem with embeded youtube iframe (Translations were used for centering iframe element). None of the solutions above worked until tried reset css filters and magic happened.
Structure:
<div class="translate">
<iframe/>
</div>
Style [before]
.translate {
transform: translateX(-50%);
-webkit-transform: translateX(-50%);
}
Style [after]
.translate {
transform: translateX(-50%);
-webkit-transform: translateX(-50%);
filter: blur(0);
-webkit-filter: blur(0);
}

I recommended an experimental new attribute CSS I tested on latest browser and it's good:
image-rendering: optimizeSpeed; /* */
image-rendering: -moz-crisp-edges; /* Firefox */
image-rendering: -o-crisp-edges; /* Opera */
image-rendering: -webkit-optimize-contrast; /* Chrome (and Safari) */
image-rendering: optimize-contrast; /* CSS3 Proposed */
-ms-interpolation-mode: nearest-neighbor; /* IE8+ */
With this the browser will know the algorithm for rendering

Just found another reason why an element goes blurry when being transformed. I was using transform: translate3d(-5.5px, -18px, 0); to re-position an element once it had been loaded in, however that element became blurry.
I tried all the suggestions above but it turned out that it was due to me using a decimal value for one of the translate values. Whole numbers don't cause the blur, and the further away I went from the whole number the worse the blur became.
i.e. 5.5px blurs the element the most, 5.1px the least.
Just thought I'd chuck this here in case it helps anybody.

I cheated problem using transition by steps, not smoothly
transition-timing-function: steps(10, end);
It is not a solving, it is a cheating and can not be applied everywhere.
I can't explain it, but it works for me. None of another answers helps me (OSX, Chrome 63, Non-Retina display).
https://jsfiddle.net/tuzae6a9/6/

Scaling to double and bringing down to half with zoom worked for me.
transform: scale(2);
zoom: 0.5;

Try filter: blur(0);
It worked for me

I've tried around 10 possibly solutions. Mixed them up and they still didn't work correctly. There was always 1px shake at the end.
I find solution by reducing transition time on filter.
This didn't work:
.elem {
filter: blur(0);
transition: filter 1.2s ease;
}
.elem:hover {
filter: blur(7px);
}
Solution:
.elem {
filter: blur(0);
transition: filter .7s ease;
}
.elem:hover {
filter: blur(7px);
}
Try this in fiddle:
.blur {
border: none;
outline: none;
width: 100px; height: 100px;
background: #f0f;
margin: 30px;
-webkit-filter: blur(10px);
transition: all .7s ease-out;
/* transition: all .2s ease-out; */
}
.blur:hover {
-webkit-filter: blur(0);
}
.blur2 {
border: none;
outline: none;
width: 100px; height: 100px;
background: tomato;
margin: 30px;
-webkit-filter: blur(10px);
transition: all .2s ease-out;
}
.blur2:hover {
-webkit-filter: blur(0);
}
<div class="blur"></div>
<div class="blur2"></div>
I hope this helps someone.

For me, now in 2018. The only thing that fixed my problem (a white glitchy-flicker line running through an image on hover) was applying this to my link element holding the image element that has transform: scale(1.05)
a {
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
-webkit-transform: translateZ(0) scale(1.0, 1.0);
transform: translateZ(0) scale(1.0, 1.0);
-webkit-filter: blur(0);
filter: blur(0);
}
a > .imageElement {
transition: transform 3s ease-in-out;
}

None of this worked, what worked for me is scaling image down.
So depending on what size you want the image or what resoultion your image is, you can do something like this:
.ok {
transform: perspective(100px) rotateY(0deg) scale(0.5);
transition: transform 1s;
object-fit:contain;
}
.ok:hover{
transform: perspective(100px) rotateY(-10deg) scale(0.5);
}
/* Demo Preview Stuff */
.bad {
max-width: 320px;
object-fit:contain;
transform: perspective(100px) rotateY(0deg);
transition: transform 1s;
}
.bad:hover{
transform: perspective(100px) rotateY(-10deg);
}
div {
text-align: center;
position: relative;
display: flex;
}
h3{
position: absolute;
bottom: 30px;
left: 0;
right: 0;
}
.b {
display: flex;
}
<center>
<h2>Hover on images</h2>
<div class="b">
<div>
<img class="ok" src='https://www.howtogeek.com/wp-content/uploads/2018/10/preview-11.png'>
<h3>Sharp</h3>
</div>
<div>
<img class="bad" src='https://www.howtogeek.com/wp-content/uploads/2018/10/preview-11.png'>
<h3>Blurry</h3>
</div>
</div>
</center>
The image should be scaled down, make sure you have a big image resoultion

I had a similar problem with blurry text but only the succeeding div was affected. For some reason the next div after the one that I was doing the transform in was blurry.
I tried everything that is recommended in this thread but nothing worked.
For me rearranging my divs worked. I moved the div that blurres the following div to the end of parents div.
If someone know why just let me know.
#before
<header class="container">
<div class="transformed div">
<span class="transform wrapper">
<span class="transformed"></span>
<span class="transformed"></span>
</span>
</div>
<div class="affected div">
</div>
</header>
#after
<header class="container">
<div class="affected div">
</div>
<div class="transformed div">
<span class="transform wrapper">
<span class="transformed"></span>
<span class="transformed"></span>
</span>
</div>
</header>

filter: blur(0)
transition: filter .3s ease-out
transition-timing-function: steps(3, end) // add this string with steps equal duration
I was helped by setting the value of transition duration .3s equal transition timing steps .3s

The blurring occurred for me in Chrome only (Windows and Mac) when animating 'transform' in a keyframe animation. For me, the -webkit-optimize-contrast setting only partially helped. For best results I also had to use a "magic value" for scaleX (slightly larger than 1 instead of 1).
Here's the code that worked:
img {
image-rendering: -webkit-optimize-contrast;
}
#keyframes scale-in-left {
0% {
transform: scaleX(0);
opacity: 0;
}
100% {
transform: scaleX(1.000001);
opacity: 1;
}
}
Here's the code that didn't work (caused blurry images in Chrome):
#keyframes scale-in-left {
0% {
transform: scaleX(0);
opacity: 0;
}
100% {
transform: scaleX(1);
opacity: 1;
}
}
In the end, the "working" code removed most of the blurring, but not all of it. Safari and Firefox were still clearer without any special settings.
Note also that just resizing the browser window cleared up the unwanted blurring, suggesting perhaps that something is causing Chrome to fail to execute a final render pass (?).

Related

Blurry content on css animation on Chrome [duplicate]

Seems like there has been a recent update to Google Chrome that causes blurry text after doing a transform: scale(). Specifically I'm doing this:
#-webkit-keyframes bounceIn {
0% {
opacity: 0;
-webkit-transform: scale(.3);
}
50% {
opacity: 1;
-webkit-transform: scale(1.05);
}
70% {
-webkit-transform: scale(.9);
}
100% {
-webkit-transform: scale(1);
}
}
If you visit http://rourkery.com in Chrome, you should see the problem on the main text area. It didn't used to do this and it doesn't seem to effect other webkit browsers (like Safari). There were some other posts about people experiencing a similar issue with 3d transforms, but can't find anything about 2d transforms like this.
Any ideas would be appreciated, thanks!
I have had this problem a number of times and there seems to be 2 ways of fixing it (shown below). You can use either of these properties to fix the rendering, or both at the same time.
Backface visibility hidden fixes the problem as it simplifies the animation to just the front of the object, whereas the default state is the front and the back.
backface-visibility: hidden;
TranslateZ also works as it is a hack to add hardware acceleration to the animation.
transform: translateZ(0);
Both of these properties fix the problem that you are having but some people also like to add
-webkit-font-smoothing: subpixel-antialiased;
to their animated object. I find that it can change the rendering of a web font but feel free to experiment with that method too.
After trying everything else here with no luck, what finally fixed this issue for me was removing the will-change: transform; property. For some reason it caused horribly blurry looking scaling in Chrome, but not Firefox.
To improve the blurriness, esp. on Chrome, try doing this:
transform: perspective(1px) translateZ(0);
backface-visibility: hidden;
UPDATE: Perspective adds distance between the user and the z-plane, which technically scales the object, making the blurriness seem 'permanent'. The perspective(1px) above is like duck-tape because we're matching the blurriness we're trying to solve. You might have better luck with the css below:
transform: translateZ(0);
backface-visibility: hidden;
I found that adjusting the scale ratio helped slightly.
Using scale(1.048) over (1.05) seemed to generate a better approximation to a whole-pixel font size, reducing the sub-pixel blurring.
I also used translateZ(0) which seems to adjust Chrome's final rounding step in the transform animation. This is a plus for my onhover usage because it increases speed and reduces visual noise. For an onclick function however, I wouldn't use it because, the transformed font doesn't appear to be as crispy.
Instead of
transform: scale(1.5);
using
zoom : 150%;
fixes the text blurring problem in Chrome.
This must be a bug with Chrome (Version 56.0.2924.87), but the below fixes the bluriness for me when changing css properties in the console('.0'). I'll report it.
filter: blur(.0px)
Sunderls lead me to the answer. Except filter: scale does not exist, but filter: blur does.
Apply the next declarations to the elements that appear blurred (in my case they were inside a transformed element):
backface-visibility: hidden;
-webkit-filter: blur(0);
It almost worked perfectly. "Almost" because i'm using a transition and while in transition, elements don't look perfect, but once the transition is done, they do.
I found out, that the problem occures on relative transforms in any way. translateX(50%), scale(1.1) or what ever. providing absolute values always works (does not produce blurry text(ures)).
None of the solutions mentions here worked, and I think there is not solution, yet (using Chrome 62.0.3202.94 while I am writing this).
In my case transform: translateY(-50%) translateX(-50%) causes the blur (I want to center a dialog).
To reach a bit more "absolute" values, I had to set decimal values to transform: translateY(-50.09%) translateX(-50.09%).
NOTE
I am quite sure, that this values vary on different screen sizes. I just wanted to share my experiences, in case it helps someone.
In my case following code caused blurry font:
-webkit-transform: translate(-50%,-50%);
transform: translate(-50%,-50%);
and just adding zoom property fixed it for me. Play around with zoom, following worked for me:
zoom: 97%;
I have found a much better and clean solution:
.element{
transform:scale(0.5)
transform-origin: 100% 0;
}
or
.element{
transform:scale(0.5)
transform-origin: 0% 0;
}
Thanks to this post:
Preventing blurry rendering with transform: scale
I have this same problem. I fixed this using:
.element {
display: table
}
Another fix to try i just found for blurry transforms (translate3d, scaleX) on Chrome is to set the element as
"display: inline-table;".
It seems to force pixel rounding in some case (on the X axis).
I read subpixel positioning under Chrome was intended and devs won't fix it.
Try using zoom: 101%; for complex designs when you can't use a combination of zoom + scale.
2019 UpdateThe Chrome display bug is still unfixed and though no fault of the patrons, none of the suggestions offered in the entirety of this website help to resolve the issue. I can concur that I have tried every single one of them in vain: only 1 comes close and that's the css rule: filter:blur(0); which eliminates the shifting of a container by 1px but does not resolve the blurred display bug of the container itself and any content it may have.
Here's the reality: there literally is no fix to this problem so here is a work around for fluid websites
CASE
I'm currently developing a fluid website and have 3 divs, all centered with hover effects and sharing percentage values in both the width and position. The Chrome bug occurs on the center container which is set to left:50%; and transform:translateX(-50%); a common setting.
EXAMPLE: First the HTML...
<div id="box1" class="box">
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry"s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.
</div>
<div id="box2" class="box">
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry"s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.
</div>
<div id="box3" class="box">
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry"s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.
</div>
Here's the CSS where the Chrome bug occurs...
*{margin:0; padding:0; border:0; outline:0; box-sizing:border-box; background:#505050;}
.box {position:absolute; border:1px solid #fff; border-radius:10px; width:26%; background:#8e1515; padding:25px; top:20px; font-size:12pt; color:#fff; overflow:hidden; text-align:center; transition:0.5s ease-in-out;}
.box:hover {background:#191616;}
.box:active {background:#191616;}
.box:focus {background:#191616;}
#box1 {left:5%;}
#box2 {left:50%; transform:translateX(-50%);} /* Bugged */
#box3 {right:5%;}
Here's the fixed css...
*{margin:0; padding:0; border:0; outline:0; box-sizing:border-box; background:#505050;}
.box {position:absolute; border:1px solid #fff; border-radius:10px; width:26%; background:#8e1515; padding:25px; top:20px; font-size:12pt; color:#fff; overflow:hidden; text-align:center; transition:0.5s ease-in-out;}
.box:hover {background:#191616;}
.box:active {background:#191616;}
.box:focus {background:#191616;}
#box1 {left:5%;}
#box2 {left:37%;} /* Fixed */
#box3 {right:5%;}
Bugged fiddle: https://jsfiddle.net/m9bgrunx/2/
Fixed fiddle: https://jsfiddle.net/uoc6e2dm/2/
As you can see a small amount of tweaking to the CSS should reduce or eliminate the requirement to use transform for positioning. This could also apply to fixed width websites as well as fluid.
It's important to add that this issue arises if the element which is being translated has a height with an odd number of pixels. So, if you have control over the height of the element, setting it to an even number will make the content appear crisp
None of above worked for me.
I had this animation for popups:
#keyframes pulse {
from {
transform: scale3d(1, 1, 1);
}
50% {
transform: scale3d(1.05, 1.05, 1.05);
}
to {
transform: scale3d(1, 1, 1);
}
}
In my case blurry effect was gone after applying this rule:
-webkit-perspective: 1000; even though it is marked as unused in Chrome inspector.
None of the above worked for me.
It worked when I added perspective
ie from
transform : translate3d(-10px,-20px,0) scale3d(0.7,0.7, 1)
i changed to
transform : perspective(1px) translate3d(-10px,-20px,0) scale3d(0.7,0.7, 1)
I used a combination of all answers and this is what worked for me in the end:
.modal .modal--transition {
display: inline-table;
transform: perspective(1px) scale(1) translateZ(0);
backface-visibility: hidden;
-webkit-font-smoothing: subpixel-antialiased;
}
My solution was:
display: initial;
Then it was crispy sharp
I was facing the blurry text issue on Chrome but not on Firefox when I used transform: translate(-50%,-50%).
Well, I really tried a lot of workarounds like:
transform: perspective(1px);
filter: blur(0);
transform: translateZ(0);
backface-visibility: hidden;
None of these worked to me.
Finally, I made the height and width of the element even. It resolved the issue for me!!!
Note: It might depend from use case to use case. But surely worth a try!
I have tried a lot of examples from these answers unfortunately nothing help for
Chrome Version 81.0.4044.138
I have added to transforming element instead
transform-origin: 50% 50%;
this one
transform-origin: 51% 51%;
it helps for me
This is what worked for me:
body { perspective: 1px; }
I fixed my case by adding:
transform: perspective(-1px)
I removed this from my code - transform-style: preserve-3d;
and added this- transform: perspective(1px) translateZ(0);
the blur went away!
FOR CHORME:
I´ve tried all suggestions here. But diden't work.
My college found a great solution, that works better:
You should NOT scale past 1.0
And include translateZ(0) in the hover but NOT in the none-hover/initial position.
Example:
a {
transition: all 500ms cubic-bezier(0.165, 0.840, 0.440, 1.000);
transform: scale(0.8, 0.8);
}
a:hover {
transform: translateZ(0)scale(1.0, 1.0);
}
In Chrome 74.0.3729.169, current as of 5-25-19, there doesn't seem to be any fix for blurring occurring at certain browser zoom levels caused by the transform. Even a simple TransformY(50px) will blur the element. This doesn't occur in current versions of Firefox, Edge or Safari, and it doesn't seem to occur at all zoom levels.
I have a div that has a small perspective shift on it to give a subtle 3D effect. The text in the div was blurring and I tried all the suggestions here to no avail.
Oddly, I found that setting 'filter: inherit;' on the text elements vastly improved the clarity. Though I can't understand why.
Here's my code in case it helps:
Html:
<div id="NavContainer">
<div id="Nav">
<label>Title</label>
<nav>
home
link1
link2
</nav>
</div>
</div>
Css:
#NavContainer {
position: absolute;
z-index: 1;
top: 0;
left: 20px;
right: 20px;
perspective: 80vw;
perspective-origin: top center;
}
#Nav {
text-align: right;
transform: rotateX(-5deg);
}
#Nav > nav > a,
#Nav > label {
display: inline-block;
filter: inherit;
}
#Nav > label {
float: left;
font-weight: bold;
}
For me the problem was that my elements were using transformStyle: preserve-3d. I realized that this wasn't actually needed for the app and removing it fixed the blurriness.
It will be difficult to solve with only css.
So I solved it with jquery.
This is my CSS.
.trY {
top: 50%;
transform: translateY(-50%);
}
.trX {
left: 50%;
transform: translateX(-50%);
}
.trXY {
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
and this is my jquery.
function tr_init() {
$(".trY, .trX, .trXY").each(function () {
if ($(this).outerWidth() % 2 != 0) {
var fixed_width = Math.ceil($(this).outerWidth() / 2) * 2;
$(this).css("width", fixed_width);
}
if ($(this).outerHeight() % 2 != 0) {
var fixed_height = Math.ceil($(this).outerHeight() / 2) * 2;
$(this).css("height", fixed_height);
}
})}
Just to add to the fix craze, putting {border:1px solid #???} around the badly looking object fixes the issue for me.
In case you have a stable background colour, consider this too.
This is so dumb noone thought about mentioning I guess, eh eh.

CSS3 animation transform jumping at osx Chrome

I am having an issue with Chrome (v. 67) at OSX and movement animations. I've prepared JS fiddle with it:
https://jsfiddle.net/5m173ghv/31/
If you open it at safari it is working very good. But, when you will use chrome it has little lags when moving.
I cannot describe it a bit more... You need to open it and try yourself on the big screen... Please look carefully at white box. You will see that this box has sometimes something like lags or small jumps(?)...
This is very weird. I've tried almost every answer from the internet (trust me ;) ).
I also tried:
Change transforms at animation into position (left)
Change animations into transitions
adding additional parameters (backface-visibility, perspective, will-change...)
Changing sequences of animation to have more steps (per 10%)
Debugging on chrome dev tools (~30-40fps)
Adding transforms like translateZ(0)
You think that this is chrome bug or maybe my fault? Do you have any solution for that?
Here you have code:
HTML
<span class="spark"></div>
SCSS
body {
background-color: black;
}
#keyframes left-to-right {
0% {
transform: translate3d(0,0,0);
}
100% {
transform: translate3d(50vw,0,0);
}
}
.spark {
position: absolute;
top: 30px;
width: 322px;
height: 500px;
background-color: white;
transform: translate3d(0,0,0);
backface-visibility: hidden;
perspective: 1000px;
animation: left-to-right 5s infinite linear;
will-change: transform;
pointer-events: none;
user-select: none;
}

Image hover CSS for revolution slider

I'm using revolution slider and I'm stuck with a problem. As an example I use the original demo from here: https://revolution.themepunch.com/wordpress-photography-slider.
On the tab 'portfolio' you see images that shrink in size with a transition and look a bit darker when you hover over them. This is what I want as well but I can't figure out how.
In revolution slider you can add classes, ID's and CSS to specific images so what I probably need is a CSS code that makes this possible. I've tried several codes I found online but none of them do the trick because they all come with an html part as well.
My guess was: the image is already there, I don't need the html part, only assign classes or id's to the images and then give each image the same kind of CSS code.
Am I on the right track with this? And can anyone help me with the code for it?
Many thanks in advance!
add a class, then do some css
for example:
<img class="slider-img">
.slider-img:hover {
{
let me know if you need help with the css.
EDIT:
try this.
wrap each of your images around 2 divs, slider-img and img-wrap:
<div class="slider-img">
<div class="img-wrap">
<img src="http://science-all.com/images/wallpapers/stock-image/stock-image-15.jpg">
</div>
</div>
then do some css:
.slider-img {
width: 200px;
cursor: pointer;
}
.slider-img img {
width: 100%;
}
.slider-img:hover .img-wrap {
background-color: black;
transform: scale(0.7);
-o-transform: scale(0.7);
-ms-transform: scale(0.7);
-moz-transform: scale(0.7);
-webkit-transform: scale(0.7);
transition: all .5s ease-in-out;
-o-transition: all .5s ease-in-out;
-ms-transition: all .5s ease-in-out;
-moz-transition: all .5s ease-in-out;
-webkit-transition: all .5s ease-in-out;
}
.slider-img:hover .img-wrap img{
opacity: 0.5;
}
basically what the css is doing is that when you hover over the main div (.slider-img), the div containing the image (.img-wrap) gets scaled down by 70% by the css -webkit-transform: scale(0.7);
it also gets a background color of black with an opacity of 80%. this gives the darkened image effect.
-webkit-transition: all .5s ease-in-out; gives a smooth transition effect.
if you are wondering why there are 5 different lines of css for the same thing, thats because each line targets specific browsers. -o- is opera, -moz- is firefox etc.
also, make sure to change the .slider-img width to match your needs.
check out the working example on js fiddle:
here

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

Safari CSS3 3D animate transformed intersecting elements flicker issue

I made a wedding invitation and there's a code at the bottom of the invitation to log into the wedding website. To make people aware of the code I created a little CSS3 3D animation: see the demo
HTML:
<section id="viewport">
<div id="invitation" class="show-front">
<figure class="front"></figure>
<figure class="ring"></figure>
</div>
</section>
CSS:
section#viewport {
-webkit-perspective: 1000;
-webkit-perspective-origin: 0% 0%;
}
div#invitation {
position: absolute;
-webkit-transform-style: preserve-3d;
-webkit-transition: -webkit-transform 1s ease;
}
#invitation .front {
-webkit-transform: rotateX(12deg) translate3d(0px, 0px, 32px);
#invitation .ring {
-webkit-transform: rotateY(-90deg) translate3d(6px, -15px, -1px);
}
#invitation.show-front {
-webkit-transform: rotateY(-24deg) rotateX(90deg);
}
#invitation:hover {
-webkit-transform: rotateY(20deg) rotateX(3deg);
}
In Chrome and Firefox looks everything well, but in Safari the intersecting elements produce an annoying flicker issue. If I remove the rings which are intersecting the front picture element the flicker issue don't appear: the demo without rings
I've tried everything and read every post I could find, but nothing solved this problem. All useless html elements in the demo aren't useless in my real animation.

Resources