I am working on a little menu animation, nothing groundbreaking but just as an experiment. This is what I currently have:
HTML
<div class="menu">
<div class="bar"></div>
<div class="bar"></div>
<div class="bar"></div>
<div class="bar"></div>
</div>
SCSS
div.menu {
width: 24px;
height: 24px;
position: relative;
margin: 48px;
cursor: pointer;
div.bar {
display: block;
width: 100%;
height: 2px;
background-color: #444;
position: absolute;
transition: all 0.25s ease-in-out;
&:nth-child(1) {
}
&:nth-child(2) {
top: 11px;
}
&:nth-child(3) {
top: 11px;
}
&:nth-child(4) {
bottom: 0;
}
}
&.active {
div.bar {
&:nth-child(1) {
width: 0;
}
&:nth-child(2) {
transform: rotate(-45deg);
}
&:nth-child(3) {
transform: rotate(45deg);
}
&:nth-child(4) {
width: 0;
}
}
}
}
JAVASCRIPT
var menu = document.querySelector('.menu');
menu.addEventListener('click', function(){
menu.classList.toggle('active');
});
And this is a pen of it in action:
https://codepen.io/mikehdesign/pen/eWJKKN
Currently, when the menu is active the top and bottom div.bar reduce their width to 0 to the left. I would like to adjust this so they reduce their width to the center. I have tried messing with margins for them but had no luck, if anyone could shed some light or suggest a different approach if needed that would be great.
Mike
You can use transform-origin to do this:
with respect to your dimensions it would be 12px up and down(+ and -) as in the code below:
&.active {
div.bar {
&:nth-child(1) {
transform-origin:12px 12px;
transform: scale(0);
}
&:nth-child(2) {
transform: rotate(-45deg);
}
&:nth-child(3) {
transform: rotate(45deg);
}
&:nth-child(4) {
transform-origin:12px -12px;
transform: scale(0);
}
}
}
Check this out: JsFiddle Link
I would use the pseudo elements to split up the transform animation.
Demo (no script version)
HTML (note only three bars)
<input type="checkbox" id="toggle-menu" hidden>
<label for="toggle-menu" class="menu">
<div class="bar"></div>
<div class="bar"></div>
<div class="bar"></div>
</label>
SCSS
// two step transition
// as translate and rotation can't (yet) be animated individually
// we use the pseudo elements to split up the animation
//
// - bar elements will handle the vertical transform
// - :after elements will handle rotation/scale
// variables to control transition time and delay
$transition-time: 300ms;
$transition-delay: 300ms;
.menu {
width: 24px;
height: 24px;
position: relative;
cursor: pointer;
display: block;
}
.bar {
height: 2px;
position: absolute;
top: 50%;
width:100%;
// Entering `hamburger` state
// 1) add a delay on bars to wait for the :after elements to rotate/scale back
// 2) the :after elements have no delay
transition: $transition-time $transition-delay; // 1
&:after {
content:'';
display:table;
background: black;
position: inherit; width: inherit; height:inherit;
transition: $transition-time; // 2
}
// transform the bars into hamburger
&:nth-child(1){ transform: translateY(-8px); }
&:nth-child(3){ transform: translateY(8px);}
}
// when toggle-menu checkbox is checked transform to `X`
[id="toggle-menu"]:checked ~ .menu {
.bar {
// Entering `X` state
// 1) to animate bars to the center we simply remove the transform
// 2) as we are now animating backwards we switch the transition
// on the bars and their :after elements
transform: none; // 1
transition: $transition-time; // 2
&:after { transition: $transition-time $transition-delay } // 2
// rotate the top and bottom :after elements
&:nth-child(1):after{ transform: rotate(-45deg); }
&:nth-child(3):after{ transform: rotate(45deg);}
// hide the middle :after by scaling to zero
// (when all bars are at the center)
&:nth-child(2):after{ transform: scale(0); }
}
}
Related
I am working on slide animation for text in container infinitely.
However, I found it not working smoothly. When the text reaches the end, it seems like restarting instead of looping the text.
What I want is the text keep sliding to the right continuously.
How to solve it?
App.js
import "./styles.css";
export default function App() {
return (
<div className="App">
<div class="slide-right">
<h2>
<span>A</span>
<span>A</span>
<span>A</span>
<span>A</span>
<span>A</span>
<span>A</span>
</h2>
</div>
</div>
);
}
Styles.css
.App {
font-family: sans-serif;
text-align: center;
}
.slide-right {
border: 1px solid grey;
width: 150px;
overflow: hidden;
}
.slide-right h2 {
animation: infinite slide-right 2s linear;
transform: translateX(-100%);
text-align: right;
}
.slide-right h2 span {
margin-right: 10px;
}
#keyframes slide-right {
to {
transform: translateX(0);
}
}
Codesandbox:
https://codesandbox.io/s/hidden-bird-qy3jy?file=/src/styles.css
Change your "slide-right" keyframes from transform: translateX(0); to transform: translateX(100%);
To get a continuous flow this snippet has two copies of the spans.
Initially the h2 is translated -50% so only the beginning of the second half is showing.
The animation moves it to the right so it ends up with the beginning of the first half showing.
Then on the repeat the beginning of the second half takes the place of the beginning of the first half, so it all looks continuous.
.App {
font-family: sans-serif;
text-align: center;
}
.slide-right {
border: 1px solid grey;
width: 150px;
overflow: hidden;
}
.slide-right h2 {
animation: infinite slide-right 2s linear;
transform: translateX(-50%);
display: inline-block;
}
.slide-right h2 span {
margin-right: 10px;
}
#keyframes slide-right {
to {
transform: translateX(0);
}
}
<div class="App"><div class="slide-right"><h2><span>A</span><span>A</span><span>A</span><span>A</span><span>A</span><span>A</span><span>A</span><span>A</span><span>A</span><span>A</span><span>A</span><span>A</span></h2></div></div>
Note: to avoid problems with wrapping of text the spans are laid out continuously (as in the codesandbox in the question).
#keyframes slide-right {
to {
transform: translateX(100%);
}
}
Use this instead of translateX(0)
Double the number of A to make the effect continuous.
Take a look at this simple JsFiddle I created.
What it does is simply inserts a new li element with a slide effect from left, when the ul display is on flex and inline-block.
Something similar to that is Stackoverflow chat avatars when someone joins.
#-webkit-keyframes enter {
0% { /*opacity:0;*/ -webkit-transform: translateX(-100%); }
100% { /*opacity:1;*/ -webkit-transform: translateX(0px); }
}
#-webkit-keyframes moves {
0% { /*opacity:0;*/ -webkit-transform: translateX(-50px); }
100% { /*opacity:1;*/ -webkit-transform: translateX(0px); }
}
in my enter animation, I start with translate -100% because I want my item come from left distanced by his size.
and in the moves animation, I want to move the whole ul to the right, by the size of the entering element.
Now I set it hard-coded, to 50px because my elements are set to 50px:
li {
width: 50px;
height: 50px;
display: inline-block;
background-color: red;
}
How can I make it calculate the width OR height automatically, on how much to translate the ul?
Example: Calculate these 50px automatically
You can do this by animating only the added element by using negative margin:
setTimeout(() => {
var item = $("<li></li>").addClass("enter");
$("ul").prepend(item).addClass('move');
}, 2000);
ul {
display: flex;
transition-timing-function: ease-out;
transition: all .2s;
}
li {
--h:50px;
width: 50px;
height: var(--h);
display: inline-block;
background-color: red;
}
.enter {
animation: enter 1s;
}
#keyframes enter {
0% {
transform: translateX(-100%);
margin-left: calc(var(--h) * -1);
}
100% {
transform: translateX(0px);
margin-left: 0
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li class="my-flex-thing">a</li>
<li class="my-flex-thing">b</li>
</ul>
I am trying to make a div where it is assigned by an id.
My goal is to make that div have two sides: the front and the back (assigned by class)
* the front is shown as default, while the back will only be shown upon user click
My question is, when trying to make a lot of this div, how can I achieve this kind of scenario, where it will only show/hide those classes only in that particular id?
<div id="user01" onclick="userId(this)">
<div class="front">
<img src="../media/media/alumni/sdc/01.jpg"> User name on front
</div>
<div class="back">
other info on back
</div>
</div>
**user01 is assigned per each user (ex.: the first user is 01, while the second is 02, and so on...)
thanks :)
Use a css class on top of your id:
<div id="user01" class="user">
Then, in your Javascript, use document.querySelectorAll along with that class:
let users = document.querySelectorAll('.user')
Now you can iterate over this DOM node collection that is returned by document.querySelectorAll and add an event listener to each one:
Array.prototype.forEach.call(users, function(user) {
user.addEventListener('click', function(event) {
/* your code to be executed on click
each user is available here as 'this' */
// example:
this.classList.toggle('active')
})
})
As mplungjan pointed out, Edge and IE don't support forEach on Node collections, so Array.prototype.forEach.call(users, function(user) ... comes in as a replacement for users.forEach(function(user) { ....
As a last step, add the CSS that flips your stuff:
.user .front {
transition: opacity .3s ease;
opacity: 1;
}
.user.active .front {
opacity: 0;
}
.user .back {
transition: opacity .3s ease;
opacity: 0;
}
.user.active .back {
opacity: 1;
}
let users = document.querySelectorAll('.user')
Array.prototype.forEach.call(users, function(user) {
user.addEventListener('click', function(event) {
/* your code to be executed on click
each user is available here as 'this' */
// example:
this.classList.toggle('active')
})
})
.user {
display: inline-block;
height: 200px;
width: 200px;
position: relative; /* this allows absolute position of children */
}
.user:hover {
cursor: pointer;
}
.user .front,
.user .back {
transition: opacity .3s ease;
color: white;
position: absolute; /* position both "sides" on top of each other */
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.user .front img,
.user .back img {
position: absolute;
z-index: 0;
}
.user .front span,
.user .back span {
background-color: rgba(0,0,0,.3);
box-shadow: 0 0 10px rgba(0,0,0,.3), 0 0 2px rgba(0,0,0,.3);
border-radius: 10px;
display: block;
left: 50%;
padding: 10px 5px;
position: absolute;
text-align: center;
top: 50%;
transform: translate(-50%, -50%);
z-index: 1;
}
.front {
opacity: 1;
}
.user.active .front {
opacity: 0;
}
.back {
background-color: #333;
opacity: 0;
}
.user.active .back {
opacity: 1;
}
<div class="user" id="user01">
<div class="front">
<img src="https://lorempixel.com/output/business-q-c-200-200-8.jpg"><span>User 1 name on front</span>
</div>
<div class="back">
other info on back for User 1
</div>
</div>
<div class="user" id="user02">
<div class="front">
<img src="https://lorempixel.com/output/business-q-c-200-200-2.jpg"><span>User 2 name on front</span>
</div>
<div class="back">
other info on back for User 2
</div>
</div>
I want to fade between two differently sized elements within a container overlaying each other. The first element should be faded out, then the container resized and finally the other element faded in.
Here's the related snippet:
var layer1 = document.getElementById("layer1");
var layer2 = document.getElementById("layer2");
function switchLayers() {
layer1.addEventListener("transitionend", function() {
layer2.classList.add("fadein");
});
layer1.classList.add("fadeout");
}
#container {
position: relative;
background-color: yellow;
padding: 10px;
overflow: hidden;
}
.layer {
position: relative;
width: 400px;
}
#layer1 {
height: 100px;
float: left;
background-color: blue;
}
#layer2 {
height: 150px;
background-color: red;
display: none;
opacity: 0;
}
#layer1.fadeout {
opacity: 0;
transition: opacity 1s ease-out;
}
#layer2.fadein {
display: block;
opacity: 1;
transition: opacity 1s ease-out;
}
<button onclick="switchLayers()">Switch layers</button>
<div id="container">
<div id="layer1" class="layer"></div>
<div id="layer2" class="layer"></div>
</div>
When the second layer's display property is set to block it works as expected, i.e. the opacity is changed from 0 to 1 within a second. Though if it's set to none, the transition suddenly is discrete.
I've tried to set all within the transition value to transition all properties and also tried to include the display property in the transition like this:
transition: display 0s, opacity 1s ease-out;
Though without success. Note that because the container should resize to the size of the currently displayed layer, the visibility property can't be used (as it hides the element but still lets it occupy the space).
How to made this work?
Try using the visibility property instead of display.
For more information regarding the state changes in visibility and display, refer article.
For transitioning the parent height, you have to manually change the height property of the #container. Using display: block & display: none will never transition the parent.
Refer code:
var layer1 = document.getElementById("layer1");
var layer2 = document.getElementById("layer2");
function switchLayers() {
layer1.addEventListener("transitionend", function() {
layer2.classList.add("fadein");
document.getElementById("container").style.height = "170px";
});
layer1.classList.add("fadeout");
}
#container {
position: relative;
background-color: yellow;
padding: 10px;
height: 100px;
overflow: hidden;
transition: all 0.5s;
}
.layer {
position: relative;
width: 400px;
}
#layer1 {
height: 100px;
float: left;
background-color: blue;
}
#layer2 {
height: 150px;
background-color: red;
visibility: none;
opacity: 0;
}
#layer1.fadeout {
visibility: none;
opacity: 0;
transition: all 1s ease-out;
}
#layer2.fadein {
visibility: visible;
opacity: 1;
transition: all 1s ease-out;
}
<button onclick="switchLayers()">Switch layers</button>
<div id="container">
<div id="layer1" class="layer"></div>
<div id="layer2" class="layer"></div>
</div>
There is no straightforward way. Transitions do not work on display, nor do they work on auto height. So, visibility is a good bet.
Note that because the container should resize to the size of the
currently displayed layer, the visibility property can't be used (as
it hides the element but still lets it occupy the space).
Then, you will need to hack it out. You can make use of min-height. Give a faux min-height to your container, and then apply the height of your layer2 to it once the transition ends. Also, because display on layer2 will block the transition, you need to separate out the classes for display and opacity and space out their application using a zero timeout in between.
Here is a crude idea:
var layer1 = document.getElementById("layer1"),
layer2 = document.getElementById("layer2"),
container = document.getElementById("container"),
h = window.getComputedStyle(layer2).getPropertyValue("height");
container.addEventListener("transitionend", function(e) {
if (e.target.id === 'layer1') {
// apply layer2 height to container min-height
container.style.minHeight = h;
}
if (e.target.id === 'container') {
// First show the layer2
layer2.classList.add("show");
// Then a dummy pause to fadein
setTimeout(function(){
layer2.classList.add("fadein");
}, 0);
}
}, false);
function switchLayers() {
layer1.classList.add("fadeout");
}
#container {
position: relative;
background-color: yellow;
padding: 10px; overflow: hidden;
min-height: 1px; /* faux min-height */
transition: min-height 1s linear;
}
.layer { position: relative; width: 400px; }
#layer1 {
height: 100px; float: left;
background-color: blue;
transition: all 1s linear;
}
#layer2 {
height: 150px; background-color: red;
display: none; opacity: 0;
transition: opacity 1s linear;
}
#layer1.fadeout { opacity: 0; }
#layer2.show { display: block; } /* Separate out display */
#layer2.fadein { opacity: 1; } /* Separate out opacity */
<button onclick="switchLayers()">Switch layers</button>
<div id="container">
<div id="layer1" class="layer"></div>
<div id="layer2" class="layer"></div>
</div>
I added this pure css cookie bar to my website and all works fine, the only problem is that when you enter in the site, you can see FIRST the cookie bar, AND the cookie bar go up and go down at the end.
How can see my cookie bar only go down when i enter in my site, i thought to change de thenimation delay, add set time out .... but nothing change !!
here is the original codepen and you can see what i want to change in it
www.codepen.io/natewiley/pen/uGtcD
HERE IS MY CODE
<input class="checkbox-cb" id="checkbox-cb" type="checkbox" />
<div class="cookie-bar">
<div class="message">
This website uses cookies to give you an incredible experience. By using
this website you agree to the
<div class="buttoncookies-container">
<a style="letter-spacing: 1px;" class="buttoncookies" id="modalcookieslinken" onclick="toggleOverlay()">terms</a>
</div>
</div>
<div class="mobile">
This website uses cookies,
<div class="buttoncookies-container">
<a style="letter-spacing: 1px;" class="buttoncookies" id="modalcookiesshortlink" onclick="toggleOverlay()">
learn more
</a>
</div>
</div>
<label for="checkbox-cb" class="close-cb">X</label>
</div>
</div>
HERE IS MY CSS
.cookie-bar { z-index:9996; position: fixed; width: 100%; top: 0; right: 0; left: 0; height: auto; padding: 20px; line-height:20px; text-align: center; background: #d2c6af; transition: .8s; animation: slideIn .8s; animation-delay: .8s; display: inline-block; }
.mobile { display: none; }
#keyframes slideIn { 0% { transform: translateY(-1000px); } 100% { transform: translateY(0); } }
.close-cb { border: none; background: none; position: absolute; display: inline-block; right: 20px; top: 10px; cursor: pointer; }
.close-cb:hover { color:#fff;; }
.checkbox-cb { display: none;}
#checkbox-cb:checked + .cookie-bar { transform: translateY(-1000px); }
Removing the line in css
animation-delay: .8s;
will give you the result
Make the animation last longer.
animation: slideIn 4s;
Plus add some trick to animation flow:
0% {
transform: translateY(-50px);
}
50% {
transform: translateY(-50px);
}
100% {
transform: translateY(0);
}