How to animate a pop up on show and close using css? - javascript

I have a layout as follows,
container - to hold all the cards.
card - div for containing information.
In the above Image, the left one shows the initial rendering of the screen,
When user clicks on any inner card, the corresponding card should popout/zoomout,
and when user click backs on pop up card, it should disappear and the first sceen should display.
The popup animation should be like that, it should start from the position of the card, we have clicked.
The popup close animation after second click(When popup is open), the animation should look like that, the popup should get minimized to the card clicked in the first step.
I have tried following code, but it is really animating..
let isOpen = false;
$(".child").on("click", function() {
if (!isOpen) {
$(".child").removeClass("active");
$(this).addClass("active");
isOpen = true;
} else {
$(this).removeClass("active");
isOpen = false;
}
})
* {
box-sizing: border-box;
}
.parent {
margin: 40px auto;
width: 400px;
height: 600px;
border: 1px solid #3b3b3b;
border-radius: 20px;
padding: 20px 40px;
position: relative;
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20px;
}
.child {
display: flex;
justify-content: center;
align-items: center;
border: 1px solid #000;
border-radius: 40px;
cursor: pointer;
transition: all 0.5s ease-in;
}
.child.active {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
z-index: 10;
border: 1px solid red;
background: #000;
border-radius: 20px;
color: #fff;
}
#keyframes zoomIn {
0% {
transform: scale(1.1);
}
50% {
transform: scale(1.2);
}
100% {}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="parent">
<div class="child">1</div>
<div class="child">2</div>
<div class="child">3</div>
<div class="child">4</div>
</div>
Please help me to simulate the same.

Your animation is pretty much complete. The problem as I see it is that when the .active class is added to a child, the other children fill up the void in the grid. This makes the active child not enlarge from its original position.
I made my own solution using CSS but without animations - and vanilla JavaScript. In my code (just as in yours) the child gets lose from the grid, gets an absolute position and then fills up the entire parent container with width: 100%; and height: 100%; I also added CSS specifications to the other children to stay put when this is happening (see below).
It's a rather snappy effect because transition is not applied to width and height unless the child is absolute positioned before the active class is added. To achieve a more "zoomy" effect is a bit more tricky:
You can observe for DOM attribute (class) mutations with JavaScript (in other words, add a class with absolute positioning, and when that operation is completed, add another class with width: 100%; and height: 100%;).
Or you could use position: absolute on all the child elements from the start, but then you also need to specify width, height, top, left etc.
Some other solution I'm too tired or not skilled enough to think of.
Current Solution
// Turn all 4 .child selectors into an integer array ranging from 0-3
let cardArray = Array.from(document.querySelectorAll(".child"));
// Loop over each integer [0-3] and give them an index number
// Listen for clicks, and then toggle the "larger" class onto the item with the corresponding index number [0-3]
cardArray.forEach(function(everyItem, index) {
everyItem.addEventListener("click", function() {
cardArray[index].classList.toggle("larger");
});
});
* {
box-sizing: border-box;
}
.parent {
margin: 40px auto;
width: 400px;
height: 600px;
border: 1px solid #3b3b3b;
border-radius: 20px;
padding: 20px 40px;
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-gap: 20px;
transition: all 0.5s;
/* relative position required for enlarged items to stay within parent container */
position: relative;
}
.child {
display: flex;
justify-content: center;
align-items: center;
border: 1px solid #000;
border-radius: 40px;
cursor: pointer;
transition: all 0.2s;
/* z-index not neccessary, just a precaution */
z-index: 1;
}
/* top/bottom/left/right required for the CURRENTLY ACTIVE child to resize from the correct corner.
:nth-child() with grid-area specified required for NOT CURRENTLY active children to stay put in grid. */
.child:nth-child(1) {
grid-area: 1 / 1;
top: 0;
left: 0;
}
.child:nth-child(2) {
grid-area: 1 / 2;
top: 0;
right: 0;
}
.child:nth-child(3) {
grid-area: 2 / 1;
bottom: 0;
left: 0;
}
.child:nth-child(4) {
grid-area: 2 / 2;
bottom: 0;
right: 0;
}
/* .larger class added with the help
of JavaScript on click */
.child.larger {
/* Unhinge from the grid */
grid-area: unset;
/* Position absolute in order to resize it */
position: absolute;
/* Fill the WIDTH of the parent container */
width: 100%;
/* Fill the HEIGHT of the parent container */
height: 100%;
/* z-index not neccessary, just a precaution */
z-index: 2;
background: #000;
opacity: 0.5;
color: #fff;
}
<div class="parent">
<div class="child">1</div>
<div class="child">2</div>
<div class="child">3</div>
<div class="child">4</div>
</div>

You can try achieve this with by css variables calculation, position: absolute and a separate .active class for each element.
let isOpen = false;
$('.child').on('click', function() {
if (!isOpen) {
$('.child').removeClass('active');
$(this).addClass('active');
isOpen = true;
} else {
$(this).removeClass('active');
isOpen = false;
}
});
*,
::after,
::before {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--parent-width: 400px;
--parent-height: 600px;
--gap: 20px;
}
.parent {
margin: 40px auto;
width: var(--parent-width);
height: var(--parent-height);
border: 1px solid #3b3b3b;
border-radius: 20px;
position: relative;
}
.child {
display: flex;
justify-content: center;
align-items: center;
border: 1px solid #000;
border-radius: 40px;
cursor: pointer;
transition: all 0.5s ease-in;
position: absolute;
height: calc((var(--parent-height) / 2) - (var(--gap) * 2));
width: calc((var(--parent-width) / 2) - (var(--gap) * 3));
}
/* Init size */
.child:nth-child(1) {
top: var(--gap); /* padding top 20px */
left: calc(var(--gap) * 2); /* padding left 40px */
}
.child:nth-child(2) {
top: var(--gap);
right: calc(var(--gap) * 2); /* padding right 40px */
}
.child:nth-child(3) {
bottom: var(--gap); /* padding bottom 20px */
left: calc(var(--gap) * 2); /* padding left 40px */
}
.child:nth-child(4) {
bottom: var(--gap);
right: calc(var(--gap) * 2);
}
/* Full size */
.child:nth-child(1).active {
top: 0;
left: 0;
}
.child:nth-child(2).active {
top: 0;
right: 0;
}
.child:nth-child(3).active {
bottom: 0;
left: 0;
}
.child:nth-child(4).active {
bottom: 0;
right: 0;
}
.child.active {
width: 100%;
height: 100%;
z-index: 10;
border: 1px solid red;
background: #000;
border-radius: 20px;
color: #fff;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="parent">
<div class="child">1</div>
<div class="child">2</div>
<div class="child">3</div>
<div class="child">4</div>
</div>

Related

Avoid div overflowing

I have a piece of code that when user hover the slider bar, a box will appear. Everything works as expected but when the user move the mouse to the beginning or to the end of the slider, the box overflow the content.
I'm looking for a way that keep the box inside the blue area
Here is my code =>
var left = document.getElementById('core').getBoundingClientRect().left - document.documentElement.getBoundingClientRect().left;
window.onmousemove = function (e) {
let x = ((e.clientX + window.pageXOffset) - left);
document.getElementById("thumbnail").style.left = (x + "px");
}
body {
overflow: hidden;
}
.container {
width: 100%;
max-width: 800px;
position: relative;
background-color: blue;
height: 200px;
overflow: hidden;
}
.core {
margin: 0;
display: flex;
position: absolute;
bottom: 4px;
width: 100%;
flex-wrap: nowrap;
background-color: red;
height: auto;
}
.range {
width: 90%;
margin: 0 auto;
}
.range:hover + .thumbnail {
display: block;
}
.thumbnail {
display: none;
z-index: 1;
position: absolute;
bottom: 30px;
right: auto;
margin: 0;
width: 12em;
height: 7em;
background: rgb(200, 200, 200);
pointer-events: none;
padding: 2px 2px;
transform: translateX(-50%);
left: 50%;
}
.thumbnail::before {
content: "";
position: absolute;
top: 100%;
left: 50%;
margin-left: -8px;
border: 8px solid transparent;
border-top: 8px solid rgb(200, 200, 200);
transform: translateY(-19%);
}
<div class="container">
<div id="core" class="core">
<input id="progress" class="range" type='range' min="0" max="100" step="0.01" value="0">
<div id="thumbnail" class="thumbnail"></div>
</div>
</div>
[ If you want jsfiddle => https://jsfiddle.net/ram9wc65/ ]
Here is a image that show +- the expected output =>
image (I cannot embed images yet)
How can I fix this? How can I keep the box inside of blue area? I spent many hours working on it but no success.
Thank you.
In your onMouseMove function, you need to get the width of the thumbnail and divide that by 2, to get the minimum left position and calculate the maximum right position (width - minimum).
Then make sure you set the left property of the thumbnail no smaller than the minimum and no larger than the calculated maximum.

How would I go about creating editable "blocks" on an html website that show up when a plus button gets pressed?

I'm extremely new to html/css/javascript and I don't even know if I am using the right tools to make this.
I am trying to create a website with a plus icon that when pressed creates a panel and "sprite" on the page. The panel has controls that change the various properties of the sprite, and can get deleted which also removes the sprite. Multiple panels can be created by pressing the plus icon, and each one has a separate sprite.
I am asking specifically about the panel creation. Is something like this even possible where one button can create many panels that are each unique and controlled by a user or am I better off with another language?
Here is the html code I currently have for the button if it matters:
<div class="createBlock">
<button onclick="createBlock()" class="buttonMain menuPlus">+</button>
</div>
Thanks so much!
You can do that with jQuery. Here is what your createBlock function could look like:
/* create a counter to keep track of the blocks */
let counter = 0;
/* this function will fire when the button is clicked */
function createBlock() {
/* increase the counter by 1 */
counter++;
/* clear all blocks after limit and reset counter */
if (counter > 20) {
$(".block-container").find("[class*=block-el]").remove();
counter = 0;
}
/* create new block element */
let block = $("<div />", {
"class": "block-el-" + counter
}).append(
/* append child elements to block */
$("<div />", {
"class": "block-content"
}).text("Content"),
$("<div />", {
"class": "block-close"
}).text("close")
).appendTo(
".block-container" /* append block to dom */
).css({
/* set dynamic styles on block element */
"z-index": counter,
"left": Math.floor(Math.random() * 101) + "%",
"top": Math.floor(Math.random() * 101) + "%"
});
}
/* remove the block when close button clocked. this is important, the selector cant be a dynamic object or it wont work */
$("body").on("click", ".block-close", function() {
/*find the parent block */
$(this).closest("[class*=block-el]").fadeOut("fast", function() {
/* after element fades out remove it */
$(this).remove();
/* subtract 1 from the blocks counter */
counter--;
});
})
/* Styling added just for example */
body,
html {
padding: 0;
margin: 0;
box-sizing: border-box;
}
.block-container {
display: flex;
justify-content: center;
align-items: center;
background: dodgerblue;
width: 100vw;
height: 100vh;
overflow: hidden;
position: relative;
}
.createBlock>button {
border: none;
outline: none;
-webkit-appearance: none;
background: aquamarine;
color: #fff;
padding: 12px;
border-radius: 4px;
z-index: 9000;
position: relative;
width: 140px;
}
[class*="block-el"] {
display: grid;
grid-template-rows: 60% 1fr;
grid-gap: 20px;
background: white;
padding: 15px;
border-radius: 6px;
filter: drop-shadow(0 2px 3px rgba(0, 0, 0, .1));
width: 140px;
height: 140px;
max-width: 100%;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
.block-content {
background: slategrey;
border-radius: 6px;
display: flex;
justify-content: center;
align-items: center;
text-align: center;
color: white;
}
.block-close {
display: flex;
justify-content: center;
align-items: center;
text-align: center;
background: black;
color: white;
cursor: pointer;
border-radius: 4px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="block-container">
<div class="createBlock">
<button onclick="createBlock()" class="buttonMain menuPlus">+</button>
</div>
</div>

addEventListener adds incorrect properties on click - only on 320x480

TL DR it should be display: flex; opacity: 1
I have a menu which works in the following way:
On mouseenter or click, the menu is shown (display: flex, opacity: 1)
On mouseleave or click (outside the menu area) the menu is hidden (display: none, opacity: 0)
The problem occures when I try to "open" the menu in the Dev. Tools on 320x480 resolution.
When I click on the menu area, only #envelope does the transformation. #links (should also transform but don't becouse of the following reasons) which should get display: flex actually gets display: none assigned to it.
Note: It's working in full screen. Something is bothering him with the 320x480 res.
If I can elaborate or provide any additional information, let me know.
Thank you
function hide (){
document.getElementById("links").style.display = "none";
};
function show (){
document.getElementById("links").style.display = "flex";
document.getElementById("links").style.opacity = "1";
};
var menu = document.getElementById("menu");
menu.addEventListener("mouseenter", show);
menu.addEventListener("mouseleave", hide);
menu.addEventListener("click", show);
document.addEventListener("click", function (){
if (this != menu){
document.getElementById("links").style.display="none";
}
});
#menu{
height: 10vh;
background-color: red;
text-align: center;
transition: all 1s ease-out;
padding-top: 5vh;
}
#menu:hover{
color: red;
}
#envelope{
height: 0;
display: block;
background-color: blue;
min-width: 100%;
z-index: 1;
position: absolute;
left: 0;
content: "";
opacity: 0;
transition: all 1.3s ease-out;
}
#links{
height: 0;
display: none;
background-color: pink;
justify-content: center;
z-index: 2;
min-width: 100%;
opacity: 0;
transition: all 1s ease-in;
}
#google{
margin-top: -1vh;
width: 150px;
}
#mysite{
padding-left: 5%;
margin-top: -1vh;
width: 150px;
}
#menu:hover #envelope{
height: 100px;
opacity: 1;
}
#menu:focus #envelope{
height: 100px;
opacity: 1;
}
#menu:hover #links{
opacity: 1;
height: 300px;
}
#menu:focus #links{
opacity: 1;
height: 300px;
}
<div id="menu">Click here to browse the internet.
<div id="envelope">
<div id="links" >
<div><img id="google" src="https://seomofo.com/wp-content/uploads/2010/05/google_logo_new.png" /></div>
<div style="width: 20%;"></div>
<div><img id="mysite" src="https://toppng.com/uploads/preview/wwf-logo-horizontal-world-wildlife-foundation-logo-shirt-11563219164hg5hfcveei.png"/></div>
</div>
</div>
</div>
Don't use transition: all because the browser then need to loop through all properties, and it might cause lag.
Don't use position: absolute unless you have to.
I removed #envelope and inserted the "Click here ..." text in a label (explanation why below).
I arranged classes so I didn't have to repeat code.
Pure CSS solution below.
I made a little CSS hack, where I used a label and a checkbox to simulate a click. So when clicking on the label#menu-toggler, the (hidden) checkbox is checked, which triggers #menu-toggler:checked ~ #links.invisible. I had to add another class to #links, otherwise the low specificity wouldn't trigger the change.
html, body { /* new */
margin: 0px;
padding: 0px;
}
#menu {
height: 15vh; /* changed */
background-color: red;
text-align: center;
margin: 0.5rem; /* new */
}
#menu > input#menu-toggler { /* new */
display: none;
}
#menu > .tagline { /* new */
display: block; /* to get padding to work */
padding: 5vh 0px;
transition: opacity 1s;
}
#menu:hover > .tagline { /* new */
opacity: 0;
}
#menu > .tagline, /* new */
#menu > #links /* new */
{
transition-timing-function: ease-out;
}
#menu > #links {
display: flex;
justify-content: space-around; /* changed */
position: relative; /* changed */
left: -0.5rem; /* changed */
top: -5vh; /* changed */
opacity: 0;
height: 0;
width: 100vw; /* changed */
z-index: 1;
overflow: hidden; /* new */
background-color: pink;
transition-property: height, opacity;
transition-duration: 1.3s;
}
#menu:hover #links,
#menu-toggler:checked ~ #links.invisible { /* new */
height: 150px !important; /* changed */
opacity: 1 !important;
}
#links #google,
#links #mysite
{
width: 150px;
}
<div id="menu">
<input id="menu-toggler" type="checkbox" />
<label for="menu-toggler" class="tagline">Click here to browse the internet.</label>
<div id="links" class="invisible">
<div><img id="google" src="https://seomofo.com/wp-content/uploads/2010/05/google_logo_new.png" /></div>
<div><img id="mysite" src="https://toppng.com/uploads/preview/wwf-logo-horizontal-world-wildlife-foundation-logo-shirt-11563219164hg5hfcveei.png"/></div>
</div>
</div>

Animate two elements from a vertical alignment to horizontal alignment

I have a page with a label directly above a textbox, and this is all centered vertically within the page. After the input is verified I would like to shrink the elements and slide them to the top of the page, while simultaneously "rotating" them to be next to one another, with the label on the left and the textbox on the right. So essentially I want to go from this in the middle of the screen and large:
Label
Textbox
to this at the top of the screen and back to a regular size:
Label: Textbox
I just can't seem to figure out the best way to handle it.
To be clear here, I'm wanting to animate this. To go from the top example to the bottom of the example in fluid motion.
Here is a fiddle of what I'm beginning with: http://jsfiddle.net/xpvt214o/280404/
Here is a fiddle of what I'm wanting to end with: http://jsfiddle.net/xpvt214o/280396/
The motion to get from fiddle 1 to fiddle 2 needs to be animated. That's what I'm struggling with.
Initial response
You can use vertical-align: middle and text-align: center to vertically and horizontally center your content. In the example: .parent1
To have both div next to each other on the same horizontal level use display: inline-block. In the example: .parent2
.parent1, .parent2 {
vertical-align: middle;
text-align: center;
}
.parent1 {
font-size: 30px;
}
.parent2 {
font-size: 20px;
}
.parent2 > div {
display: inline-block;
}
.parent2 > div:nth-child(2):before {
content: ': ';
}
<div class="parent1">
<div>Label</div>
<div>Textbox</div>
</div>
<br /><hr />
<div class="parent2">
<div>Label</div>
<div>Textbox</div>
</div>
With animation
Bonus if you wish to animate it:
You will need transition to set the animation type and duration. Then prepare two CSS classes:
a default one called .parent positionning the item in the center of the screen
.parent {
position: absolute;
left: 25%;
top: calc(50% - 47px);
width: 50%;
vertical-align: middle;
text-align: center;
font-size: 40px;
transition: all 0.5s ease;
}
another one that will be toggled and which overwriting the position of the element to the top of the screen.
.parentMod {
top: 0px;
}
I have added an event listener attached to the button so you can see the animation by toggling the state with a mouse click:
const changeLayout = () => {
document.querySelector('.parent').classList.toggle('parentMod');
};
.container {
height: 200px;
}
.parent {
position: absolute;
left: 25%;
top: calc(50% - 47px);
width: 50%;
vertical-align: middle;
text-align: center;
font-size: 40px;
transition: all 0.5s ease;
}
.parentMod>div {
font-size: 20px;
display: inline-block;
}
.parentMod {
top: 0px;
}
.parentMod>div:nth-child(2):before {
content: ': ';
}
<div class="parent">
<div>Label</div>
<div>Textbox</div>
</div>
<button onclick="changeLayout()">Switch Layout</button>
Note: I have seen the result you wanted to achieve: smoothly translate the two elements from inline to inline-block. I've found a way to do this. As it is a completely different approach from my initial solution, I have decided to post another answer.
Setup
You can achieve the desired result with plain CSS but there are a couple of things to take care of.
we will position the main container .parent with absolute
.parent {
position: absolute;
/* dimensions */
width: 35%
height: 20%;
/* centered horizontally */
margin: 0 auto;
left: 0;
right: 0;
/* centered vertically */
bottom: 0;
top: 45%;
}
to change the layout .parent will get the class .parentMod which will overwrite some of .parent's initial properties:
.parentMod {
top: 10px;
height: 12%;
}
both sub elements are too positioned with absolute their vertical position inside .parent have to be set (with percentages to ensure the layout is responsive):
.parent > div:nth-child(1) {
left: calc(50% - 125px);
}
.parent > div:nth-child(2) {
left: calc(50% - 10px);
}
then we can set their position when the layout has changed:
both sub elements will have the same vertical position:
.parentMod > div:nth-child(1),
.parentMod > div:nth-child(2) {
top: 0px;
left: 0px;
right: 0px;
width: 100%;
}
the second sub element will have a vertical offset (it will be positioned below the first sub element):
.parentMod > div:nth-child(2) {
top: 35px;
}
Demo
Overall the result will look like:
const changeLayout = () => {
document.querySelector('.parent').classList.toggle('parentMod');
};
.parent {
position: absolute;
margin: 0 auto;
bottom: 0;
right: 0;
top: 45%;
left: 0;
width: 35%;
height: 20%;
font-size: 40px;
transition: all 2s ease;
}
.parent > div {
text-align: center;
width: 130px;
margin: 0px;
position: absolute;
top: 10px;
transition: all 2s;
}
.parent > div:nth-child(1) {
left: calc(50% - 125px);
}
.parent > div:nth-child(2) {
left: calc(50% - 10px);
}
.parentMod {
top: 10px;
height: 12%;
}
.parentMod > div:nth-child(1),
.parentMod > div:nth-child(2) {
top: 0px;
left: 0px;
right: 0px;
width: 100%;
}
.parentMod > div:nth-child(2) {
top: 35px;
}
<div class="parent">
<div>Label</div>
<div>Textbox</div>
</div>
<button onclick="changeLayout()">Switch Layout</button>
You can try positioning them absolutely and using flex-box on the parent to center-align them.
It's not a very flexible solution, but it gets the job done!
HTML
<div class="container">
<span id="one">Label</span>
<span id="two">Textbox</span>
</div>
CSS
.container {
position: relative;
width: 200px;
height:200px;
display: flex;
flex-flow: column;
align-items: center;
}
#one, #two {
font-size: 1em;
display: block;
height:1em;
text-align: center;
margin: 0;
transition: all 500ms ease;
}
#one {
position: absolute;
}
#two {
position:absolute;
margin-top:1em;
}
.container:hover #one,
.container:hover #two {
font-size: .75em
}
.container:hover #one{
margin-left: -10px;
}
.container:hover #two{
margin-top: 0;
margin-left:30px
}

Achieve animated seesaw -ish effect with linear-gradient (pure CSS)

I have an element with one diagonal side achieved by adjusting linear-gradient and height - in two different states. Now I try to toggle between these states and have a smooth transition of the red triangle, so that it would look like a seesaw :-) The problem is, that from one state to another it changes the direction and is jumpy, I did not find a way to animate it fluently .. Is there a way to to what I want using pure CSS e.g. using transitions?
let btn = document.getElementsByTagName('button')[0];
let stage = document.getElementById('stage');
btn.addEventListener('click', function() {
stage.classList.toggle('fixie');
});
body,
ul {
padding: 0;
margin: 0;
}
#stage {
width: 100%;
height: 14em;
background: pink;
padding: 0;
border: 1px solid blue;
}
#stage::before {
display: block;
position: relative;
width: 100%;
height: 100%;
/*as high as #stage*/
opacity: 0.4;
content: '';
z-index: 1;
background: linear-gradient(to left bottom, red 50%, pink 50%);
/*transition: height 4s;*/
/*transition: linear-gradient 4s 8s;*/
}
#stage.fixie::before {
height: 30%;
background: linear-gradient(to right bottom, red 50%, pink 50%);
}
<div id="stage"></div>
<button>animate gradient</button>
Here is my FIDDLE
As you can't animate linear-gradient, here is a workaround using transform
In this sample I used skew. As the degree of skew will differ based on the width/height, and as long as its ratio is kept, this will be fully responsive, else you'll need a small script.
(function(){
let btn = document.getElementsByTagName('button')[0];
let stage = document.getElementById('stage');
btn.addEventListener('click', function() {
stage.classList.toggle('fixie');
});
})();
body, ul {
padding: 0;
margin: 0;
}
#stage {
position: relative;
overflow: hidden;
width: 90vw;
height: calc(90vw * 0.2677); /* 0.2677, aspect ratio that match skew degree */
background: pink;
padding: 0;
border: 1px solid blue;
}
.navi {
width: 100%;
min-height: 4em;
height: auto;
background: green;
z-index: 2;
position: relative;
}
#stage::before {
content: '';
position: absolute;
width: 100%;
height: 100%; /*as high as #stage*/
bottom: 100%;
opacity: 0.4;
z-index: 1;
background: red;
transform: skewY(15deg);
transform-origin: left bottom;
transition: transform 2s;
}
#stage.fixie::before {
transform: skewY(-15deg) translateY(100%);
}
.navi ul {
list-style: none;
display: flex;
background: lightblue;
justify-content: flex-end;
}
.navi ul li {
display: flex;
align-items: center;
justify-content: center;
min-width: 4em;
width: auto;
height: 2em;
margin: 1px;
background: yellow;
}
<div id="stage"></div>
<button>
animate
</button>
Side note:
One can use any fixed value instead of vw, as long as the #stage's ratio is kept. If to change ratio, you'll either need a script, since CSS calc can't do math with sqrt and sin/cos etc. to get the angle, or using media query's, and have angle's and ratio's manually set for different screens.

Categories