SCSS Passing variable to keyframe inside a mixin - sass

I'm trying to create an animation which involves moving several cloud at different speeds to animate a sky. In an attempt to create reusable code I have moved to mixins but I seem to have run in a problem.
The clouds have different starting positions (e.g defined by right: 100px - passed at $startpos).
On the initial pageload the clouds are at the correct position but the animation starts from a random right position.
my scss code is looking like this
#keyframes fadein {
from { opacity: 0 }
to { opacity: 1 }
}
#mixin cloud($height, $width, $bg, $startpos, $slowanim, $fastanim) {
background: url($bg) no-repeat;
background-size: contain;
border: none;
height: $height; // 800
width: $width; // 800
position: absolute;
right: $startpos;
opacity: 1;
animation: movecloudA $fastanim infinite;
&.animate {
animation: fadein 5s, movecloud $slowanim infinite;
opacity: 1;
}
#include keyframes(movecloud) {
0% { right: $startpos; }
100% { right: 100%; animation: movecloud $slowanim infinite;}
}
}
.cloudA {
#include cloud(800px, 800px, 'assets/cloudA.svg', -100px, 5s, 1s)
bottom: -400px;
}
.cloudB {
#include cloud(1200px, 1200px, 'assets/cloudB.svg', -1500px, 9s, 5s)
bottom: -800px;
transform: rotate(360deg);
}
the behaviour can be reproduced on https://meshto.space/, after hovering on the exclamation mark

So I managed to figure this out with some more experimenting.
It seems that keyframe animations are not wrapped up inside a scope and need to have a unique name. The behavior above was actually not random and changed the right offset to -1500 for both clouds once the animation was started.
i.e. all your animations need to have unique names.
The code above was changed to
#keyframes fadein {
from { opacity: 0 }
to { opacity: 1 }
}
#mixin cloud($bg, $anim, $height, $width, $startpos, $slowanim, $fastanim) {
background: url($bg) no-repeat;
background-size: contain;
border: none;
height: $height; // 800
width: $width; // 800
position: absolute;
right: $startpos;
opacity: 1;
// animation: movecloudA $fastanim infinite;
&.animate {
animation: fadein 5s, $anim $slowanim infinite;
opacity: 1;
}
#include keyframes($anim) {
0% { right: $startpos; }
100% { right: 100% }
}
}
.cloudA {
#include cloud('assets/cloudA.svg', cloudAnimA, 800px, 800px, -100px, 5s, 1s)
bottom: -400px;
}
.cloudB {
#include cloud('assets/cloudB.svg', cloudAnimB, 1200px, 1200px, -1500px, 9s, 5s)
bottom: -800px;
transform: rotate(360deg);
}
Would appreciate if there are neater solutions to this problem :). Ty

Related

Poor CSS Animation performance - no browser paints

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.

How to delay a css animation which should start after a particular time

I am using steps() in my css animation. Now I want to start the css animation after a particular time. Below is an example.
#-webkit-keyframes typing {
from { width: 0 }
to { width:455px; }
}
#-moz-keyframes typing {
from { width: 0 }
to { width:16.3em }
}
#-webkit-keyframes blink-caret {
from, to { border-color: transparent }
50% { border-color: black }
}
#-moz-keyframes blink-caret {
from, to { border-color: transparent }
50% { border-color: black }
}
.typing_animation h3 {
position: absolute;
font-size: 36px;
width:455px;
left: 0;
top:110px;
font-style: italic;
display: inline-block;
margin-left: -48px;
letter-spacing: 4px;
white-space: nowrap;
overflow: hidden;
border-right: 1px solid #000;
-webkit-animation: typing 10s steps(25, end), blink-caret 1s step-end infinite;
-moz-animation: typing 10s steps(25, end), blink-caret 1s step-end infinite;
}
The above one results in a cool typing animation. However, it starts right away. I want to hold the typing and start it after a delay of say 5 seconds.
Thanks
PS: A Fiddle in case that's useful
Maybe it's late but this is what you need:
Change your element width from "455px" to "0" ;
Then add "animation-fill-mode: forwards" into your ".typing_animation h3" class, so this css rule force to animation can change the width;
Add your desired delay like this: "animation-delay: 3s".
Notice: Don't forget prefixes and if you use several animations on a element, it's better to use a shorthand animation property. see tutorials.
This is a example: jsfiddle
/* typing animation */
#keyframes typing {from {width: 0} to {width:19em}}
#-webkit-keyframes typing {from {width: 0} to {width:19em}}
#-moz-keyframes typing {from {width: 0} to {width:19em}}
/* blinking animation */
#keyframes blink-caret {from, to {border-color: transparent} 50% {border-color: #000000}}
#-webkit-keyframes blink-caret {from, to {border-color: transparent} 50% {border-color: #000000}}
#-moz-keyframes blink-caret {from, to {border-color: transparent} 50% {border-color: #000000}}
.my-animation {
width: 0;
overflow: hidden;
white-space:nowrap;
border-right: 0.1em solid #000000;
animation: typing 6s steps(30, end) 4s forwards, blink-caret 1s step-end 4s infinite;
-webkit-animation: typing 6s steps(30, end) 4s forwards, blink-caret 1s step-end 4s infinite;
-moz-animation: typing 6s steps(30, end) 4s forwards, blink-caret 1s step-end 4s infinite;
}
Try the animation-delay property:
-webkit-animation-delay: 5s;
animation-delay: 5s;
Note: Add this to the end of your h3 rule
you can use setTimeout function and add class to the particular element
setTimeout(function(){
$('p').addClass('text-show')
}, 3000);
p
{
font-size:60px;
}
.text-show
{
animation: textshow 2s ease-out forwards;
}
#keyframes textshow {
0% {
transform: scale3d(0.6, 0.6, 0.6);
opacity: 0;
}
100% {
transform: scale3d(1, 1, 1);
opacity: 1;
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>We Design.</p>
thanks,
Jomin George paul

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