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

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.

Related

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

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>

How can I create a circle with elements at the very end of its axis?

I'll be using some SVGs that will be those avatars on the axis. For now all I need is a hint on how to set those avatars at the very end of every axis.
I am trying to achieve this:
This the code I have so far:
body {
background-color: #de4e40;
text-align: center;
justify-content: center;
align-items: center;
height: 100vh;
width: 100vw;
margin: 0;
display: flex;
}
.squareLoader {
height: 200px;
width: 200px;
background-color: white;
border: 1px solid black;
border-radius: 50%;
}
<div class='squareLoader'></div>
And here is a codepen in case you want to take a look.
Is there any guide or someone can help me on how to achieve this?
I can use flexbox and also I am using React Native. Just in case.
Make squareLoader a relative parent
Position element in corners using translate, top, left
Share styles wherever possible
Store repeated offset value in CSS variable
:root {
--offset: -1.4em;
}
body {
background-color: #de4e40;
text-align: center;
justify-content: center;
align-items: center;
height: 100vh;
width: 100vw;
margin: 0;
display: flex;
}
.squareLoader {
height: 200px;
width: 200px;
background-color: white;
border: 1px solid black;
border-radius: 50%;
position: relative;
}
.squareLoader img {
position: absolute;
width: 50px;
}
.one, .three {
left: 50%;
transform: translateX(-50%);
}
.two, .four {
top: 50%;
transform: translateY(-50%);
}
.one {
top: var(--offset);
}
.two {
right: var(--offset);
}
.three {
bottom: var(--offset);
}
.four {
left: var(--offset);
}
<div class="squareLoader">
<img src="https://image.flaticon.com/icons/svg/190/190675.svg" alt="" class="one">
<img src="https://image.flaticon.com/icons/svg/190/190675.svg" alt="" class="two">
<img src="https://image.flaticon.com/icons/svg/190/190675.svg" alt="" class="three">
<img src="https://image.flaticon.com/icons/svg/190/190675.svg" alt="" class="four">
</div>
https://jsfiddle.net/uf2t5p6r/3/
If I understand your post correctly, you want to place SVG images on the North, South, East, and West points of a circle with CSS; It is definitely possible to do this with CSS, but it depends completely on the size of the container and the size of the images being used.
The example circle you provided has a set height and width, so assuming that your SVG images are also a set size and do not change with page size you can do something like this.
body {
background-color: #de4e40;
text-align: center;
justify-content: center;
align-items: center;
height: 100vh;
width: 100vw;
margin: 0;
display: flex;
}
.squareLoader {
/* added 'position: relative', because parent elements of absolute positioned elements have to be positioned to prevent mayhem */
position: relative;
height: 200px;
width: 200px;
background-color: white;
border: 1px solid black;
border-radius: 50%;
}
.avatar {
position: absolute;
left: 75px;
top: 75px;
border: 1px solid black;
height: 50px;
width: 50px;
}
.avatarN {
top: -25px;
}
.avatarS {
top: 175px;
}
.avatarE {
left: 175px;
}
.avatarW {
left: -25px;
}
and the HTML with .avatar <div>s (you can stick the SVG images inside these divs):
<div class='squareLoader'>
<div class="avatar avatarN"></div>
<div class="avatar avatarS"></div>
<div class="avatar avatarE"></div>
<div class="avatar avatarW"></div>
</div>
In the previous example all four avatar <div>s are given a height and width of 50px.
.avatarN has a top position of -25px because that is half of the image's height and a left position of 75px (half of .squareLoader's width minus half of the image's width).
.avatarW has a top position of 75px (half of .squareLoader's height minus half of the image's height) and a left position of 25px, which is half of the image's height.
And so on for the other divs.
If you are going to make your .squareLoader <div> resize based on the page size, and the images resize based on page size, you can use the calc() CSS function which can be used anywhere a length unit is allowed in CSS.
https://developer.mozilla.org/en-US/docs/Web/CSS/calc
w3Schools has a pretty decent cursory explanation of all the units allowed in CSS:
https://www.w3schools.com/cssref/css_units.asp
And also, here's their page on position values in CSS as well:
https://www.w3schools.com/cssref/pr_class_position.asp
Whether or not you are using flexbox, you'll still have to use the position property to get the images where you want them.
One idea to create this is to consider only background and it will be easier to handle:
body {
background-color: #de4e40;
text-align: center;
justify-content: center;
align-items: center;
height: 100vh;
width: 100vw;
margin: 0;
display: flex;
}
.squareLoader {
height: 250px;
width: 250px;
background:
url(https://picsum.photos/50/50?image=1069) top center,
url(https://picsum.photos/50/50?image=1069) bottom center,
url(https://picsum.photos/50/50?image=1069) left center,
url(https://picsum.photos/50/50?image=1069) right center,
/*the circle*/
radial-gradient(farthest-side,
#fff calc(100% - 32px),#000 calc(100% - 30px),
#000 calc(100% - 30px),#000 calc(100% - 20px),
transparent calc(100% - 18px));
background-repeat:no-repeat;
}
<div class='squareLoader'></div>

How do I overlay text in a transparent box over my image slider? [duplicate]

I am trying to achieve something like this:
When I hover over an image, I would like to put on that image this dark color with some text and the icon.
I am stuck here. I found some tutorials but they didn't work out for this case.
Also, another issue -- every image has a different height. The width is always the same.
How can this effect be achieved?
You can achieve this with this simple CSS/HTML:
.image-container {
position: relative;
width: 200px;
height: 300px;
}
.image-container .after {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: none;
color: #FFF;
}
.image-container:hover .after {
display: block;
background: rgba(0, 0, 0, .6);
}
HTML
<div class="image-container">
<img src="http://lorempixel.com/300/200" />
<div class="after">This is some content</div>
</div>
Demo: http://jsfiddle.net/6Mt3Q/
UPD: Here is one nice final demo with some extra stylings.
.image-container {
position: relative;
display: inline-block;
}
.image-container img {display: block;}
.image-container .after {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: none;
color: #FFF;
}
.image-container:hover .after {
display: block;
background: rgba(0, 0, 0, .6);
}
.image-container .after .content {
position: absolute;
bottom: 0;
font-family: Arial;
text-align: center;
width: 100%;
box-sizing: border-box;
padding: 5px;
}
.image-container .after .zoom {
color: #DDD;
font-size: 48px;
position: absolute;
top: 50%;
left: 50%;
margin: -30px 0 0 -19px;
height: 50px;
width: 45px;
cursor: pointer;
}
.image-container .after .zoom:hover {
color: #FFF;
}
<link href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.min.css" rel="stylesheet"/>
<div class="image-container">
<img src="http://lorempixel.com/300/180" />
<div class="after">
<span class="content">This is some content. It can be long and span several lines.</span>
<span class="zoom">
<i class="fa fa-search"></i>
</span>
</div>
</div>
You could use a pseudo element for this, and have your image on a hover:
.image {
position: relative;
height: 300px;
width: 300px;
background: url(http://lorempixel.com/300/300);
}
.image:before {
content: "";
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
transition: all 0.8s;
opacity: 0;
background: url(http://lorempixel.com/300/200);
background-size: 100% 100%;
}
.image:hover:before {
opacity: 0.8;
}
<div class="image"></div>
Putting this answer here as it is the top result in Google.
If you want a quick and simple way:
filter: brightness(0.2);
*Not compatible with IE
A bit late for this, but this thread comes up in Google as a top result when searching for an overlay method.
You could simply use a background-blend-mode
.foo {
background-image: url(images/image1.png), url(images/image2.png);
background-color: violet;
background-blend-mode: screen multiply;
}
What this does is it takes the second image, and it blends it with the background colour by using the multiply blend mode, and then it blends the first image with the second image and the background colour by using the screen blend mode. There are 16 different blend modes that you could use to achieve any overlay.
multiply, screen, overlay, darken, lighten, color-dodge, color-burn, hard-light, soft-light, difference, exclusion, hue, saturation, color and luminosity.
.bg-img{
text-align: center;
padding: 130px 0px;
width: 100% !important;
background-size: cover !important;
background-repeat: no-repeat !important;
background: linear-gradient(0deg, rgba(0, 0, 0, 0.86), rgba(0, 0, 0, 0.86)), url(your-img-path);
}

Calculate left css value for skewed div

I have a wiper div which I want to use to wipe diagonally through a parent div.
The wiper div has the following class:
.wiper {
position: absolute;
height: 100%;
top: 0;
left: 0;
background-color: orange;
transform: skew(45deg);
}
I want the wiper to start offscreen to the right, and end offscreen to the left.
When the wiper is halfways, the screen must be completely filled by the wiper.
The problem is, I don't know how big the wipers parent is. So I need to calculate the following:
Width: How wide in percent must the wiper be, to fill the screen when it is halfway.
Startleft: What should the starting left property be in percent, for the wiper to be just offscreen right.
Endleft: What should the ending left property be, for the wiper to be just offscreen left.
Here is an example in jsfiddle, but with hardcoded values. I just don't know how to calculate the relative values, when dealing with a skewed div.
http://jsfiddle.net/jpg850kx/22/
I would do something differently using gradient where you don't need complex calculation:
body,
html {
width: 100%;
height: 100%;
box-sizing: border-box;
overflow: hidden;
padding: 20px;
}
#wrapper {
width: 60%;
height: 100%;
position: relative;
overflow: hidden;
}
.content {
width: 100%;
height: 100%;
color: white;
display: flex;
justify-content: center;
align-items: center;
background-color: black;
}
.wiper {
position: absolute;
height: 200%;
width: 400%;
top: 0;
left: 100%;
z-index:1;
background-image:
linear-gradient(to bottom left,orange 49.5%,transparent 50%),
linear-gradient(to top right,orange 49.5%,transparent 50%);
background-size:50.1% 100%;
background-repeat:no-repeat;
background-position:left,right;
transition:all 3s;
}
#wrapper:hover .wiper{
left:-300%;
}
<div id="wrapper">
<div class="content">
Old content
</div>
<div class="wiper"></div>
</div>

Horizontally fix background image to logo in header

my problem is quite simple. I need the diagonal line in the background image to align with the "X" in the logo and make it stay there no matter how wide the viewport is. I tried to achieve it with css only but with no result. It´s gonna need some javascript I suppose but I have no idea where to start.
<body>
<header>
<div class="container">
<svg class="logo"></svg>
</div>
</header>
</body>
* {
box-sizing: border-box
}
body {
background: url(http://mujtest.tk/ci/site/templates/img/hero2.jpg) no-repeat center center;
background-size: cover;
background-attachment: fixed;
height: 100vh;
}
header {
opacity: 1;
height: 135px;
line-height:135px;
position: fixed;
width: 100%;
top: 0;
left: 0;
z-index: 5;
transition: all 0.3s;
z-index: 1000;
text-align: center;
}
.container {
width: 100%;
max-width: 1200px;
line-height: 135px;
margin: 0 auto;
display: inline-block;
vertical-align: middle;
padding: 0 80px;
}
.logo {
max-width: 200px;
float: left;
}
http://codepen.io/easynowbaby/pen/qdLbgP
Thanks!
looks like what you want is to play around with the background position, try something like this,
background: url(http://mujtest.tk/ci/site/templates/img/hero2.jpg) no-repeat center -110px;

Categories