Poor CSS Animation performance - no browser paints - performance

I've got a number of elements that I'm animating which I've developed in a manner that shouldn't cause any browser paints. If I turn on "Paint Flashing" in Chrome Devtools I don't see any paint flashing at all. However, if I record the performance then the graph shows that there is a lot of time spent on painting. The FPS is as low as 15fps at times.
I actually built this in Vue, and the compiled code results in too much code to paste here. I realise the animation is somewhat broken, I still need to work out some timings etc - but for the purpose of this question, I'm only concerned about the performance.
I have posted the compiled code here on CodePen:
https://codepen.io/IOIIOOIO/pen/gjBqyg
It seems StackOverflow requires that I post some code here, so here is the compiled code for just one element:
.circle {
position: relative;
width: 100%;
padding-top: 100%;
border-radius: 50%;
overflow: hidden;
}
.circle::before {
content: "";
background-color: black;
position: absolute;
top: 0;
left: 0;
width: 50%;
height: 100%;
animation-name: switch;
animation-iteration-count: infinite;
animation-timing-function: steps(1);
animation-duration: 3s;
animation-delay: inherit;
}
.rotating-circle {
display: block;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border-radius: 50%;
}
.rotating-circle--first-cycle {
background-color: black;
animation-name: rotate, toggle-first;
animation-duration: 3s, 3s;
animation-iteration-count: infinite, infinite;
animation-timing-function: linear, steps(1);
animation-delay: 1800ms;
}
.rotating-circle--second-cycle {
opacity: 0;
animation-name: rotate, toggle-second;
animation-duration: 3s, 3s;
animation-iteration-count: infinite, infinite;
animation-timing-function: linear, steps(1);
animation-delay: 1800ms;
}
#keyframes rotate {
0% {
transform: rotate3d(0, 1, 0, 0deg);
}
50% {
transform: rotate3d(0, 1, 0, 180deg);
}
}
#keyframes toggle-first {
0% {
opacity: 1;
}
25% {
opacity: 0;
}
75% {
opacity: 1;
}
}
#keyframes toggle-second {
0% {
opacity: 0;
}
25% {
opacity: 1;
}
75% {
opacity: 0;
}
}
#keyframes switch {
0% {
transform: translatex(0);
}
50% {
transform: translatex(100%);
}
100% {
transform: translatex(0);
}
}
<div class="circle" style="background-color: rgb(255, 0, 0); animation-delay: 0ms;">
<div class="rotating-circle rotating-circle--first-cycle" style="animation-delay: 0ms;">
</div>
<div class="rotating-circle rotating-circle--second-cycle" style="background-color: rgb(255, 0, 0); animation-delay: 0ms;">
</div>
</div>

It seems that all the work was being done on Composite Layers, and not necessarily on Painting alone. I found that adding transform: translateZ(0) or will-change to the individual elements that were being animated didn't help much. However, if I add transform: translateZ(0) to the parent .circle element then the time spent on Composite Layers and Painting is greatly reduced.
It still runs fairly slow but I think that may just be because my computer has onboard graphics and 4GB of RAM.
So I think this is as good as it will get but would appreciate any further suggestions.
Here is an example where I've added transform: translateZ(0) to the parent element:
https://codepen.io/IOIIOOIO/pen/gjBqyg
EDIT:
I've found a significant improvement by removing border-radius on the parent, which I had set to overflow: hidden to create a mask:
Before:
.circle {
border-radius: 50%;
overflow: hidden;
}
Instead, I used clip-path as a mask:
After
transform: translateZ(0);
clip-path: circle(49% at 50% 50%);
I'm sure you'll notice straight away it's much better:
https://codepen.io/IOIIOOIO/pen/OwBBJV
Any further insight into why this works would be much appreciated.

Related

Can't vertically centre image, but horizontal works

I'm trying to make a custom follower alert for Twitch TV. And I'm trying to centre a small image inside a div. So far I've managed to centre it horizontaly but no matter what I try it will not centre vertically. I'm not sure why, i've tried reading many other questions on stackoverflow already, as well as following a guide from W3schools but I think this is more of a specific problem to my code. Here is a fiddle. (You can't see the image but you can see where the image would be)
And here is the code; with the idea being that the image is centered both horizontally and vertically inside the small blue square, which i've named 'left-square-container'. However currently the image is horizontally centered at the top of the div only.
If anyone can help I'd appreciate it.
#keyframes slideInFromAbove {
0% {
transform: translateY(-100%);
}
6% {
transform: translateY(0);
}
98% {
transform: translateY(0);
}
100% {
transform: translateY(-100%);
}
}
#keyframes slideInFromTheLeft {
0% {
opacity: 1;
transform: translateX(-100%);
}
4.4% {
opacity: 1;
transform: translateX(0);
}
97% {
opacity: 1;
transform: translateX(0);
}
100% {
opacity: 0;
transform: translateX(-100%);
}
}
#keyframes slideInFromBelow {
0% {
transform: translateY(100%);
}
100% {
transform: translateY(0);
}
}
#keyframes slideInFromTheLeft-Text {
0% {
transform: translateX(100%);
}
100% {
transform: translateX(0);
}
}
.follower-container {
display: flex;
font-family: 'Roboto';
position: absolute;
overflow: hidden;
/*hide elements when they overflow*/
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-50%);
}
.left-square-container {
width: 100px;
height: 100px;
background: #0d47a1;
position: relative;
display: inline-block;
float: left;
z-index: 1;
transform: translateX(-100%);
animation: 9.6s 1 slideInFromAbove;
/* timing (.4s duration + 8s hold + .4s removal of self + animation of right + removal of right) */
-webkit-animation-fill-mode: forwards;
/* Safari 4.0 - 8.0 */
animation-fill-mode: forwards;
}
.icon img
/*THIS IS THE DIV TO CHANGE THE IMAGE ALIGNMENT*/
{
display: block;
margin: 0 auto;
webkit-filter: drop-shadow(1px 1px 1px #212121);
filter: drop-shadow(1px 1px 1px #212121);
}
.right-retangle-container {
width: 400px;
height: 100px;
opacity: 0;
background: #292929;
border-top: 5px solid #0d47a1;
box-sizing: border-box;
display: inline-block;
float: left;
position: relative;
/* needed for z-index*/
z-index: 0;
/*place under left square*/
transform: translateX(-100%);
animation: 8.8s .6s 1 slideInFromTheLeft;
/* timing (.5 initial animation duration + 8s hold + .3s removal of self) additional .6s of delay for animation of left square*/
-webkit-animation-fill-mode: forwards;
/* Safari 4.0 - 8.0 */
animation-fill-mode: forwards;
}
.text {
font-size: 30px;
color: #ffffff;
overflow: hidden;
text-align: center;
/*vertical alignment of text*/
position: relative;
/*horizontal alignment of text*/
top: 50%;
/*horizontal alignment of text*/
transform: translateY(-50%);
/*horizontal alignment of text*/
}
.text-animation {
transform: translateY(100%);
animation: .5s 1s 1 slideInFromBelow;
-webkit-animation-fill-mode: forwards;
/* Safari 4.0 - 8.0 */
animation-fill-mode: forwards;
margin-left: 20px;
margin-right: 20px;
}
.keyword:not(.user_message) {
color: #f57f17;
}
<div class="follower-container">
<div class="left-square-container">
<div class="icon">
<img class="image" src="{image}" />
</div>
</div>
<div class="right-retangle-container">
<div class="text">
<div class="text-animation">
New Follower <span class='keyword name'>{name}</span></div>
</div>
</div>
</div>
There are several ways to do this, but since you're already using flexbox, I would recommend continuing with that path.
On your .left-square-container div, simply change display to display:flex and then set align-items: center; and justify-content: center;.
Seems to work for me.
Fiddle
If you know the height of the container, you can set the line-height of said container to the value of its height.
I updated your CSS to look like so:
.icon {
text-align: center;
heignt: 100px;
line-height: 100px;
}
The "icon" div does not have any specified height. It is declared block. Hence, you cannot expect to align an image inside this div vertically as the scope of the div height-wise on the screen will be only of the size of the image.
Even in the in css of "icon", you have said margin:0 auto; -> The command will align the image in center not vertically but only horizontally. For what you want to happen, that 0 should be auto and then there should be some height of the div to see it align in the center vertically as well.

Flickering CSS animation on IE only

I have following code running fine on modern browsers, except IE11 :
A simple pseudo-element animated to rotate indefinitely.
#keyframes spin{
0% {
-ms-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-ms-transform: rotate(360deg);
transform: rotate(360deg);
}
}
#-moz-keyframes spin{
0% {
-moz-transform: rotate(0deg);
}
100% {
-moz-transform: rotate(360deg);
}
}
#-webkit-keyframes spin{
0% {
-webkit-transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
}
}
.spin-container{
position: relative;
width: 400px;
height: 300px;
margin: 2em auto;
box-sizing: border-box;
}
.spin-container::after{
content: "";
position: absolute;
display: block;
opacity: 1;
border-color: rgba(0, 0, 0, 0.2) green green rgba(0, 0, 0, 0.2);
border-radius: 100%;
border-style: solid;
border-width: 10px;
width: 100px;
height: 100px;
bottom: 50px;
right: calc(50% - 50px);
border-radius: 100%;
-webkit-animation: spin 1s linear infinite;
-moz-animation: spin 1s linear infinite;
-ms-animation: spin 1s linear infinite;
animation: spin 1s linear infinite;
}
<div class="spin-container"></div>
I've been looking for a reason for weeks now, and I cannot determinate a property that can cause this.
I first suspected a transform-origin to not be the center of my pseudo-element spinner, but it appears that default value is 50% 50% 0 for every browser.
Then I looked into z-axis modification, or crazy inheritance, but I definitely found nothing.
Does anyone know why this flickers on IE11, and not on other browsers ?
IE11 has some issues related to hardware acceleration and CSS animation:
IE 11 Leaving Artifacts and not redrawing screen
CSS Transition Property not working for SVG Elements
Microsoft is only fixing security related issues in IE11, so this will most likely remain unfixed.

How do I use CSS animations to slide a fixed position element from the bottom of the page to the top?

http://jsfiddle.net/cD4Gr/1/
This is my animation code:
#-webkit-keyframes silde_to_top {
0% {
bottom: 0%;
top: default;
}
100% {
bottom: default;
top: 0%;
z-index: 1000000;
opacity: 0.5;
}
}
#test{
-webkit-animation-name: silde_to_top;
-webkit-animation-duration: 5s;
-webkit-animation-timing-function: ease;
-webkit-animation-iteration-count: 1;
-webkit-animation-direction: normal;
-webkit-animation-delay: 0;
-webkit-animation-play-state: running;
-webkit-animation-fill-mode: forwards;
}
currently, the div just auto-starts at the top, instead of sliding to the top. the only thing that animates is the opacity.
It can't animate from a percent value to a default/auto value (or vice versa). This code gets it to work, albeit it starts offscreen:
#-webkit-keyframes silde_to_top {
0% {
top: 100%;
}
100% {
top: 0%;
z-index: 1000000;
opacity: 0.5;
}
}
Here's your fiddle example: http://jsfiddle.net/blineberry/cD4Gr/2/

CSS3 Rotating Animation with delay

Ok, so I have this rotating CSS3 animation (with a repeating timeout in the animation) almost working but I'm getting this really weird behavior where the animation seems to "jump" backward as it's animation.
I have a demo here in JS Fiddle (EDIT - Please excuse the long delay, it's a necessary part of the animation - a long timeout): http://jsfiddle.net/3mnMz/1/
For posterity, here is my CSS
#logo { position: relative; float: left; width: 175; height: 75px; margin: 0 0 16px; padding: 0; }
#-webkit-keyframes rotate {
0%, 65%, 75%, 100% {
-webkit-transform: rotate(0deg);
}
70% {
-webkit-transform: rotate(360deg);
-webkit-animation-timing-function: ease-in-out;
-webkit-animation-delay: 3s;
}
}
#logo span.star
{
-webkit-animation-name: rotate;
-webkit-animation-duration: 6s;
-webkit-animation-iteration-count: infinite;
}
#logo span.star { width: 84px; height: 84px; background: url('../img/logo_star.png') no-repeat left top; position: absolute; top: -8px; right: -20px; display: block;
}
Can someone shed some light on the subject?
I'm not sure about what you're trying to achieve, but the reason why it's rotating back and forth is because you're stating at keyframe 70% that the rotation is 360, then at 75 that it's rotation 0, so it goes back to the original state.
The animation properties should also be stated within the span.star element, not within the keyframes.
Here is a demo:
http://jsfiddle.net/3VrjE/

Can't stop animation at end of one cycle [duplicate]

This question already has answers here:
Stopping a CSS3 Animation on last frame
(8 answers)
Closed 7 years ago.
I'm making a CSS animation at the minute, and in it I'm moving stuff and want it to stay at the end position until the user moves their mouse away.
body {
background: url('osx.jpg');
padding: 0;
margin: 0;
line-height: 60px;
}
#-webkit-keyframes item1 {
0% { bottom: -120px; left: 0px; }
10% { bottom: -40px; left: 10px; -webkit-transform: rotate(5deg); }
100% { bottom: -40px; left: 10px; -webkit-transform: rotate(5deg); }
}
#-webkit-keyframes item2 {
0% { bottom: -120px; left: 0px; }
10% { bottom: 60px; left: 20px; -webkit-transform: rotate(7deg); }
100% { bottom: 60px; left: 20px; -webkit-transform: rotate(7deg); }
}
#-webkit-keyframes item3 {
0% { bottom: -120px; left: 0px; }
10% { bottom: 160px; left: 30px; -webkit-transform: rotate(9deg); }
100% { bottom: 160px; left: 30px; -webkit-transform: rotate(9deg); }
}
div {
position: relative;
}
#folder {
width: 120px;
margin: 0px auto;
}
#folder > div {
position: absolute;
}
#folder:hover > div:nth-of-type(1) {
-webkit-animation-name: item1;
-webkit-animation-duration: 10s;
}
#folder:hover > div:nth-of-type(2) {
-webkit-animation-name: item2;
-webkit-animation-duration: 10s;
}
#folder:hover > div:nth-of-type(3) {
-webkit-animation-name: item3;
-webkit-animation-duration: 10s;
}
Whenever the animation ends though, it just repeats itself. I only want it to happen once and stay where it is until the user moves away. I tried using the paused thing from the spec but it doesn't work as I'd expect it to. Any help is appreciated. Thanks. :)
The following comment worked for me. Thanks michael
"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
Just in case your animation still resets to the first frame, be careful with the order in which you declare the css:
This is working fine:
animation: yourAnimationName 1s;
animation-fill-mode: forwards;
This won't (reset to first frame):
animation-fill-mode: forwards;
animation: yourAnimationName 1s;
Tricky!
If I understand the question correctly, it sounds like you need to set the iteration to '1'.
-webkit-animation-iteration-count: 1;
The css3 animations reset to the default style at the end of the animation. You can try something with javascript:
You can add an eventListener to your animation, retrieve the styles you wish to keep, apply them manually and remove the animation.
document.getElementById('yourdiv').addEventListener(
'webkitAnimationEnd',
function(){
document.getElementById('yourdiv').style.backgroundPosition = '0px 0px';
document.getElementById('yourdiv').style.webkitAnimationName = 'none';
},
false
);
try this:
-webkit-animation-duration: 10s, 10s;
:)

Resources