Trying to do some CSS3 transitions and animations using 2 images.
Our requirement is
First display the background image
Move it slightly on the northward direction
Display the background image for few seconds (pause effect)
After few seconds introduce the foreground image (fade in effect)
Slightly move the image in northward direction
Fade out the foreground image
But we are unable to achieve the above exactly. Currently the background and foreground image are moving almost at the same time, unable to achieve the 'fade in' effect for the foreground image.
Demo Link: https://jsfiddle.net/sandeepskm/kLtyssjc/
Please help us out.
Our code
HTML5 Code
<div id="a" class="animated slideInUp">
<div id="b" class="animated slideInUpChild">
<img src="https://cdn3.iconfinder.com/data/icons/black-easy/512/535106-user_512x512.png" width="150px" alt="" />
</div>
</div>
CSS3 Code
#a
{
width: 100%;
height: 600px;
margin: 0 0 0 0;
padding: 0 0 0 0;
position: relative;
background-image: url(http://webneel.com/wallpaper/sites/default/files/images/08-2013/11-sea-beach-sand-wallpaper.jpg);
}
#b
{
width: 100%;
height: 10px;
margin: 0 0 0 0;
padding: 0 0 0 0;
position: absolute;
bottom: 0;
}
.animated
{
-webkit-animation-duration: 1s;
animation-duration: 1s;
}
.slideInUp
{
-webkit-animation-name: slideInUp;
animation-name: slideInUp;
-webkit-animation-duration: 1s;
animation-duration: 1s;
}
.slideInUpChild
{
-webkit-animation-name: slideInUpChild;
animation-name: slideInUpChild;
-webkit-animation-delay: 1s;
animation-delay: 1s;
}
#keyframes slideInUp
{
from
{
-webkit-transform: translate3d(0, 100%, 0);
transform: translate3d(0, 100%, 0);
visibility: visible;
}
to
{
-webkit-transform: translate3d(0, 10%, 0);
transform: translate3d(0, 10%, 0);
}
}
#-webkit-keyframes slideInUp
{
from
{
-webkit-transform: translate3d(0, 100%, 0);
transform: translate3d(0, 100%, 0);
visibility: visible;
}
to
{
-webkit-transform: translate3d(0, 10%, 0);
transform: translate3d(0, 10%, 0);
}
}
#-webkit-keyframes slideInUpChild
{
from
{
bottom: 0;
}
to
{
bottom: calc(100% - 100px);
}
}
#keyframes slideInUpChild
{
from
{
bottom: 0;
}
to
{
bottom: calc(100% - 100px);
}
}
You can achieve this by doing the following changes:
Set initial opacity of the element that contains the image as 0 because it needs to fade-in later.
To make sure that the foreground image fades-in and moves up a few seconds after background image has appeared and taken its position, add a delay that is more than animation-duration of the background image. Here, I have set it as 2s. (I have also increased animation-duration of the foreground image to make the effect more visible but that is optional).
Within the keyframes setting for the foreground image, make the initial state as opacity: 0 and bottom: 150px (this is equal to the height of the image).
Since there are 3 stages of animation for the foreground image (that is, the fade-in, the move and the fade-out), set the splits as 33%, 66% and 100%.
At 33% change its opacity alone to 1 while bottom position remains the same. This produces the fade-in effect.
At 66% retain the opacity as 1 but change the bottom position as required. This means that the image moves-up while still being visible.
At 100%, retain the bottom position as-is but change the opacity to 0. This makes it fade-out.
Modified CSS:
.slideInUpChild {
opacity: 0;
animation-name: slideInUpChild;
animation-duration: 4s;
animation-delay: 2s;
}
#keyframes slideInUpChild {
0% {
opacity: 0;
bottom: 150px;
}
33% {
opacity: 1;
bottom: 150px;
}
66% {
opacity: 1;
bottom: calc(100% - 100px);
}
100% {
opacity: 0;
bottom: calc(100% - 100px);
}
}
#a {
width: 100%;
height: 600px;
margin: 0 0 0 0;
padding: 0 0 0 0;
position: relative;
background-image: url(http://webneel.com/wallpaper/sites/default/files/images/08-2013/11-sea-beach-sand-wallpaper.jpg);
overflow: hidden;
}
#b {
width: 100%;
height: 10px;
margin: 0 0 0 0;
padding: 0 0 0 0;
position: absolute;
bottom: 0;
}
.animated {
animation-duration: 1s;
}
.slideInUp {
animation-name: slideInUp;
animation-duration: 1s;
}
.slideInUpChild {
opacity: 0;
animation-name: slideInUpChild;
animation-duration: 4s;
animation-delay: 2s;
}
#keyframes slideInUp {
from {
transform: translate3d(0, 100%, 0);
}
to {
transform: translate3d(0, 0%, 0);
}
}
#keyframes slideInUpChild {
0% {
opacity: 0;
bottom: 150px;
}
33% {
opacity: 1;
bottom: 150px;
}
66% {
opacity: 1;
bottom: calc(100% - 100px);
}
100% {
opacity: 0;
bottom: calc(100% - 100px);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<div id="a" class="animated slideInUp">
<div id="b" class="animated slideInUpChild">
<img src="https://cdn3.iconfinder.com/data/icons/black-easy/512/535106-user_512x512.png" width="150px" alt="" />
</div>
</div>
Related
So I'm aiming to have two circles meet from either side of the screen and meet in the middle to perform the second half of the animation (scaling and opacity change).
But by setting the initial keyframe and last using vw they don't meet in the middle - since the vw value is relative to the left side of the div and not the centre (I have used vw as I need this to be responsive). So, what happens is that the left sides of the circle meet in the centre.
Does anyone know a simple fix to this using just css? I am newish to coding, so if the answer is obvious I apologise.
Here is my code:
#keyframes left {
0% {
transform: translate3d(0vw, 50%, 0) scale3d(1, 1, 1);
opacity: 50%;
animation-timing-function: ease-in;
}
60% {
transform: translate3d(50vw, 50%, 0) scale3d(1, 1, 1);
opacity: 50%;
animation-timing-function: ease-out;
}
100% {
transform: translate3d(50vw, 50%, 0) scale3d(2, 2, 1);
opacity: 0%;
animation-timing-function: ease-out;
}
}
#keyframes right {
0% {
transform: translate3d(100vw, 50%, 0) scale3d(1, 1, 1);
opacity: 50%;
animation-timing-function: ease-in;
}
60% {
transform: translate3d(50vw, 50%, 0) scale3d(1, 1, 1);
opacity: 50%;
animation-timing-function: ease-out;
}
100% {
transform: translate3d(50vw, 50%, 0) scale3d(2, 2, 1);
opacity: 0%;
animation-timing-function: ease-out;
}
}
.circleleft {
overflow: hidden;
position: absolute;
background: white;
border-radius: 50%;
width: 500px;
height: 500px;
animation: left 2s;
animation-fill-mode: forwards;
}
.circleright {
overflow: hidden;
position: absolute;
background: white;
border-radius: 50%;
width: 500px;
height: 500px;
animation: right 2s;
animation-fill-mode: forwards;
}
<div style="width:100vw; height:100vh; background-color:#87827E">
<div class="circleleft"></div>
<div class="circleright"></div>
</div>
You can see it in use here too: https://ruairimadine.co.uk/sudoroux
One trick is to initially position both circles in the center and the animation/translation will offset them from the left or right.
I optimized the code to only use pseudo-elements and make it easier to understand:
body {
margin: 0;
height: 100vh;
background-color: #87827E;
overflow: hidden;
position:relative;
}
body::before,
body::after{
content:"";
position: absolute;
top: calc(50% - 25vmin);
left:calc(50% - 25vmin);
background: white;
opacity: 50%;
border-radius: 50%;
width: 50vmin;
height: 50vmin;
animation: move 2s forwards;
}
/* 50vw : half the screen width | 25vmin half the circle width*/
body::before { transform:translateX(calc( 50vw + 25vmin)); }
body::after { transform:translateX(calc(-50vw - 25vmin)); }
#keyframes move {
60% {
transform: translateX(0) scale(1);
opacity: 50%;
}
100% {
transform: translateX(0) scale(2);
opacity: 0%;
}
}
In this example the circles size is stored in the root variable --circle-size: 100px;. So the circles can be centered with top and left easily. The animation uses the properties left (position), opacity and transform: scale (scaling).
setTimeout(()=>{
document.querySelector('.circle-left').classList.add('circle__animated');
document.querySelector('.circle-right').classList.add('circle__animated');
}, 1000);
:root{
--circle-size: 100px;
}
.circle{
position: absolute;
width: var(--circle-size);
height: var(--circle-size);
border-radius: 50%;
top: calc(50% - var(--circle-size)/2);
}
.circle.circle-left{
background: red;
left: 0;
animation: left 2s;
animation-fill-mode: forwards;
}
.circle.circle-right{
background: green;
left: calc(100% - var(--circle-size));
animation: right 2s;
animation-fill-mode: forwards;
}
#keyframes left {
0% {
left: 0;
opacity: 1;
transform: scale(1);
animation-timing-function: ease-in;
}
60% {
left: calc(50% - var(--circle-size)/2);
opacity: 0.5;
transform: scale(1);
animation-timing-function: ease-out;
}
100% {
left: calc(50% - var(--circle-size)/2);
opacity: 0;
transform: scale(5);
animation-timing-function: ease-out;
}
}
#keyframes right {
0% {
left: calc(100% - var(--circle-size));
opacity: 1;
transform: scale(1);
animation-timing-function: ease-in;
}
60% {
left: calc(50% - var(--circle-size)/2);
opacity: 0.5;
transform: scale(1);
animation-timing-function: ease-out;
}
100% {
left: calc(50% - var(--circle-size)/2);
opacity: 0;
transform: scale(5);
animation-timing-function: ease-out;
}
}
<div style="position: absolute; top:0; left: 0; width:100vw; height:100vh; background-color:#87827E; padding: 0; margin: 0; overflow: hidden;">
<div class="circle circle-left"></div>
<div class="circle circle-right"></div>
</div>
I am displaying div from right to left and after 5 sec it will hide. I tried some code right to left is working but after 5 sec it's not hiding.
I tried opacity:0 inside keyframe but then my animation not working.
Would you help me out in this?
.successAlet {
background-color: red;
color: #fff;
position: fixed;
top: 0;
width: 400px;
right: 0;
z-index: 1001;
-webkit-animation-name: fadeInRight;
animation-name: fadeInRight;
-webkit-animation-duration: 1s;
animation-duration: 1s;
-webkit-animation-fill-mode: both;
animation-fill-mode: both;
}
#-webkit-keyframes fadeInRight {
0% {
opacity: 0;
-webkit-transform: translate3d(100%, 0, 0);
transform: translate3d(100%, 0, 0);
}
100% {
opacity: 1;
-webkit-transform: none;
transform: none;
}
}
#keyframes fadeInRight {
0% {
opacity: 0;
-webkit-transform: translate3d(100%, 0, 0);
transform: translate3d(100%, 0, 0);
}
100% {
opacity: 1;
-webkit-transform: none;
transform: none;
}
}
<div class="successAlet">
<h2>Animate and then autohide in 5 sec</h2>
</div>
Consider a second animation. You can also simplify your code by removing a lot of non needed properties
.successAlet {
background-color: red;
color: #fff;
position: fixed;
top: 0;
width: 400px;
right: 0;
z-index: 1001;
animation-name: fadeInRight,hide;
animation-duration: 1s;
animation-delay: 0s,3s;
animation-fill-mode: both;
}
#keyframes fadeInRight {
0% {
opacity: 0;
transform: translate3d(100%, 0, 0);
}
}
#keyframes hide {
100% {
opacity: 0;
}
}
<div class="successAlet">
<h2>Animate and then autohide in 5 sec</h2>
</div>
for smooth hide
#keyframes hide {
100% {
opacity: 1;
width: 0;
}
}
I want to have a straight line in my header, and then a few seconds after the page loads, I want those lines to slowly move down until they look like the one in the image below:
I thought of using css transform property to rotate two rotate two divs, but that does not seem to be a solution as you can see the result in my pen here
HTML:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.0/jquery.min.js"></script>
<div id="big">
<div class="arrow-box">
<div class="line line-1"></div>
<div class="line line-2"></div>
</div>
</div>
CSS:
#big{
background: red;
height: 200px;
}
.arrow-box{
max-width: 200px;
margin: 0 auto;
padding-top: 10px;
}
.line{
background: white;
width: 60px;
height: 1px;
}
.line-1{
transform: rotate(20deg);
}
.line-2{
transform: rotate(-20deg);
}
How can I make a/the line look like the icon on the image after the page loads?
You could do this using css animation. You could use rotateZ transform to create arrow shape and also scale to keep increasing width of the lines as animation goes.
You also need to use transform-origin for both parts to transform at the right point.
.line {
position: relative;
width: 100px;
}
.line:after,
.line:before {
background: black;
position: absolute;
content: "";
height: 2px;
width: 50%;
bottom: 0;
}
.line:before {
left: 0;
animation: moveBefore 1s linear forwards;
transform-origin: center left;
}
.line:after {
right: 0;
animation: moveAfter 1s linear forwards;
transform-origin: center right;
}
#keyframes moveBefore {
0% {
transform: rotateZ(0) scale(1, 1);
}
50% {
transform: rotateZ(15deg) scale(1.05, 1);
}
100% {
transform: rotateZ(30deg) scale(1.16, 1);
}
}
#keyframes moveAfter {
0% {
transform: rotateZ(0) scale(1, 1);
}
50% {
transform: rotateZ(-15deg) scale(1.05, 1);
}
100% {
transform: rotateZ(-30deg) scale(1.16, 1);
}
}
<div class="line"></div>
You could also do this with svg using line element and some javascript to move y position left and right line parts. To increase angle gradually you can use setInterval method.
let step = 0;
const left = document.querySelector('.left-line');
const right = document.querySelector('.right-line');
function move(el, prop, size) {
el.setAttribute(prop, +el.getAttribute(prop) + size);
}
setInterval(() => {
if (step <= 40) {
move(left, 'y2', 0.8);
move(right, 'y1', 0.8)
step += 1;
}
}, 30)
<svg xmlns="http://www.w3.org/2000/svg">
<line class="left-line" x1="0" y1="20" x2="40" y2="20" stroke="black" />
<line class="right-line" x1="40" y1="20" x2="80" y2="20" stroke="black" />
</svg>
While the accepted answer works just fine the artist in me can't take the overlapping of the lines in the center due to the scaling. Here's a few alternate options:
Option 1 - clip-path
Using clip-path, animate the mid-points of a rectangle to transform the polygon into a chevron. This works by masking the background color of the element outside of the animated shape.
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.line {
display: inline-block;
width: 200px;
height: 50px;
background-color: black;
clip-path: polygon(0 0, 100% 0, 100% 2px, 0 2px);
animation: 2s infinite linear;
}
.line.down {
animation-name: chevron-down;
}
.line.up {
animation-name: chevron-up;
}
#keyframes chevron-down {
from {
clip-path: polygon(0 0, 50% 0, 100% 0, 100% 2px, 50% 2px, 0 2px);
}
to {
clip-path: polygon(0 0, 50% 48px, 100% 0, 100% 2px, 50% 50px, 0 2px);
}
}
#keyframes chevron-up {
from {
clip-path: polygon(0 48px, 50% 48px, 100% 48px, 100% 50px, 50% 50px, 0 50px);
}
to {
clip-path: polygon(0 0, 50% 48px, 100% 0, 100% 2px, 50% 50px, 0 2px);
}
}
<div class="line down"></div>
<div class="line up"></div>
Support for clip-path is spotty, however.
Option 2 - pseudo-elements
If you can't use clip-path or prefer to use pseudo elements, change their placement and origins of transform to come from the center (and not the upper corners):
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.line {
position: relative;
width: 200px;
height: 50px;
overflow: hidden;
}
.line::before,
.line::after {
position: absolute;
content: '';
display: block;
bottom: 0;
height: 2px;
width: 50%;
background-color: black;
animation: 2s linear infinite;
}
.line::before {
transform-origin: bottom right;
left: 0;
animation-name: before;
}
.line::after {
transform-origin: bottom left;
right: 0;
animation-name: after;
}
#keyframes before {
to { transform: rotateZ(30deg); }
}
#keyframes after {
to { transform: rotateZ(-30deg); }
}
<div class="line"></div>
I try to do an animated box that will unfold itself on the page load.
I have a panel lifted up. I try to rotate it 90deg to the ground, and after, I would want to lift it up again based on the other edge 90deg.
I tried to change the origin (transform-origin: top) but it change the origin to the original origin. I had to add 2 translation to position it at the right place but it create a bump. The edge on the ground don't stick on the ground.
Here's my current fiddle: https://jsfiddle.net/hbnta1uj/2/
I also tried without modifying the origin but I still get a bump:
#keyframes slideFront2 {
0% {
transform: rotateX(-0deg);
}
50% {
transform: rotateX(-90deg);
}
100% {
transform: rotateX(-180deg) translateZ(-100px) translateY(100px);
}
}
I have another idea where I position the second panel flat already and I hide it (opacity 0) and at 50% when the first panel is flat I show the second and just 90deg it.
But I would want to know for more complex animations if there's a way to do it the way I describe it, by always start at the new position the new transformation?
Thanks
I would consider an animation on the container to make it easier where you only need one keyframe:
* {
box-sizing: border-box;
}
body {
margin: 0;
}
.container {
width: 90%;
margin: auto;
height: 100vh;
background-color: rgb(194, 194, 194);
}
.progressbar-wrapper {
width: 300px;
height: 100px;
top: calc(50% - 50px);
left: calc(50% - 150px);
position: absolute;
transform-style: preserve-3d;
transform: rotateX(-20deg) rotateY(-30deg);
}
.progressbar {
width: 100%;
height: 100%;
transform-origin: bottom;
animation: 0.5s ease-out 1 slideFront forwards;
transform-style: preserve-3d;
}
.side {
width: 100%;
height: 100%;
background-color: rgba(254, 254, 254, 0.3);
position: absolute;
top: 0;
left: 0;
}
#keyframes slideFront {
100% {
transform: rotateX(-90deg);
}
}
.bottom {
box-shadow: 10px 10px 50px 5px rgba(90, 90, 90, 0.7);
}
.back {
animation: 1s ease-out 0.5s 1 slideFront forwards;
transform-origin: top;
}
<div class="container">
<div class="progressbar-wrapper">
<div class="progressbar">
<div class="side back">
</div>
<div class="side bottom">
</div>
</div>
</div>
</div>
The thing I discover is that the order matter in the translate function. Everything is executed left to right so the origin of the rotation will be relative to the current position of the element if you execute everything left to right (Here's 2 boxes getting the same rotation translation but the order differ: https://codepen.io/anon/pen/oOQGPp)
So in my example, if you do:
50.001% {
transform: rotateX(90deg) translateZ(00px) translatey(100px) ;
transition-timing-function: linear;
}
100% {
transform: rotateX(0deg) translateZ(100px) translatey(00px) ;
transition-timing-function: linear;
}
The rotation will be applied before the translation so the rotation origin will not be the bottom line after the translation but it will be the position without the origin based on the translated part (So it will be the 0% position origin.) CSS will fail to make the path of the animation and it will add a little bump.
But if you do transform: TRANSLATE ROTATE, the rotation will be applied after the translation so the origin of the rotation will be related to the position with the translation. This is how I was able to rotate the item without getting the little bump bug.
Here's the full fixed css. You can run it in my original jsfiddle to see the result
* {
box-sizing: inherit;
margin: 0;
}
html {
box-sizing: border-box;
}
.container {
width: 90%;
margin: auto;
height: 100vh;
background-color: rgb(194, 194, 194);
}
.progressbar-wrapper {
width: 300px;
height: 100px;
top: 50%;
left: 50%;
position: absolute;
transform: translate(-50%, -50%);
}
.progressbar {
width: 100%;
height: 100%;
transform-style: preserve-3d;
transform: rotateX(-20deg) rotateY(-30deg);
}
.side {
width: 100%;
height: 100%;
background-color: rgba(254, 254, 254, 0.3);
position: absolute;
top: 0;
left: 0;
}
#keyframes slideBottom {
0% {
transform: rotateX(-0deg);
}
100% {
transform: rotateX(-90deg);
}
}
#keyframes slideFront {
0% {
transform: rotateX(-0deg);
}
50% {
transform: rotateX(-90deg);
}
50.001% {
transform: translateZ(100px) rotateX(90deg);
transition-timing-function: linear;
}
100% {
transform: translateZ(100px) rotateX(0deg) ;
transition-timing-function: linear;
}
}
.bottom {
animation: 0.5s ease-out 0s 1 slideBottom forwards;
box-shadow: 10px 10px 50px 5px rgba(90, 90, 90, 0.7);
transform-origin: bottom;
}
.back {
animation: 1s ease-out 0s 1 slideFront forwards;
transform-origin: bottom;
}
I have a small horizontal notification bar that slides up from the bottom of the page.
It comes up fine, but when you open up the page it quickly flashes, then disappears and then slides up.
How do I modify it so it doesn't appear/disappear before the transition takes place?
#notificationBarBottom {
position: fixed;
z-index: 101;
bottom: 0;
left: 0;
right: 0;
background: #5cb85c;
color: #ffffff;
text-align: center;
line-height: 2.5;
overflow: hidden;
-webkit-box-shadow: 0 0 5px black;
-moz-box-shadow: 0 0 5px black;
box-shadow: 0 0 5px black;
}
#-webkit-keyframes slideDown {
0%, 100% {
-webkit-transform: translateY(0px);
}
10%,
90% {
-webkit-transform: translateY(510px);
}
}
#-moz-keyframes slideDown {
0%, 100% {
-moz-transform: translateY(0px);
}
10%,
90% {
-moz-transform: translateY(510px);
}
}
.cssanimations.csstransforms #notificationBarBottom {
-webkit-transform: translateY(510px);
-webkit-animation: slideDown 2.5s 1.0s 1 ease forwards;
-moz-transform: translateY(510px);
-moz-animation: slideDown 2.5s 1.0s 1 ease forwards;
}
<div id="notificationBarBottom">Hello, human!</div>
Jsfiddle demo here: https://jsfiddle.net/cnfk36jd/ (but unfortunately the problem is not visible there). Here's the page where you can see the "flickering" http://www.whycall.me/bannerTest.html
I tried the advice here: https://css-tricks.com/pop-from-top-notification/ and tweaked the translateY values quite a bit, but it doesn't help, not sure what else to do.
Thank you for your help
The elements initial position is visible, and then, during page load, the first translate kicks in to hide it, hence it flickers.
Do like this, push it out of view using transform: translateY(calc(100% + 10px)); and then slide it up with transform: translateY(0));.
I used 100% instead of -510px to make it height independent, and added 10px to make up for the top shadow.
I also temporary removed the prefixed properties, so you need to add them back
Updated 2021: Updated answer, improved the code suggestion, and, based on a comment, added how to make it hide using a click
document.getElementById('notificationBarBottom').addEventListener('click', function() {
this.classList.add('hideMe');
})
#notificationBarBottom {
position: fixed;
z-index: 101;
bottom: 0;
transform: translateY(calc(100% + 10px));
left: 0;
right: 0;
background: #5cb85c;
color: #ffffff;
text-align: center;
line-height: 2.5;
overflow: hidden;
box-shadow: 0 0 5px black;
}
#keyframes slideUp {
0% { transform: translateY(100% + 10px); }
100% { transform: translateY(0); }
}
#notificationBarBottom {
animation: slideUp 2.5s ease forwards;
}
#close {
display: none;
}
/* added to show how to hide with a click */
#keyframes slideDown {
0% { transform: translateY(0); }
100% { transform: translateY(100% + 10px); }
}
#notificationBarBottom.hideMe {
animation: slideDown 2.5s ease forwards;
}
<div id="notificationBarBottom">Hello, human!</div>
I think this is happening because the css file in which you've written this code is called a bit too late.
In your html page, try calling this css file earlier than other css or JS files