Preserve CSS transform property after removing the class - html

Is it possible to preserve a CSS property (including variables) after removing a class that modifies them ?
The problem is in the code below.
I want to preserve the transform property of the cyan box after removing the class animation-slide-to-left that slides the box to the left.
That is because when removing that animation-slide-to-left and adding the class animation-slide-to-right class, I want that new animation to start from where the old one ended.
But the problem is when removing animation-slide-to-left the transform property resets and becomes equal to Its old value before adding that class.
Note: I don't want to hard code the transform property at 0%, because there are a lot of animations, and I am searching for a way that automatically solve the problem without JAVASCRIPT.
(expand the snippet result to full page too see the example)
const box = document.querySelector (".animated");
function leftclicked (){
box.classList.remove ("animation-slide-to-right");
box.classList.add ("animation-slide-to-left");
}
function rightclicked (){
box.classList.remove ("animation-slide-to-left");
box.classList.add ("animation-slide-to-right");
}
.center {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.animated {
animation-duration: 0.5s;
animation-delay: 0s;
animation-iteration-count: 1;
animation-direction: normal;
animation-timing-function: ease-out;
animation-fill-mode: forwards;
}
.animation-slide-to-left {
animation-name: slide-to-left;
}
.animation-slide-to-right {
animation-name: slide-to-right;
}
#keyframes slide-to-left {
100%{
transform: translate(-150%, -50%);
}
}
#keyframes slide-to-right {
100%{
transform: translate(50%, -50%);
}
}
<div class="center" style="width:300px; height: 300px; background-color: red;">
<div class="center animated" style="width: 50%; height: 50%; background-color:cyan;">
</div>
<button style="float:left;" onclick="leftclicked()">LEFT</button>
<button style="float:right;" onclick="rightclicked()">RIGHT</button>
</div>

You can set the starting position for each time you click on the buttons LEFT and RIGHT in the CSS keyframes, so that when the button is on the left side and click on the RIGHT button, it first sets the position to the left side (0%) before it animates to the right (100%) and vice-versa. This way it doesn't reset back to the center when clicking the buttons.
const box = document.querySelector (".animated");
function leftclicked (){
box.style.transform = 'translate(50%, -50%)';
box.classList.remove ("animation-slide-to-right");
box.classList.add ("animation-slide-to-left");
}
function rightclicked (){
box.style.transform = 'translate(-150%, -50%)';
box.classList.remove ("animation-slide-to-left");
box.classList.add ("animation-slide-to-right");
}
.center {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.animated {
animation-duration: 0.5s;
animation-delay: 0s;
animation-iteration-count: 1;
animation-direction: normal;
animation-timing-function: ease-out;
animation-fill-mode: forwards;
}
.animation-slide-to-left {
animation-name: slide-to-left;
}
.animation-slide-to-right {
animation-name: slide-to-right;
}
#keyframes slide-to-left {
100%{
transform: translate(-150%, -50%);
}
}
#keyframes slide-to-right {
100%{
transform: translate(50%, -50%);
}
}
<div class="center" style="width:300px; height: 300px; background-color: red;">
<div class="center animated" style="width: 50%; height: 50%; background-color:cyan;">
</div>
<button style="float:left;" onclick="leftclicked()">LEFT</button>
<button style="float:right;" onclick="rightclicked()">RIGHT</button>
</div>

Well It turns out that it is unnecessary to create such behaviour without JavaScript.
Because if a CSS class is being replaced at runtime, we are already using JavaScript.
So the solution is to set the transform property in JavaScript after removing the old class and before adding the new class

Related

Smooth parent transition on child "translateX"

On GIF below, there's parent div, which contain "left-menu" and "app-content". Left menu is animating, using keyframes and translate property
#LeftMenuContainer {
&.menu_hidden {
animation: slide-out 0.6s forwards;
}
&.menu_shown {
animation: slide-in 0.6s forwards;
}
}
#keyframes slide-in {
0% { transform: translateX(-100%); width: 0; }
100% { transform: translateX(0); width: auto; }
}
#keyframes slide-out {
0% { transform: translateX(0); width: auto; }
100% { transform: translateX(-100%); width: 0; }
}
DOM tree looks like this
<div id="contentContainer" class="flex">
<app-left-menu></app-left-menu>
<div>Be smooth!</div>
</div>
Is there any way, to "smooth" transition parent width, when "left-menu" is hiding? I try to add styles for parent, with
transition-property: width;
transition-duration: 0.6s;
but it doesn't work.
https://im2.ezgif.com/tmp/ezgif-2-a1f74da85c.gif
Assume It only flickers when comes to animate, so you can try to use will-change, css-will-change-property
The will-change CSS property hints to browsers how an element is expected to change. Browsers may set up optimizations before an element is actually changed
If you are using framework like React can try to use framer-motion

Css transition not transitioning back to original state when using animation with it

I am trying to make a loading animation. I am using css transition to transition into the loading by scaling and then using animation to scale out the x axis. But when I try to transition back to the original state it doesn't use the transition anymore it just snaps back. I could use animation for the whole thing but I want to account for the page continuing to load so I don't want to have to write extra javascript logic to handle it. It would be nice if It would just transition on its own.
When you click the following snippet the first time it works fine. But when you click it again it just snaps back to its original state and doesn't use the transition. If you use a different property like opacity in the animation part then it works fine so I'm assuming there is something with the browser not recognizing the current scaled value. Is this a bug or am I doing something wrong?
document.querySelector('.wrapper').addEventListener('click', () => {
document.querySelector('.wrapper').classList.toggle('loading')
})
.wrapper{
position:fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.wrapper > div{
color: white;
display:flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
background: blue;
transition: transform 500ms ease-out;
transform: scale(1);
}
.wrapper.loading > div{
transform: scale(0.2, 0.002);
animation: loading 1000ms ease-out infinite;
animation-delay: 500ms;
}
#keyframes loading {
0%{
transform: scale(0.2, 0.002)
}
50%{
transform: scale(0.5, 0.002)
}
100%{
transform: scale(0.2, 0.002)
}
}
<div class="wrapper">
<div>click me</div>
</div>
TL;DR
I believe that this happens because CSS transition eventually gives a class two states and transitions between them, when you remove your class you don't change its state, you remove it. my solution would be to add another class to set it back.
CSS transitions work by defining two states for the object using CSS. In your case, you define how the object looks when it has the class "loading" and you define how it looks when it doesn't have the class "saved" (it's normal look). When you remove the class "loading", it will transition to the other state according to the transition settings in place for the object without the "loading" class.
If the CSS transition settings apply to the object (without the "loading" class), then they will apply to both transitions.
your transition CSS settings only apply to .saved and thus when you remove it, there are no controls to specify a CSS setting. You may want to add another class ".fade" that you leave on the object all the time and you can specify your CSS transition settings on that class so they are always in effect.
I don't know a pure css fix for this.
But you can add a different class with a animation that restores to what it was before with JS
const wrapper = document.querySelector(".wrapper");
wrapper.onclick = () => {
if ([...wrapper.classList].includes("loading")) {
wrapper.classList.add("restore");
} else {
wrapper.classList.remove("restore");
}
wrapper.classList.toggle("loading");
};
.wrapper {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.wrapper>div {
color: white;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
background: blue;
animation: none;
transform: scale(1);
transition: transform 500ms ease-out;
}
.wrapper.restore>div {
animation: restore 500ms ease-out;
}
.wrapper.loading>div {
transform: scale(0.2, 0.002);
animation: loading 1000ms 500ms ease-out infinite;
}
#keyframes restore {
0% {
transform: scale(0.2, 0.002);
}
100% {
transform: scale(1);
}
}
#keyframes loading {
0% {
transform: scale(0.2, 0.002)
}
50% {
transform: scale(0.5, 0.002)
}
100% {
transform: scale(0.2, 0.002)
}
}
<div class="wrapper">
<div>click me</div>
</div>
You can use animation iteration count property:
div {
animation-iteration-count: 2;
}
or use fill mode to freeze the animation at the end:
-webkit-animation-fill-mode: forwards;

How to overlay div above html form?

I want to display an animated loading icon when the user have submitted a search.
SEQUENCE OF EVENTS I'M LOOKING FOR:
USER SUBMITTED A SEARCH
MAKE LOADING ICON VISIBLE
MAKE LOADING ICON INVISIBLE ONCE THE SEARCH IS COMPLETED
The issue I'm facing is mostly css.
Firstly, the loading icon seems to be behind the form element.
Secondly, I cannot increase the size of the div (searchEngineForm) to have the same size as the form.
Lastly, I cannot set div (searchEngineForm) width to 100%. It goes outside of the form.
Here is my code:
HTML:
<form action="setJobFields" method="POST">
<div id="searchEngineForm" style="display: none;">
<div class="loader">
</div>
</div>
...
</form>
CSS:
#searchEngineForm{
position: absolute;
/* width: 100%; */
}
.loader {
border: 16px solid #f3f3f3;
border-radius: 50%;
border-top: 16px solid #3498db;
width: 120px;
height: 120px;
-webkit-animation: spin 2s linear infinite; /* Safari */
animation: spin 2s linear infinite;
}
/* Safari */
#-webkit-keyframes spin {
0% { -webkit-transform: rotate(0deg); }
100% { -webkit-transform: rotate(360deg); }
}
#keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
To set an html element above others, you could set its Z-index to a higher number than what’s around it.
For example, say you’re wanting to display your loading symbol in front of the rest of the page. You could contain the whole page in a single div, for the sake argument we give it a class of “page”, we can set it as:
.page{ z-index: -1 ;}
And the loader as
.loader{ z-index: 1; }
Then, to position it where you want, you can set the position to absolute and move it around with the top and left properties, such as
.loader{ z-index:1; position: absolute; top: 50%; left: 50%; }

How to prevent logo from causing navigation bar from jittering while it's loading

I've been trying to find a solution for a while now but none seem to work.
The issue I am having happens when navigating to any and all the pages on the site- it's very annoying.
While I would expect that site images take time to load, this loading affects my navigation bar and the loading of my site's logo. For the time that it takes each page to load, my site's logo is completely absent- this causes my navigation bar to be shifted all the way up until the logo appears. This usually takes about a split second but it's also completely dependent on the user's internet connection).
How do I prevent this from happening? This causes my entire site to "bounce" when navigating, with all the content being shifted up for a brief moment while the logo is absent.
Give your image tag an absolute height attribute. This will make the browser keep the img tag the height it should be and allow the elements to load in the proper place.
You can also try tweaking a loader to have the page load only when all of the elements in the page have loaded. Something as simple as this:
<!DOCTYPE html>
<html>
<head>
<style>
/* Center the loader */
#loader {
position: absolute;
left: 50%;
top: 50%;
z-index: 1;
width: 150px;
height: 150px;
margin: -75px 0 0 -75px;
border: 16px solid #f3f3f3;
border-radius: 50%;
border-top: 16px solid #3498db;
width: 120px;
height: 120px;
-webkit-animation: spin 2s linear infinite;
animation: spin 2s linear infinite;
}
#-webkit-keyframes spin {
0% { -webkit-transform: rotate(0deg); }
100% { -webkit-transform: rotate(360deg); }
}
#keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* Add animation to "page content" */
.animate-bottom {
position: relative;
-webkit-animation-name: animatebottom;
-webkit-animation-duration: 1s;
animation-name: animatebottom;
animation-duration: 1s
}
#-webkit-keyframes animatebottom {
from { bottom:-100px; opacity:0 }
to { bottom:0px; opacity:1 }
}
#keyframes animatebottom {
from{ bottom:-100px; opacity:0 }
to{ bottom:0; opacity:1 }
}
#myDiv {
display: none;
text-align: center;
}
</style>
</head>
<body onload="myFunction()" style="margin:0;">
<div id="loader"></div>
<div style="display:none;" id="myDiv" class="animate-bottom">
<h2>Tada!</h2>
<p>Some text in my newly loaded page..</p>
</div>
<script>
var myVar;
function myFunction() {
myVar = setTimeout(showPage, 3000);
}
function showPage() {
document.getElementById("loader").style.display = "none";
document.getElementById("myDiv").style.display = "block";
}
</script>
</body>
</html>
With some modification, can help the UI experience!
Source: W3 Schools
Hope it helps!

css animation-name property does not allow transform property

I have the library animate.css loaded on my website and I animate an arrow moving onto the page using "fadeInLeftBig"
My html:
<div class="swipe-button b-left animated fadeInLeftBig"></div>
My css:
.swipe-button.b-left {
left: 10px;
background-image: url(/images/left-arrow.png);
}
.swipe-button:hover {
transform: rotate(90deg) !important;
}
Animate.css
.fadeInLeftBig {
animation-name: fadeInLeftBig;
}
.animated {
animation-duration: 1s;
animation-fill-mode: both;
}
#keyframes fadeInLeftBig {
0% {
opacity: 0;
transform: translateX(-2000px);
}
100% {
opacity: 1;
transform: translateX(0);
}
}
The transform: rotate(90deg) does not work on hover as long as animation-name: fadeInLeftBig is set on the element. But works if you unceck or comment it out.
I can see now there are two transform properties on the element, but Why does setting the animation-name property override the transform property with an !important flag from taking effect?
As vals stated earlier..an animation overrides the static properties. For what you're trying to achieve, you're best bet is to wrap your swipe-button class with a new fadeInLeftBig div:
<div class="fadeInLeftBig animated">
<div class="swipe-button b-left animated"></div>
</div>
Then use keyframe animations on both divs. This separates your animations so that your "fade in" doesn't start over once you unhover your swipe-button. Here's a working fiddle. https://jsfiddle.net/kj4v36ye/2/ Let me know if you're trying to achieve something else and I can easily modify it.
First at you code your don't close '}' in .fadeInLeftBig.
look at this fiddle:
<div class="swipe-button b-left animated fadeInLeftBig"></div>
and css
.swipe-button.b-left {
left: 10px;
background-color: blue;
width: 50px;
height: 50px;
}
.swipe-button:hover {
transform: rotate(45deg);
}
.fadeInLeftBig {
animation-name: fadeInLeftBig;
}
.animated {
animation-duration: 1s;
animation-fill-mode: both;
}
https://jsfiddle.net/kj4v36ye/