Smooth #keyframes animation goes discrete on Safari - html

I built a preloading screen for a website with a loading bar that is animated with CSS #keyframes. Works fine on Chrome and Firefox, but on macOS Safari it gets very discrete. Here is a video demo of how it looks on Safari: https://www.youtube.com/watch?v=ODV5lN2xZSI&feature=youtu.be
As you can see, loading bar background (gray line) and the bar itself (black line) twitch instead of going smoothly from 0% width to 100%. What could be a problem, is this known bug of Safari? Latest macOS and Safari.
#keyframes loading-wrapper-anim {
0% {
width:0%;
}
100% {
width:100%;
}
}
.preloader .loading_wrapper {
position:absolute;
width:0%;
height:1px;
background:#dbdbdb;
top:12rem;
animation: loading-wrapper-anim 1s;
animation-delay: 1s;
animation-fill-mode: forwards;
align-self:flex-start; /*this one is because of the parent element*/
}
.preloader .loading_wrapper .loading_bar {
height:100%;
width:0%;
height:100%;
background:#000;
animation: loading-wrapper-anim 3s;
animation-delay: 2s;
animation-fill-mode: forwards;
}
<div class="preloader">
<div class="loading_wrapper">
<div class="loading_bar">
</div>
</div>
</div>
Smooth animation is expected.
Thank you.

You can attempt to force the hardware acceleration by adding a translateZ on the animation.
.preloader .loading_wrapper {
position:absolute;
width:0%;
height:1px;
background:#dbdbdb;
top:12rem;
animation: loading-wrapper-anim 1s;
animation-delay: 1s;
animation-fill-mode: forwards;
align-self:flex-start;
/* Add this */
-webkit-transform: translateZ(0);
}
JSFiddle
Alternatively, you can look into using the will-change method as a last resort for smoother animations.
https://developer.mozilla.org/en-US/docs/Web/CSS/will-change

The way I fixed it is instead of trying to manipulate width of an element (which causes redrawing each time the width changes), did the following:
#keyframes loading-wrapper-anim {
0% {
transform:scaleX(0);
}
100% {
transform:scaleX(1);
}
}
.preloader .loading_wrapper {
position:absolute;
width:100%;
height:1px;
background:#dbdbdb;
top:12rem;
animation: loading-wrapper-anim 1s;
animation-delay: 1s;
animation-fill-mode: forwards;
align-self:flex-start; /*this one is because of the parent element*/
transform:scaleX(0);
transform-origin:0% 0%;
}
.preloader .loading_wrapper .loading_bar {
height:100%;
width:100%;
height:100%;
background:#000;
animation: loading-wrapper-anim 3s;
animation-delay: 2s;
animation-fill-mode: forwards;
transform:scaleX(0);
transform-origin:0% 0%;
}
I used transform:scaleX() in conjunction with transform-origin:0% 0% (this one sets center of transformation to the top left corner) to emulate width change without actually changing it.
Conclusion: use transform where/when possible. They are more efficient in terms of CSS animations and transitions.

Related

Setting a keyframe value as an offset?

Let's say I have a div with a specified height of 100px and I want to animate it so it grows by a fixed 20px height.
The snippet below shows how I implemented it, successfully.
#keyframes foo {
0% {
height: 100px;
}
50% {
height: 120px;
}
100% {
height: 100px;
}
}
#foo1 {
background-color:blue;
width:100px;
height:100px;
animation-name: foo;
animation-duration: 1.4s;
animation-iteration-count: infinite;
animation-direction: alternate;
animation-timing-function: ease-in-out;
}
<div id='foo1'>
But what if I need to set the height property outside the CSS, so that it's not a specific value of 100px and it can be changed to any other value, but still, I want to animate an increasing height of a fixed 20px ?
Is there a way to set the animaton value as an offset of the original element value?
Increase the padding if you will not have any content and it's a simple visual animation:
#keyframes foo {
0%,100% {
padding-bottom: 0px;
}
50% {
padding-bottom: 20px;
}
}
#foo1 {
background-color:blue;
width:100px;
height:100px;
animation-name: foo;
animation-duration: 1.4s;
animation-iteration-count: infinite;
animation-direction: alternate;
animation-timing-function: ease-in-out;
}
<div id='foo1'>
Or use CSS variables:
#keyframes foo {
0%,100% {
height:var(--h,100px)
}
50% {
height:calc(var(--h,100px) + 20px);
}
}
#foo1 {
background-color:blue;
width:100px;
height:var(--h,100px);
display:inline-block;
animation-name: foo;
animation-duration: 1.4s;
animation-iteration-count: infinite;
animation-direction: alternate;
animation-timing-function: ease-in-out;
}
<div id='foo1'></div>
<div id='foo1' style="--h:50px;"></div>
I don't think there is a direct way, but you have several workarounds:
use padding (e.g. padding-bottom) to extend the element's height – only applicable if the element is not supposed to have text content flowing into the padding
use border – same as above
use an extra element, e.g. ::after
use JavaScript – if you are already setting the height "outside" of CSS, it may be that you are setting it with JS?

Element looking weird with animation set two times

So I got this pen: https://codepen.io/tobiasglaus/pen/NedpxQ
Whenever it "clicks", a circle should be animated. There are 2 clicks in the animation, so I just added the animation 2 times, like this:
animation: circle .3s forwards, circle .3s forwards;
animation-delay: 1.7s, 4.9s;
The problem is, that the circle isn't a circle anymore, but a blurry square:
But it should look like this:
I can't replicate the problem in a SO-snippet, but since I need to provide a minimal code example, here's the snippet of how it should look.
Note: When I view the animation with the Chrome DevTools, the animation looks correct.
.circle:after{
content:"";
position:absolute;
top:20px;
left:20px;
width:50px;
height:50px;
border-radius:50%;
border:2px solid #222f3e;
animation:circle .3s forwards, circle .3s forwards;
animation-delay:0s, 1s;
opacity:0;
transform:scale(0);
}
#keyframes circle {
0%{
transform:scale(0);
}
50%{
opacity:1;
}
100%{
transform:scale(1);
opacity:0;
}
}
<div class="circle"></div>
You can make use of the animation-iteration-count property, like so:
animation: circle .3s forwards;
animation-iteration-count: 2;
Animation-iteration-count sets the number of times an animation cycle should be played before stopping. You want the click to happen twice, so we set the value to 2.
I still don't know why the circle rendered weirdly, but I managed to come up with a solution that works:
That's what I had:
animation: circle .3s forwards, circle .3s forwards;
animation-delay: 1.7s, 4.9s;
and the animation:
#keyframes circle {
0%{
transform:scale(0);
}
50%{
opacity:1;
}
100%{
transform:scale(1);
opacity:0;
}
}
Instead of calling the animation twice, I created one big animation:
animation: circle 3.5s forwards;
animation-delay:1.7s;
and that's the animation:
#keyframes circle{
0%{
transform:scale(0);
}
4%{
opacity:1;
}
8%{
transform:scale(1);
opacity:0;
}
92%{
transform:scale(0);
opacity:0;
}
96%{
opacity:1;
}
100%{
transform:scale(1);
opacity:0;
}
}
So the initial animation duration of 0.3s is now equal to 8% and the animation delay between the animations is equal to the 74% of nothing in the animation.

Animation delay property to change content at fixed intervals

I am trying to animate two pictures so that they change at fixed intervals but the problem is that the second image appears quickly and fades I need a way to make the delay property to repeat i have refereed this but that doesn't seem to work CSS animation delay in repeating
http://jsfiddle.net/fc3nb5rL/2/
I think my problem is somewhere here
#-webkit-keyframes anim {
from {
z-index:1;
}
to {
z-index:-2;
}
}
.back {
-webkit-animation:anim 5s;
-webkit-animation-iteration-count:infinite;
-webkit-animation-timing-function: ease-in-out;
}
#-webkit-keyframes anim2 {
from {
z-index:1;
}
to {
z-index:-2;
}
}
First fix your HTML(markup), then you can animate the opacity and not the z-index
.container {
position:relative;
height:500px;
width:500px;
margin:0 auto;
}
.container img {
position:absolute;
width:100%;
}
#-webkit-keyframes anim1 {
from {
opacity:0;
}
to {
opacity:1;
}
}
#-webkit-keyframes anim2 {
from {
opacity:1;
}
to {
opacity:0;
}
}
[href=first] img {
opacity:0;
/*animation----:----name--duration--delay--timing function---direction----iteration count*/
-webkit-animation: anim1 2s 0s linear alternate infinite;
}
[href=second] img {
opacity:1;
-webkit-animation: anim2 2s 0s linear alternate infinite;
}
<div class="container">
<a href="second">
<img src="http://placekitten.com/300/300" />
</a>
<a href="first">
<img class="front" src="http://placekitten.com/300/301" />
</a>
</div>
Give this a shot. I have also set it up to be cross-browser compatible. http://jsfiddle.net/fc3nb5rL/2/
A few things to note about CSS3 transitions. It does not know how to interpolate between the z-index
property, as well as the display property.

Image positioning seems strange

When I insert an image into my HTML it gets positioned in the lower left corner for some reason. Even if I set position to center; it stays in that strange position. What could be causing this?
My code:
<!DOCTYPE HTML>
<html>
<header>
<title>Animation Verkefni</title>
<link type="text/css" href="stylesheet2.css" rel="stylesheet"/>
</header>
<body>
<div class="doge1">
<p>
Transitions in CSS are applied to an element and specify that when a property changes it should do so gradually over a period of time. Animations are different. When applied, they just run and do their thing. They offer more fine-grained control as you can control different stops of the animations.
</p>
</div>
<div class="doge2">
<img src="spengbab.jpg">
</div>
</body>
</html>
css:
body {
background-color:gray;
}
p {
font-size:50px;
margin-left:500px;
margin-right:500px;
text-align:center;
margin-top:250px;
font-family:impact;
}
#keyframes myfirst
{
0% {opacity:1;}
25% {opacity:2;}
50% {opacity:3;}
75% {opacity:4;}
100% {opacity:10;}
}
#-webkit-keyframes myfirst /* Safari og Chrome */
{
0% {opacity:1;}
25% {opacity:2;}
50% {opacity:3;}
75% {opacity:4;}
100% {opacity:10;}
}
.doge2 {
position:fixed center;
top:20px;
}
.doge1:hover
{
animation-name: myfirst;
animation-duration: 5s;
animation-timing-function: linear;
animation-delay: 1s;
animation-iteration-count: infinite;
animation-direction: alternate;
animation-play-state: running;
/* Safari og Chrome: */
-webkit-animation-name: myfirst;
-webkit-animation-duration: 5s;
-webkit-animation-timing-function: linear;
-webkit-animation-delay: 1s;
-webkit-animation-iteration-count: infinite;
-webkit-animation-direction: alternate;
-webkit-animation-play-state: running;
}
Thanks!
Try using position:fixed; & text-align:center;
Like this:
.doge2 {
position:fixed;
top:20px;
width:100%;
text-align:center;
}
Try with
vertical-align:middle;
to keep image at center position.

How to play CSS3 transitions in a loop?

The following style is just an example of how to set transitions in CSS3. Is there a pure CSS trick to make this play in loop?
div {
width:100px;
height:100px;
background:red;
transition:width 0.1s;
-webkit-transition:width 0.1s; /* Safari and Chrome */
-moz-transition:width 0.1s; /* Firefox 4 */
-o-transition:width 0.1s; /* Opera */
transition:width 0.1s; /* Opera */
}
div:hover {
width:300px;
}
CSS transitions only animate from one set of styles to another; what you're looking for is CSS animations.
You need to define the animation keyframes and apply it to the element:
#keyframes changewidth {
from {
width: 100px;
}
to {
width: 300px;
}
}
div {
animation-duration: 0.1s;
animation-name: changewidth;
animation-iteration-count: infinite;
animation-direction: alternate;
}
Check out the link above to figure out how to customize it to your liking, and you'll have to add browser prefixes.
If you want to take advantage of the 60FPS smoothness that the "transform" property offers, you can combine the two:
#keyframes changewidth {
from {
transform: scaleX(1);
}
to {
transform: scaleX(2);
}
}
div {
animation-duration: 0.1s;
animation-name: changewidth;
animation-iteration-count: infinite;
animation-direction: alternate;
}
More explanation on why transform offers smoother transitions here:
https://medium.com/outsystems-experts/how-to-achieve-60-fps-animations-with-css3-db7b98610108