Alternative to keyframes in CSS? - javascript

I need to create a loading spinner, at the moment I am using the following code.
I would like to know if is possible to rewrite it using an alternative syntax for keyframes (maybe transitions?).
Unfortunately I am using a build tool which rewrite keyframes properties adding bugs and the CSS generated is not working so I would like to work around the problem.
A JS solution is also possible, adding CSS inline.
.loadingSpinner {
width: 50px;
height: 50px;
border: 5px solid #3498db;
border-top-color: rgba(0, 0, 0, 0);
border-left-color: rgba(0, 0, 0, 0);
border-radius: 50%;
-moz-animation: loadingSpinner 0.7s infinite linear;
-o-animation: loadingSpinner 0.7s infinite linear;
-webkit-animation: loadingSpinner 0.7s infinite linear;
animation: loadingSpinner 0.7s infinite linear;
}
#-moz-keyframes loadingSpinner {
0% {
-moz-transform: rotate(0deg);
}
100% {
-moz-transform: rotate(360deg);
}
}
#-o-keyframes loadingSpinner {
0% {
-o-transform: rotate(0deg);
}
100% {
-o-transform: rotate(360deg);
}
}
#-webkit-keyframes loadingSpinner {
0% {
-webkit-transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
}
}
#keyframes loadingSpinner {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
<div class="loadingSpinner"></div>

You could use a very long transition and trigger it with a quick js line.
For example, add a .start class that triggers a 60 second transition that rotates the spinner several times (for example, 36000deg).
.loadingSpinner {
width: 50px;
height: 50px;
border: 5px solid #3498db;
border-top-color: rgba(0, 0, 0, 0);
border-left-color: rgba(0, 0, 0, 0);
border-radius: 50%;
transform: rotate(0deg);
transition: transform 60s;
}
.loadingSpinner.start {
transform: rotate(36000deg);
}
Example: https://jsfiddle.net/6tkf1f95/1/

what about only css solution?
#keyframes spinner {
to {transform: rotate(360deg);}
}
#-webkit-keyframes spinner {
to {-webkit-transform: rotate(360deg);}
}
.spinner {
min-width: 30px;
min-height: 30px;
}
.spinner:before {
content: 'Loading…';
position: absolute;
top: 50%;
left: 50%;
width: 50px;
height: 50px;
margin-top: -13px;
margin-left: -13px;
}
.spinner:not(:required):before {
content: '';
border-radius: 50%;
border: 5px solid #ccc;
border-top-color: #03ade0;
animation: spinner .7s linear infinite;
-webkit-animation: spinner .7s linear infinite;
}
<div class="spinner"></div>

You can create an infinite CSS transition using a single CSS Custom Property, the value of which you can update every time the JavaScript transitionend event fires.
In the CSS stylesheet, we can start with the following transform and transition:
transform: var(--rotation);
transition: transform 0.7s linear;
We will need to initialise --rotation, so we can do that at the top of the stylesheet:
:root {
--rotation: rotate(0deg);
}
So far, so good. Now we need a JS function which moves the rotation value up by 360deg every time the transition completes:
const rotateSpinner = () => {
let loadingSpinnerStyles = window.getComputedStyle(loadingSpinner);
let rotation = loadingSpinnerStyles.getPropertyValue('--rotation');
rotation = parseInt(rotation.replace('rotate(', '').replace('deg)', '')) + 360;
rotation = 'rotate(' + rotation + 'deg)';
loadingSpinner.style.setProperty('--rotation', rotation);
}
When we put everything together, we get the working example below.
Working Example:
const loadingSpinner = document.querySelector('.loadingSpinner');
const rotateSpinner = () => {
let loadingSpinnerStyles = window.getComputedStyle(loadingSpinner);
let rotation = loadingSpinnerStyles.getPropertyValue('--rotation');
rotation = parseInt(rotation.replace('rotate(', '').replace('deg)', '')) + 360;
rotation = 'rotate(' + rotation + 'deg)';
loadingSpinner.style.setProperty('--rotation', rotation);
}
loadingSpinner.addEventListener('transitionend', rotateSpinner);
window.addEventListener('load', rotateSpinner);
:root {
--rotation: rotate(0deg);
}
.loadingSpinner {
width: 50px;
height: 50px;
border: 5px solid #3498db;
border-top-color: rgba(0, 0, 0, 0);
border-left-color: rgba(0, 0, 0, 0);
border-radius: 50%;
transform: var(--rotation);
transition: transform 0.7s linear;
}
<div class="loadingSpinner"></div>

Related

Adding and removing classes on click of a button to animate divs

I have 2 words that I am trying to animate on the click of a button, it does this by adding a class to the div's. But I can only get it to do it every other button press, I would like it to play every time I press the button.
function animateWords() {
word1.classList.add('puffIn');
word2.classList.add('vanishIn');
}
I have tried removing the classes first but that doesn't work, the only way I can get it to do every other time is by doing an if statement that checks if the classList contains the classes and removes them if not it adds them. But this only works every other time.
Any help would be great
Cheers
this might be a solution to your question/problem.
I have added some styling to the words so the animation will be better visible. The removing of the class is done by a timeout function (so don't need to use an if statement).
function animateWords() {
let word1 = document.getElementById('word1');
let word2 = document.getElementById('word2');
word1.classList.add('puffIn');
word2.classList.add('vanishIn');
var wait = window.setTimeout(function() {
word1.classList.remove('puffIn');
word2.classList.remove('vanishIn');
}, 1500);
}
p {
display: block;
float: left;
padding: 1px;
background: #5c5c5c;
border: 2px solid #f6f6f6;
color: #f6f6f6;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
}
.wordsAnimation {
-webkit-animation-duration: 2s;
animation-duration: 2s;
-webkit-animation-fill-mode: both;
animation-fill-mode: both;
}
.puffIn {
-webkit-animation-name: puffIn;
animation-name: puffIn;
}
.vanishIn {
-webkit-animation-name: vanishIn;
animation-name: vanishIn;
}
#-webkit-keyframes puffIn {
0% {
opacity: 0;
-webkit-transform-origin: 50% 50%;
-webkit-transform: scale(2, 2);
-webkit-filter: blur(2px);
}
100% {
opacity: 1;
-webkit-transform-origin: 50% 50%;
-webkit-transform: scale(1, 1);
-webkit-filter: blur(0px);
}
}
#keyframes puffIn {
0% {
opacity: 0;
transform-origin: 50% 50%;
transform: scale(2, 2);
filter: blur(2px);
}
100% {
opacity: 1;
transform-origin: 50% 50%;
transform: scale(1, 1);
filter: blur(0px);
}
}
#-webkit-keyframes vanishIn {
0% {
opacity: 0;
-webkit-transform-origin: 50% 50%;
-webkit-transform: scale(2, 2);
-webkit-filter: blur(90px);
}
100% {
opacity: 1;
-webkit-transform-origin: 50% 50%;
-webkit-transform: scale(1, 1);
-webkit-filter: blur(0px);
}
}
#keyframes vanishIn {
0% {
opacity: 0;
transform-origin: 50% 50%;
transform: scale(2, 2);
-webkit-filter: blur(90px);
}
100% {
opacity: 1;
transform-origin: 50% 50%;
transform: scale(1, 1);
-webkit-filter: blur(0px);
}
}
<p id="word1" class="wordsAnimation">Puff in</p>
<p id="word2" class="wordsAnimation">Vanish in</p>
<button onclick="animateWords()"> Animate </button>
function myFunction(){
let x=document.getElementById('question-header')
if(x.classList.contains('redclass')){
x.classList.remove('redclass')
x.classList.add('blueclass')
}else {
x.classList.remove('blueclass')
x.classList.add('redclass')
}
}
.redclass{
background: red;
}
.blueclass{
background: orange;
}
<div id="question-header">
<h1 itemprop="name" class="">Adding and removing classes on click of a button to animate divs</h1>
</div>
<button onclick="myFunction()">animate me</button>
Here is a IMHO bizarre answer. I'm expanding upon #tmach's answer, but instead of using a setTimeout to remove the classes, I'm forcing a DOM reflow. Now you can press the animate button to restart the animation while it animates.
function animateWords() {
let word1 = document.getElementById('word1');
let word2 = document.getElementById('word2');
word1.classList.remove('puffIn');
word2.classList.remove('vanishIn');
word1.offsetWidth; /* Magic! (forced reflow) */
word1.classList.add('puffIn');
word2.classList.add('vanishIn');
}
p {
display: block;
float: left;
padding: 1px;
background: #5c5c5c;
border: 2px solid #f6f6f6;
color: #f6f6f6;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
}
.wordsAnimation {
-webkit-animation-duration: 2s;
animation-duration: 2s;
-webkit-animation-fill-mode: both;
animation-fill-mode: both;
}
.puffIn {
-webkit-animation-name: puffIn;
animation-name: puffIn;
}
.vanishIn {
-webkit-animation-name: vanishIn;
animation-name: vanishIn;
}
#-webkit-keyframes puffIn {
0% {
opacity: 0;
-webkit-transform-origin: 50% 50%;
-webkit-transform: scale(2, 2);
-webkit-filter: blur(2px);
}
100% {
opacity: 1;
-webkit-transform-origin: 50% 50%;
-webkit-transform: scale(1, 1);
-webkit-filter: blur(0px);
}
}
#keyframes puffIn {
0% {
opacity: 0;
transform-origin: 50% 50%;
transform: scale(2, 2);
filter: blur(2px);
}
100% {
opacity: 1;
transform-origin: 50% 50%;
transform: scale(1, 1);
filter: blur(0px);
}
}
#-webkit-keyframes vanishIn {
0% {
opacity: 0;
-webkit-transform-origin: 50% 50%;
-webkit-transform: scale(2, 2);
-webkit-filter: blur(90px);
}
100% {
opacity: 1;
-webkit-transform-origin: 50% 50%;
-webkit-transform: scale(1, 1);
-webkit-filter: blur(0px);
}
}
#keyframes vanishIn {
0% {
opacity: 0;
transform-origin: 50% 50%;
transform: scale(2, 2);
-webkit-filter: blur(90px);
}
100% {
opacity: 1;
transform-origin: 50% 50%;
transform: scale(1, 1);
-webkit-filter: blur(0px);
}
}
<p id="word1" class="wordsAnimation">Puff in</p>
<p id="word2" class="wordsAnimation">Vanish in</p>
<button onclick="animateWords()"> Animate </button>

loading animation not happening in container

I have a working example of a page loading animation when the user hits submit on the form here. Im trying to change the animation to something different but the newer animation is not happening inside the container and is loading even before the user hits submit on that form. The non working example can be found here
Can someone help me understand why this animation is not happening inside the container and why its being run when the page loads?
var myForm = document.getElementById('needs-validation');
myForm.addEventListener('submit', showLoader);
function showLoader(e) {
this.querySelector('.loader-container').style.display = 'block';
// the line below is just for the demo, it stops the form from submitting
// so that you can see it works. Don't use it
e.preventDefault();
}
#needs-validation {
/* .loader-container will be positionned relative to this */
position: relative;
}
.loader-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.loader {
display: block;
position: relative;
left: 50%;
top: 50%;
width: 150px;
height: 150px;
margin: -75px 0 0 -75px;
border-radius: 50%;
border: 3px solid transparent;
border-top-color: #9370DB;
-webkit-animation: spin 2s linear infinite;
animation: spin 2s linear infinite;
}
.loader:before {
content: "";
position: absolute;
top: 5px;
left: 5px;
right: 5px;
bottom: 5px;
border-radius: 50%;
border: 3px solid transparent;
border-top-color: #BA55D3;
-webkit-animation: spin 3s linear infinite;
animation: spin 3s linear infinite;
}
.loader:after {
content: "";
position: absolute;
top: 15px;
left: 15px;
right: 15px;
bottom: 15px;
border-radius: 50%;
border: 3px solid transparent;
border-top-color: #FF00FF;
-webkit-animation: spin 1.5s linear infinite;
animation: spin 1.5s linear infinite;
}
#-webkit-keyframes spin {
0% {
-webkit-transform: rotate(0deg);
-ms-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
-ms-transform: rotate(360deg);
transform: rotate(360deg);
}
}
#keyframes spin {
0% {
-webkit-transform: rotate(0deg);
-ms-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
-ms-transform: rotate(360deg);
transform: rotate(360deg);
}
}
<form id="needs-validation">
<p>First name: <input type="text"></p>
<p>Last name: <input type="text"></p>
<p>Age: <input type="text"></p>
<button type="submit">Next</button>
<!-- insert your loader container here, inside the <form> element -->
<div class="loader-container">
<div class="loader"></div>
</div>
</form>
There was something messy in your .loader-container
Actually only thing I have done, I haave taken this class from working solution and added into the new one and it works.
In your new solution, you are missing the display:none at first place.
Here is an updated solution for you.
https://jsfiddle.net/v9dhqvrc/

Rails: Infinite Scroll, load spinner instead of text

All works properly, but I want to show a spinner loading instead of text. This is my code in Page.js.coffee:
jQuery ->
if $('.pagination').length
$(window).scroll ->
url = $('.pagination .next_page').attr('href')
if url && $(window).scrollTop() > $(document).height() - $(window).height() - 50
$('.pagination').text("Fetching more...")
$.getScript(url)
$(window).scroll().
This line show the text:
$('.pagination').text("Fetching more...")
Thank you guys.
Perhaps css #keyframes suits just fine for your case, you can check possible implementation here https://projects.lukehaas.me/css-loaders/
for example you can take
.loader,
.loader:before,
.loader:after {
border-radius: 50%;
}
.loader {
color: #ffffff;
font-size: 11px;
text-indent: -99999em;
margin: 55px auto;
position: relative;
width: 10em;
height: 10em;
box-shadow: inset 0 0 0 1em;
-webkit-transform: translateZ(0);
-ms-transform: translateZ(0);
transform: translateZ(0);
}
.loader:before,
.loader:after {
position: absolute;
content: '';
}
.loader:before {
width: 5.2em;
height: 10.2em;
background: #0dc5c1;
border-radius: 10.2em 0 0 10.2em;
top: -0.1em;
left: -0.1em;
-webkit-transform-origin: 5.2em 5.1em;
transform-origin: 5.2em 5.1em;
-webkit-animation: load2 2s infinite ease 1.5s;
animation: load2 2s infinite ease 1.5s;
}
.loader:after {
width: 5.2em;
height: 10.2em;
background: #0dc5c1;
border-radius: 0 10.2em 10.2em 0;
top: -0.1em;
left: 5.1em;
-webkit-transform-origin: 0px 5.1em;
transform-origin: 0px 5.1em;
-webkit-animation: load2 2s infinite ease;
animation: load2 2s infinite ease;
}
#-webkit-keyframes load2 {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
#keyframes load2 {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
and then in your coffescript just add and remove the class "loader" loading div, or add/remove div with such a class.
<div class="loader">Loading...</div>

Reverse backward animation doesn't apply properly on mouseout event

I'm trying to apply animation on mouseover event (works fine) and the same animation with reverse and backwards properties, to play it back on mouseleave. But the second part doesn't work properly. Finally i want the animation plays forward on mouseover and backward on mouseleave. And if there is a way to apply next animation from that point, where the previous was stopped, please, include it in your answer. Here is my code:
const target = document.getElementById("animated");
target.addEventListener("mouseover", animateForward);
target.addEventListener("mouseout", animateBackward);
function animateForward() {
target.style.animation = 'custom 1.6s forwards';
}
function animateBackward() {
target.style.animation = 'custom 1.6s reverse backwards';
}
/* Safari 4.0 - 8.0 */
#-webkit-keyframes custom {
25% {
border-radius: 50% 0 0 0;
}
50% {
border-radius: 50% 50% 0 0;
}
75% {
border-radius: 50% 50% 50% 0;
}
100% {
border-radius: 50% 50% 50% 50%;
background-color: violet;
}
}
/* Standard syntax */
#keyframes custom {
25% {
border-radius: 50% 0 0 0;
}
50% {
border-radius: 50% 50% 0 0;
}
75% {
border-radius: 50% 50% 50% 0;
}
100% {
border-radius: 50% 50% 50% 50%;
background-color: violet;
-moz-transform: rotate(180deg);
-webkit-transform: rotate(180deg);
-o-transform: rotate(180deg);
-ms-transform: rotate(180deg);
transform: rotate(180deg);
}
}
div {
border: 1px black solid;
width: 100px;
height: 100px;
margin: 50px;
}
<div id="animated"></div>
And here is JsFiddle.
For a CSS only solution, leave the #keyframes, and use transitions instead. When dealing with :hover, transitions are almost always what you really need.
E.g, all the properties of your animation can be set independently, and thus they can have their own transition rules.
So your animation could be converted by the following transition, where each keyframe has been replaced by a trio transition-property-transtion-duration-transition-delay.
div {
border: 1px black solid;
width: 100px;
height: 100px;
margin: 50px;
/* define all the props */
transition-property:
transform,
background-color,
border-top-left-radius,
border-top-right-radius,
border-bottom-right-radius,
border-bottom-left-radius;
/* set their duration independently */
transition-duration: 1.6s, 1.6s, 0.4s, 0.4s, 0.4s, 0.4s;
/* same for delays */
transition-delay: 0s, 0s, 0s, 0.4s, 0.8s, 1.2s;
}
div:hover{
border-radius: 50%;
background-color: violet;
transform: rotate(180deg);
}
<div id="animated"></div>
You need to play with the animation-iteration-count in order to fix this:
const target = document.getElementById("animated");
target.addEventListener("mouseover", animateForward);
target.addEventListener("mouseout", animateBackward);
function animateForward() {
target.style.animation = '';
setTimeout(function() {
target.style.animation = 'custom 1.6s forwards';
target.style.animationIterationCount = '1';
}, 0)
}
function animateBackward() {
target.style.animation = 'custom 1.6s reverse backwards';
target.style.animationIterationCount = '2';
}
/* Safari 4.0 - 8.0 */
#-webkit-keyframes custom {
25% {
border-radius: 50% 0 0 0;
}
50% {
border-radius: 50% 50% 0 0;
}
75% {
border-radius: 50% 50% 50% 0;
}
100% {
border-radius: 50% 50% 50% 50%;
background-color: violet;
}
}
/* Standard syntax */
#keyframes custom {
25% {
border-radius: 50% 0 0 0;
}
50% {
border-radius: 50% 50% 0 0;
}
75% {
border-radius: 50% 50% 50% 0;
}
100% {
border-radius: 50% 50% 50% 50%;
background-color: violet;
-moz-transform: rotate(180deg);
-webkit-transform: rotate(180deg);
-o-transform: rotate(180deg);
-ms-transform: rotate(180deg);
transform: rotate(180deg);
}
}
div {
border: 1px black solid;
width: 100px;
height: 100px;
margin: 50px;
}
<div id="animated"></div>

How can I put a transition when my overlay is opened?

I'm working on, when I click on an element, an overlay is opened with a content inside, but I'd like to add a transition effect to it.
Here's my JavaScript code:
(function($)
{
$('.overlay-trigger').click(function(e)
{
e.preventDefault();
$('#expose-mask').css({'display': 'inherit'}).fadeIn(function()
{
$('.overlay-box').css({'display': 'inherit'});
});
});
$('#expose-mask, .overlay-box').css({'display': 'none'});
$('.overlay-box-closer, #expose-mask').click(function()
{
$('.overlay-box, #expose-mask').css({'display': 'none'});
$('#expose-mask');
});
})(jQuery);
.overlay-trigger class means the activator of the overlay when I click on an element, #expose-mask means the background when the overlay is opened and the .overlay-box class means the content inside the #expose-mask id when it is open.
I'd like something like this, on this site: http://tympanus.net/Development/ModalWindowEffects/
I'd like to have the "Slide in (bottom)" effect.
I don't use the same code as on this site, so I don't know how. Here's my HTML code:
<a id="help" class="overlay-trigger" href="help.php">Help</a>
<div class="overlay-box">
<div class="overlay-box-container">
<span class="overlay-box-closer" title="Close the overlay"></span>
<h1 class="big-title">Help</h1>
<p>Your privacy is important to us. To better protect your privacy we provide this notice explaining our online information practices and the choices you can make about the way your information is collected and used. To make this notice easy to find, we make it available in our footer and at every point where personally identifiable information may be requested.Log files are maintained and analysed of all requests for files on this website's web servers. Log files do not capture personal information but do capture the user's IP address, which is automatically recognised by our web servers.</p>
</div>
my CSS code:
.overlay-box
{
background-color: #FFFFFF;
position: fixed;
top: 35%;
right: 0;
left: 0;
z-index: 4;
width: 70%;
margin: 0 auto;
box-shadow: rgba(0, 0, 0, 0.3) 0px 1px 7px;
-webkit-box-shadow: rgba(0, 0, 0, 0.3) 0px 1px 7px;
-moz-box-shadow: rgba(0, 0, 0, 0.3) 0px 1px 7px;
}
.overlay-box-container
{
margin: 20px;
}
.overlay-box-closer:before
{
content: "\f00d";
position: absolute;
top: -21px;
right: -15px;
cursor: pointer;
font-size: 40px;
}
#expose-mask
{
background-color: rgba(0, 0, 0, 0.6);
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 3;
}
Live preview here: http://nextgenfocus.com/test1/ Click the "Help" text in the footer to open the overlay.
Thanks.
Try this :
(function($)
{
$('.overlay-trigger').click(function(e)
{
e.preventDefault();
$('#expose-mask').show();
$('.overlay-box').slideDown("slow");
});
$('#expose-mask, .overlay-box').hide();
$('.overlay-box-closer, #expose-mask').click(function()
{
$('.overlay-box, #expose-mask').hide();
});
})(jQuery);
css transition::
transition: all 1s ease-in-out;
-moz-transition: all 1s ease-in-out;
-webkit-transition: all 1s ease-in-out;
-o-transition: all 1s ease-in-out;
-ms-transition:all 1s ease-in-out;
css animation:
#-webkit-keyframes fadeInUp {
0% {
opacity: 0;
-webkit-transform: translateY(20px);
transform: translateY(20px);
}
100% {
opacity: 1;
-webkit-transform: translateY(0);
transform: translateY(0);
}
}
#keyframes fadeInUp {
0% {
opacity: 0;
-webkit-transform: translateY(20px);
-ms-transform: translateY(20px);
transform: translateY(20px);
}
100% {
opacity: 1;
-webkit-transform: translateY(0);
-ms-transform: translateY(0);
transform: translateY(0);
}
}
.fadeInUp {
-webkit-animation-name: fadeInUp;
animation-name: fadeInUp;
}
add to your .overlay-box:
-webkit-animation-name: fadeInUp;
-webkit-animation-fill-mode: flash;
-webkit-animation-duration: 1s;
-webkit-animation-iteration-count: 1;
-webkit-animation-timing-function: linear;
-moz-animation-name: fadeInUp;
-moz-animation-fill-mode: both;
-moz-animation-duration: 1s;
-moz-animation-iteration-count: 1;
-moz-animation-timing-function: linear;
animation-name: fadeInUp;
animation-fill-mode: both;
animation-duration: 1s;
animation-iteration-count: 1;
animation-timing-function: linear;

Categories

Resources