So I've been trying this form challenge involving using no JS for interactive elements and I decided to make it 'fancy'. On click, the form drops and the arrow rotates; however, you can notice a jump in the pixels. I've viewed the box model several times and all of the pixels add up - I have no idea where this jump comes from. The only way to stop it is to make the arrow absolute, but it still isn't making sense why exactly that's happening if there's no shift in the layout. Any ideas?
https://codepen.io/mtbroomell/pen/zeMYdb
.ins {
display: block;
text-shadow:
20px 0 0 rgba(255,0,0,.6),
-20px 0 0 rgba(0,255,0,.6),
0 20px 0 rgba(0,0,255,.6);
font-size: 200px;
line-height:1;
color: transparent;
transform: rotate(0deg);
transition: .5s;
}
.form-toggle:checked ~ .ins-wrap .ins {
text-shadow:
0 0 0 rgba(0,0,0,.5),
0 0 0 rgba(0,0,0,.5),
0 0 0 rgba(0,0,0,.5);
transition: .5s;
transform: rotate(90deg);
}
^^^ The above is some of the sample styling as I'm not allowed to post CodePen without code.
I'm going to preface this by saying it seems quite smooth to me on a 2017 Macbook Pro using Chrome 72.
That said, almost all CSS animation jankiness on basic transforms can be improved by tricking the browser into using the GPU thread to render the element instead of the CPU. You can do that by forcing a 3d transform.
.animatedElement {
transform: translateZ(0);
}
Flickers and jumps in Chrome and FF can often be fixed with backface-visibility and perspective. Remember to use browser prefixes or a build tool that adds them.
.animatedElement {
backface-visibility: hidden;
perspective: 1000;
}
On their own, these don't do anything visually but they trick the browser renderer into doing some additional calculations.
I'm not sure what the native frame rate for css animations is but it's not fast enough. In animation the minimum frame rate needed to create the illusion of seamless movement is 24 fps. Using a requestanimationframe() would bump it up to 60 but then you'd need JS. I found this article on medium about CSS smooth animations. Might help? https://medium.com/outsystems-experts/how-to-achieve-60-fps-animations-with-css3-db7b98610108
Related
Im new with HTML & CSS and this is my first question. I tried to make a box who scales from 1.0 to 0.8 when you hover it with your mouse.
It works fine when you're with the mouse in the middle (0.0 between 0.8) on the box.
But when you are between 0.8 and 1.0 area it goes crazy everytime I move my mouse.
I would like that the box scales to 0.8 when you hover the whole 1.0 area at any time. It should be scale back to 1.0 when I leave the fixed 1.0 area. Not any other area. I cant help myself.
Test it or see it: https://jsfiddle.net/cyLxLbya/
YouTube Video: https://youtu.be/4MT4hrK7DVE
(left is between 0-0.8, right goes crazy between 0.8-1)
The problem is your scroll event triggering and blurring while the element scales. You're detecting a :hover but when the element gets small enough it loses it's :hover state and tries to scale up again. And back again (as it goes into :hover again).
One of the ways to fix this would be to detect the hover on a parent element (that doesn't scale). For example: https://jsfiddle.net/cyLxLbya/1/
.box{
display: inline-block;
position: relative;
cursor: pointer;
box-shadow: inset 0px 0px 83px -8px rgba(0,0,0,0.75);
transition: all 200ms ease;
transition-duration: 0.2s;
}
.hover:hover .box{
transform: scale(0.8);
box-shadow: inset 0px 0px 24px 0px rgba(0,0,0,0.75);
}
It's also advisable to move the transition to the box itself (not the hover state of the box).
I've been experimenting with CSS transitions lately, and I keep running into an issue when making shrinking headers.
In my example, the script C and The Corbo Group are both transparent PNGs. I'm adding a shrink class through jQuery and using CSS transitions to handle the animation.
When the transition completes, there is around 1 second of pixelation before the browser anti-aliasing completes. Is there any way to prevent this?
I've tried using transform: translate3d(0, 0, 0);
But that seems to cause the image to stay in the pixelated state.
Here's an example of my css
header img.corbo-c {max-width:118px; margin:20px 0 0 -38px; transition:all 0.25s ease;}
header.shrink img.corbo-c {max-width:60px; margin:10px 0 0 -20px; transition:all 0.25s ease;}
Pen Demonstrating Issue:
http://codepen.io/roachdesign/pen/BoOGYy
I noticed that my page was lagging when I hovered over an element with an animated box-shadow. Using Chrome's Devtools, I noticed that the entire page was being repainted when I hovered over the element. The repaint was taking 40+ milliseconds, or about 3 frames. The transition lasts about half a second, so there's noticeable lag during the half second.
How do I limit the repaint to just the area with the box shadow?
Here's a demo: http://jsfiddle.net/8sa41xfL/
html,body{
height:100%;
}
#test{
background:red;
height:100px;
width:200px;
transition:box-shadow 0.5s;
}
#test:hover{
box-shadow:0 0 3px 3px rgba(0,0,0,0.3);
}
<div id=test></div>
transform:translateZ(0) doesn't work on my page, but it works in the fiddle. Is there another fix aside from transform:translateZ(0)?
As mentioned in the thread linked in Pierre's answer box-shadow are expensive to paint. Explaining why it is expensive would require in-depth understanding of the way rendering works and I don't have near enough knowledge to explain it completely. But this answer attempts to explain why the whole page gets repainted and the various possible methods to avoid it.
According to CSS Triggers website:
Changing box-shadow does not trigger any geometry changes, which is good. But since it is a visual property, it will cause painting to occur. Painting is typically a super expensive operation, so you should be cautious.
Once any pixels have been painted the page will be composited together.
Why does the whole page get repainted everytime?
The below articles explain the way that painting actually works at a high level:
HTML5 Rocks - How Browsers Work - Painting
The Chromium Project - GPU accelerated rendering in Chrome
Based on those articles, we can see that each node in the DOM tree that produces a visual output is considered as a RenderObject and that each RenderObject is part of a RenderLayer directly or indirectly. Whenever a change happens, the renderer (or the render object) invalidates its rectangle (or RenderLayer) on screen and triggers a repaint.
In this case it seems like the whole page is getting repainted because the #test element does not warrant the creation of a separate RenderLayer (based on the criteria mentioned in the Chromium Project article) and so becomes a part of the root render layer. Because it is a part of the root render layer the whole page is getting repainted everytime a repaint is required.
The following snippet proves that the above assertion is correct. Here, I have added a #cover element (with positioning) to enclose the #test element. Now since the #cover element has explicit positioning, it creates an extra layer above root layer and #test becomes a part of this intermediate layer. Now, we can see that the box-shadow transition repaints only this intermediate layer and not the whole page.
html,
body {
height: 100%;
}
#cover {
position: relative;
}
#test {
background: red;
height: 100px;
width: 200px;
transition: box-shadow 0.5s;
}
#test:hover {
box-shadow: 0 0 3px 3px rgba(0, 0, 0, 0.3);
}
<div id=cover>
<div id=test></div>
</div>
What is the solution?
There are various CSS properties that can be used to address this problem but they all seem to point to the same point at a high level - which is, to create a separate render layer for the #test element.
Below are a few possible options to create a separate render layer for the #test element:
By adding explicit position properties - This is the same option described in Pierre's answer but absolute positioning is not the only option. Even relative positioning would solve it.
html,
body {
height: 100%;
}
#test {
position: relative;
background: red;
height: 100px;
width: 200px;
transition: box-shadow 0.5s;
}
#test:hover {
box-shadow: 0 0 3px 3px rgba(0, 0, 0, 0.3);
}
<div id=test></div>
By adding transparency (opacity) - Browsers seem to treat even opacity: 0.99 as adding transparency and it is very useful because adding this doesn't cause any visual difference.
html,
body {
height: 100%;
}
#test {
background: red;
height: 100px;
width: 200px;
opacity: 0.99;
transition: box-shadow 0.5s;
}
#test:hover {
box-shadow: 0 0 3px 3px rgba(0, 0, 0, 0.3);
}
<div id=test></div>
By adding a dummy CSS filter - We could add a filter: blur(0px) as it would do nothing.
html,
body {
height: 100%;
}
#test {
background: red;
height: 100px;
width: 200px;
-webkit-filter: blur(0px);
filter: blur(0px);
transition: box-shadow 0.5s;
}
#test:hover {
box-shadow: 0 0 3px 3px rgba(0, 0, 0, 0.3);
}
<div id=test></div>
CSS box shadows are expensive to paint. Read more on SO here.
If you want to avoid a full page repaint, use a position:absolute on your element. This will repaint the area that surrounds your element without affecting the whole page. Fiddle.
I am trying to rotate a div with this dead-simple code:
<!DOCTYPE html>
<html>
<head>
<style>
.spinner {
width: 100px;
height: 100px;
margin: auto;
border-radius: 50%;
border-bottom: 1px solid #f00;
animation: rotate-bottom 1s linear infinite;
}
#keyframes rotate-bottom {
from {
transform: rotateX(30deg) rotateY(-60deg) rotateZ(0deg);
}
to {
transform: rotateX(30deg) rotateY(-60deg) rotateZ(360deg);
}
}
</style>
</head>
<body>
<div class="spinner"></div>
</body>
</html>
I created a jsfiddle using the code above: http://jsfiddle.net/zg8vdyns/1/
Everything works fine on Chrome and Internet Explorer. A red curved line rotates in an endless, smooth and steady loop. However, firefox (39.0) seems to have issues rendering the animation (both the windows and linux build). First, the spinning line is much shorter than it should be. Second, the animation keeps faltering intermittently (it is not smooth). This looks like a firefox bug. Does anyone have a deeper insight into this issue?
Btw I know I should probably prefix 'animation' and 'keyframes' with '-moz-' but that is not the issue here.
Your issue is half-pixel/sub-pixel rendering. Playing around and changing border-bottom: 1px solid #f00; to border-bottom: 3px solid #f00; shows that animation is ok, but the rendering is very different from other browser engines... From another answer here of StackOverflow: Firefox CSS Animation Smoothing (sub-pixel smoothing)
The rendering engines for each browser is obviously different. Firefox does not implement an anti-aliasing effect on CSS animations. This does not inherently make it better or worse, it just depends on what you are animating. Linear transitions can appear undesirably blurred in Chrome for example.
That said it appears what you would like to achieve is to have an anti-aliased/sub-pixel smoothed transitions. We can't change the way the engine renders but we can manipulate the animation to appear softer to the end user.
But, differently from the approach provided by the answer in the link, in your scenario I think that there is a easier way to make the rendering more equivalent: http://jsfiddle.net/zg8vdyns/7/
Adding border-left: 1px solid rgba(255, 0, 0, 0.7); will kinda "force" the rendering of the half-pixels/sub-pixels that FireFox doesn't naturally...
Update:
#joshin855 also give a great answer below: adding the property background:rgba(255,255,255,0.01); will kinda "force" the rendering of the half-pixels/sub-pixels too. Your solution is very nice... It only have the disadvantage of a filled circle which depending on the scenario may not be suitable, but the line animation seems even more equivalent than in my solution... So, it also may be a good solution.
As far the line being a dot you can add background:white; or background:RGBA(255,255,255,.01); to the element which should fix the problem and make it look similar to other browsers. Sorry it's not a great answer just thought I would throw in my 2 cents.
.spinner {
width: 100px;
height: 100px;
margin: auto;
border-radius: 50%;
border-bottom: 1px solid #f00;
animation: rotate-bottom 1s linear infinite;
background:RGBA(255,255,255,.01);
}
http://jsfiddle.net/fqqko0uv/2/
CSS3 3D transforms + animations are great. I'm wondering if there is a way to make something bend.
This example flips the (paper) div but the animation looks stiff because, in real, when you flip paper, it bends a bit.
So any properties I overlooked or maybe a combination that makes it look like it bends?
div {
width: 90%;
height: 700px;
position: fixed;
left: 5%;
top: 0;
background-color: rgba(0,0,0,0.9);
-webkit-transform: perspective(1000);
-webkit-transform-style: preserve-3d;
-webkit-transform-origin: top;
-webkit-animation: "page curl down" 1s ease-out forwards;
}
#-webkit-keyframes "page curl down" {
from {
-webkit-transform: rotate3D(1,0,0,180deg);
}
to {
-webkit-transform: rotate3D(0,0,1);
}
}
Example page curl with bending (image): http://numerosign.com/software/css3machine/#documentation
No. The best way is to use canvas then slide an image across, like this: http://www.20thingsilearned.com/. Here's a breakdown of how that works: http://www.html5rocks.com/en/tutorials/casestudies/20things_pageflip.html
There are many limitations with that method, but it's the best I've seen.
Browser elements are currently limited to occupying a single flat plane. No matter what. I've wrestled with the idea of simulating curved <div>s for some time with no notable progress.
Using a transparent object with a border, border-radius and matrix3d can create "bends", but that's about as far as that goes. If you specify border-top only, with border-top-left-radius set, you can create a straight line with a curved end. Obviously, this is still part of a single flat plane, bending in the direction of its sibling 2-dimensional axis.
As soon as we begin thinking of bending your 2-dimensional element in the 3rd dimension it does not currently occupy, it literally becomes impossible without Canvas, WebGL, or other rendering engines.