I am trying to chain together 2 transforms, but I want the 2nd one to begin after the 1st one ends.
This is how I am trying to do it:
.trailingNote {
position:absolute;
bottom:0px;
background:black;
width:20px;
transition: transform 5s;
transition-timing-function: linear;
}
.trailingNote-moveUp{
transform: scaleY(10) translateY(-200px);
}
Basically, I want the element to be scaled by 10 on the y axis, then, after scaleY ends, start translateY(-200px) to move the scaled element up.
Link to CodePen: https://codepen.io/Sederfo/pen/abqOoOP
Use CSS keyframes
function startAnimation() {
var element = document.getElementById("x");
element.classList.add("trailingNote-moveUp");
}
.trailingNote {
position:absolute;
bottom:0px;
background:black;
width: 20px;
}
.trailingNote:hover, .trailingNote-moveUp {
animation: animate 5s linear forwards;
}
#keyframes animate {
0% {
transform: none;
}
50% {
transform: scaleY(10);
}
100% {
transform: scaleY(10) translateY(-200px);
}
}
<div id="x" class="trailingNote">Note</div>
<button onclick="startAnimation()">Animate</button>
You can use something like this.
const box = document.getElementById('box');
const button = document.querySelector('button');
button.addEventListener('click', () =>{
box.classList.add('transform-1');
box.addEventListener('transitionend', () =>{
setTimeout(function (){
box.classList.add('transform-2');
},1000)
})
});
*,
*::before,
*::after {
box-sizing: border-box;
}
body {
min-height: 100vh;
margin: 0;
background-color: bisque;
display: grid;
place-content: center;
}
button{
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%,-50%);
}
.box{
width: 5rem;
height: 5rem;
background-color: brown;
transition: all 1s linear;
}
.transform-1{
transform: scaleY(5);
}
.transform-2{
transform: translateY(-2000px);
}
<div class="box" id="box"></div>
<button>Click me</button>
Related
I'm sure this must have been asked before and I've found related questions but I can't quite seem to crack this.
I have an element which receives a class and, on doing so, expands. Later, when that class is removed, it should revert (animate) back to its original width.
let el = document.querySelector('#side-bar');
el.addEventListener('click', evt => el.classList.toggle('contracted'));
#side-bar {
height: 100%;
width: 75px;
background: red;
position: fixed;
left: 0;
top: 0;
}
#side-bar.contracted {
animation: .5s side-bar-contract forwards;
}
#side-bar:not(.contracted) {
animation: .5s side-bar-expand forwards;
}
#keyframes side-bar-expand {
to {
width: 350px;
}
}
#keyframes side-bar-contract {
to {
width: 75px;
}
}
<div id='side-bar' class='contracted'></div>
The expansion animation works fine. But the reversion animation doesn't happen; it just snaps back to its original properties, no anim.
Fiddle
What am I doing wrong?
[ EDIT ]
OK I should obviously have mentioned why I'm not doing this with transition. This is part of a wider set of dependent animations which run in a sequence, one after another. My understanding is that this sort of chronologically non-trivial situation is better for animation rather than transition.
UPDATE: (Removing the animation at the beginning)
let init = 0,
el = document.querySelector('#side-bar');
el.addEventListener('click', function() {
if (init < 1) {
init++;
el.classList.remove("init");
el.classList.add('contracted');
}
el.classList.toggle('contracted');
});
#side-bar {
height: 100%;
width: 75px;
background: #d4653c;
position: fixed;
left: 0;
top: 0;
padding: .8rem;
}
#side-bar:not(.init) {
animation: .5s side-bar-expand forwards;
}
#side-bar.contracted {
animation: .5s side-bar-contract forwards;
}
#keyframes side-bar-expand {
to {
width: 350px;
}
}
#keyframes side-bar-contract {
from {
width: 350px;
}
}
<div id='side-bar' class='init'>Click me</div>
Just change to to from in side-bar-contract
#keyframes side-bar-expand { to { width: 350px; } }
#keyframes side-bar-contract { from { width: 350px; } }
let el = document.querySelector('#side-bar');
el.addEventListener('click', evt => el.classList.toggle('contracted'));
#side-bar {
height: 100%;
width: 75px;
background: #d4653c;
position: fixed;
left: 0;
top: 0;
padding: .8rem;
}
#side-bar:not(.contracted) {
animation: .5s side-bar-expand forwards;
}
#side-bar.contracted {
animation: .5s side-bar-contract forwards;
}
#keyframes side-bar-expand {
to {
width: 350px;
}
}
#keyframes side-bar-contract {
from {
width: 350px;
}
}
<div id='side-bar' class='contracted'>Click me</div>
Why not just use a transition animation:
let el = document.querySelector('#side-bar');
el.addEventListener('click', evt => el.classList.toggle('contracted'));
#side-bar {
height: 100%;
width: 350px; /* have width at 350px when not contracted */
background: #d4653c;
position: fixed;
left: 0;
top: 0;
padding: .8rem;
transition: width .5s; /* animate the width */
}
#side-bar.contracted {
width: 75px;
}
<div id='side-bar' class='contracted'>Click me</div>
If you need to use keyframes then you need to start the second one off at 350px - you start it at 75 to 75 which is why it doesn't animate:
let el = document.querySelector('#side-bar');
el.addEventListener('click', evt => el.classList.toggle('contracted'));
#side-bar {
height: 100%;
width: 75px;
background: #d4653c;
position: fixed;
left: 0;
top: 0;
padding: .8rem;
}
#side-bar:not(.contracted) {
animation: .5s side-bar-expand forwards;
}
#side-bar.contracted {
animation: .5s side-bar-contract forwards;
}
#keyframes side-bar-expand {
to {
width: 350px;
}
}
#keyframes side-bar-contract {
0% {
width: 350px;
}
100% {
width: 75px;
}
}
<div id='side-bar' class='contracted'>Click me</div>
First, I would recommend you do this with hover styles and css transition instead of an animation for something as simple as animating a single property.
.class {
width: 400px;
transition: width 1500ms ease-in-out;
}
.class:hover {
width: 100px;
}
CSS transition will actually stop part way through the transition and reverse to the initial size for you.
Second, I would recommend that you do not animate or transition the width property in CSS. Here's a great article about what properties you should avoid animating.
If you need to delay a transition from happening on other elements, you can use the transition-delay property. This property can also be applied in hover effects... including with hover effects on parent elements. So you may potentially have multiple hover effects in play at a given time to accomplish your desired effect.
I have a page in my website where I display to panels, side by side. I'm displaying these 2 panels in 2 views: Horizontal and Vertical. I have a button that switches between these 2 views. I'm trying to add some CSS animation on the transition between the two views. However my animation work only in one direction (from Vertical to Horizontal), the reverse animation appear in the wrong order.
var isVertical = false;
var boxes = $(".box");
function toggleViews()
{
isVertical = !isVertical;
if (isVertical)
{
boxes.addClass("vertical-box");
}
else
{
boxes.removeClass("vertical-box");
}
}
.container
{
display: block;
width: 400px;
height: 150px;
border: 2px solid black;
overflow: hidden;
}
.box
{
-webkit-transition-property: width, height;
-webkit-transition-duration: 2s, 2s;
-webkit-transition-delay: 0s, 2s;
-webkit-transition-timing-function: ease;
display: inline-block;
width: 50%;
height: 100%;
}
.vertical-box
{
width: 100%;
height: 50%;
}
.a { background-color: darkred; }
.b { background-color: darkorchid; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<html>
<body>
<button onclick="toggleViews()">toggle</button>
<div class="container">
<div class="a box">A</div><div class="b box">B</div>
</div>
</body>
</html>
var isVertical = false;
var boxes = $(".box");
function toggleViews()
{
isVertical = !isVertical;
if (isVertical)
{
boxes.addClass("vertical-box");
}
else
{
boxes.removeClass("vertical-box");
}
}
.container
{
display: block;
width: 400px;
height: 150px;
border: 2px solid black;
overflow: hidden;
}
.box
{
-webkit-transition-property: height, width; /* swapped */
-webkit-transition-duration: 0.5s, 0.5s;
-webkit-transition-delay: 0s, 0.5s;
-webkit-transition-timing-function: ease;
display: block; /* TRY THIS */
float: left; /* AND THIS */
width: 50%;
height: 100%;
}
.vertical-box
{
-webkit-transition-property: width, height; /* added */
width: 100%;
height: 50%;
}
.a { background-color: darkred; }
.b { background-color: darkorchid; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<html>
<body>
<button onclick="toggleViews()">toggle</button>
<div class="container">
<div class="a box">A</div><div class="b box">B</div>
</div>
</body>
</html>
Explained
Added transition-property: width, height; to .vertical-box
Desired behavior: expand width, shink height; expand height shrink width.
.box has transition-property first height then width
.vertical-box overwrites and flippes transition-property: first width, then height
You might think this is the wrong order, but as soon as you click the class is applied immideately, but the transition takes time. So you transition from .box to .vertical-box with the transition-property of .vertical-box and vise versa.
EDIT Answer using animation (little hacky, since i found no way to reset current keyframe)
var isVertical = false;
var boxes = $(".box");
function toggleViews()
{
isVertical = !isVertical;
if (isVertical)
{
boxes.removeClass("vertical-box-reverse");
window.requestAnimationFrame(function() { // apply to forget animation state
window.requestAnimationFrame(function() { // re-apply animation
boxes.addClass("vertical-box");
});
});
}
else
{
boxes.removeClass("vertical-box");
window.requestAnimationFrame(function() { // apply to forget animation state
boxes.addClass("vertical-box-before-reverse"); // apply to set animation end-like state
window.requestAnimationFrame(function() { // re-apply animation
boxes.removeClass("vertical-box-before-reverse");
boxes.addClass("vertical-box-reverse");
});
});
}
}
.container
{
display: block;
width: 400px;
height: 150px;
border: 2px solid black;
overflow: hidden;
}
.box
{
display: block;
float: left;
width: 50%;
height: 100%;
}
.a.vertical-box { animation: boxAnimationA 1s normal forwards; }
.b.vertical-box { animation: boxAnimationB 1s normal forwards; }
.a.vertical-box-reverse { animation: boxAnimationA 1s ease-in reverse forwards; }
.b.vertical-box-reverse { animation: boxAnimationB 1s ease-in reverse forwards; }
.vertical-box-before-reverse { width: 100%; height: 50%; }
.a { background-color: darkred; }
.b { background-color: darkorchid; }
/* Keyframes */
#keyframes boxAnimationA {
0% { width: 50%; }
50% { width: 100%; height: 100%; }
100% { width: 100%; height: 50%; }
}
#keyframes boxAnimationB {
0% { width: 50%; }
50% { width: 0%; height: 100%; }
51% { width: 100%; height: 100%; }
100% { width: 100%; height: 50%; }
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<html>
<body>
<button onclick="toggleViews()">toggle</button>
<div class="container">
<div class="a box">A</div><div class="b box">B</div>
</div>
</body>
</html>
Firefox has a nice behavior when turning off animation in a transition enabled element, it takes the element wherever it is and transition back to original form.
In Chrome it just jumps without transitioning.
Why the inconsistency? Is there any way to replicate in Chrome without using too much JS?
.wrapper {
width: 400px;
height: 200px;
}
.move {
width: 100px;
height: 100px;
position: absolute;
background-color: #f66;
transition: 1s;
cursor: pointer;
}
.move {
animation: move 2s linear infinite;
}
.wrapper:hover .move {
animation: none;
}
#keyframes move {
50% {
transform: translateX(200px);
}
}
<div class="wrapper">
<div class="move"></div>
</div>
$(".spinny").bind("webkitAnimationEnd mozAnimationEnd animationEnd", function(){
$(this).removeClass("spin")
})
$(".spinny").hover(function(){
$(this).addClass("spin");
})
div {
width: 100px;
height: 100px;
background-color: #f66;
transition: 1s;
cursor: pointer;
}
.spin {
animation: spin 1s linear 1;
}
#keyframes spin {
100% {
transform: rotate(180deg);
}
}
<script src="https://code.jquery.com/jquery-3.1.0.js"></script><div class="spinny"></div>
Taken from this answer, JS can be used to add and remove classes on hover and animation finish, respectively. jQuery is used in this example, but it is not necessary for the functionality.
EDIT: Now without jQuery, this will play the animation whenever hovered over by remembering the state and checking after the end of every animation.
hover = 0;
s = document.getElementById("spinny");
s.addEventListener("animationend", function(){
s.className = "";
if (hover)
setTimeout(function(){s.className = "spin"},0);
})
s.onmouseover = function(){
s.className = "spin";
hover = 1;
}
s.onmouseout = function(){
hover = 0;
}
div {
width: 100px;
height: 100px;
background-color: #f66;
transition: 1s;
cursor: pointer;
}
.spin {
animation: spin 1s linear 1;
}
#keyframes spin {
100% {
transform: rotate(180deg);
}
}
<div id="spinny"></div>
Add transition along side your transform
.rotate {
width: 200px;
height: 200px;
transform: rotate(0deg);
transition: 0.5s ease all;
background-color: #222;
}
.rotate:hover {
transform: rotate(20deg);
transition: 0.5s ease all;
}
This should prevent it from jumping.
I'm building something that is using the css slide animation to have some text slide in when the text display is set to "block", but I was wondering how I would go about doing the reverse (sliding it out) when it is set to "none"? Is it possible to slide in and slide out with CSS animations, or would I have to use Javascript?
Hope that makes sense! Let me know if you have any questions!
JSfiddle of give you a better idea https://jsfiddle.net/qjwqL236/
Thanks!
And code below:
document.getElementById("in-button").onclick = function(){
document.getElementById("text-container").style.display = "block";
}
document.getElementById("out-button").onclick = function(){
document.getElementById("text-container").style.display = "none";
}
#text-container{
height: 30px;
width:300px;
color: white;
background-color: blue;
float:left;
display:none;
position: relative;
left: -300px;
animation: slide 0.5sforwards;
-webkit-animation: slide 0.5s forwards;
}
#-webkit-keyframes slide {
100% {
left: 0;
}
}
#keyframes slide {
100% {
left: 0;
}
}
#in-button{
float:left;
}
#out-button{
float:left;
}
<button id="in-button">
Make Div slide in
</button>
<button id="out-button">
Make Div slide out?
</button>
<br>
<br>
<div id="text-container">
This is some text in a div.
</div>
In your case. It's easier to use transition than animation. This is how it works.
Everytime the button is click. It changes the value of the left property.
<button id="in-button">
Make Div slide in
</button>
<button id="out-button">
Make Div slide out?
</button>
<br>
<br>
<div id="text-container">
This is some text in a div.
</div>
CSS
#text-container{
height: 30px;
width:300px;
color: white;
background-color: blue;
float:left;
position: relative;
left: -400px;
transition: all 0.3s linear
}
#in-button, #out-button{
float:left;
}
JS
var tC = document.getElementById('text-container');
document.getElementById("in-button").onclick = function(){
tC.style.left = '0';
}
document.getElementById("out-button").onclick = function(){
tC.style.left = '-400px';
}
Working fiddle: https://jsfiddle.net/qjwqL236/1/
You should use css translate because it's more performant than positioning and then you need an animation in and an animation out that you can trigger with a class change instead of the display property.
document.getElementById("in-button").onclick = function(){
document.getElementById("text-container").className = "in";
}
document.getElementById("out-button").onclick = function(){
document.getElementById("text-container").className = "out";
}
#text-container{
height: 30px;
width: 300px;
color: white;
background-color: blue;
transform: translateX(-310px);
}
#text-container.in {
animation: in 0.5s both;
-webkit-animation: in 0.5s both;
}
#text-container.out {
animation: out 0.5s both;
-webkit-animation: out 0.5s both;
}
#-webkit-keyframes in {
100% {
transform: translateX(0);
}
}
#keyframes in {
100% {
transform: translateX(0);
}
}
#-webkit-keyframes out {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-310px);
}
}
#keyframes out {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-310px);
}
}
#in-button{
float:left;
}
#out-button{
float:left;
}
<button id="in-button">
Make Div slide in
</button>
<button id="out-button">
Make Div slide out?
</button>
<br>
<br>
<div id="text-container">
This is some text in a div.
</div>
Here is what you can do using animations. You need to create a slide-out animation just like you have a slide-in.
Note that it won't slide in/out automatically when you change the display to none or block.
document.getElementById("in-button").onclick = function() {
document.getElementById("text-container").setAttribute('class', 'slide-in');
}
document.getElementById("out-button").onclick = function() {
document.getElementById("text-container").setAttribute('class', 'slide-out');
}
#text-container {
height: 30px;
width: 300px;
color: white;
background-color: blue;
float: left;
position: relative;
left: -300px;
}
.slide-in {
animation: slide-in 0.5s forwards;
-webkit-animation: slide-in 0.5s forwards;
}
.slide-out {
animation: slide-out 0.5s forwards;
-webkit-animation: slide-out 0.5s forwards;
}
#-webkit-keyframes slide-in {
100% {
left: 0;
}
}
#keyframes slide-in {
100% {
left: 0;
}
}
#-webkit-keyframes slide-out {
0% {
left: 0;
}
100% {
left: -300px;
}
}
#keyframes slide-out {
0% {
left: 0;
}
100% {
left: -300px;
}
}
#in-button {
float: left;
}
#out-button {
float: left;
}
<button id="in-button">
Make Div slide in
</button>
<button id="out-button">
Make Div slide out?
</button>
<br>
<br>
<div id="text-container">
This is some text in a div.
</div>
I want to add 2 transition transforms
But I want to start the second transform after the end of the first transform
the element should go to a point slowly and after that it should go to another point
transform: translate(0%, 300%), translate(15%, -136%);
You cannot do this with just a single element using transition because when you put more than one translate within the transform, the transform property on the whole is transitioned and not one by one.
With pure CSS transition using an extra wrapper element:
If you add an extra wrapper element around the actual element and put one of the transforms on the wrapper element you could achieve the effect that you are looking for. It would also produce the exact reverse effect on the hover out (hover the body and hover out in the below snippet).
.wrapper {
position: relative;
height: 100px;
width: 100px;
transition: all 1s 1s;
}
.content {
position: absolute;
height: 100%;
width: 100%;
border: 1px solid;
transition: all 1s;
}
body:hover .content {
transform: translate(15%, -136%);
transition: all 1s 1s;
}
body:hover > .wrapper {
transform: translate(0%, 300%);
transition: all 1s;
}
body {
min-height: 100vh;
}
<div class='wrapper'>
<div class='content'>Some text</div>
</div>
Transition with a bit of JS/jQuery without any extra elements:
If you add an extra wrapper element around the actual element and put one of the transforms on the wrapper element you could achieve the effect that you are looking for. It would also produce the exact reverse effect on the hover out (hover the body and hover out in the below snippet).
$(document).ready(function() {
var isHover; /* variable to track state */
$('body').hover(function() {
isHover = !isHover; /* invert the state */
$('.content').css('transform', 'translate(0%, 300%)');
}, function() {
isHover = !isHover; /* invert the state */
$('.content').css('transform', 'translate(0%, 300%)');
});
$('.content').on('transitionend', function() {
if (isHover) {
$('.content').css('transform', 'translate(0%, 300%) translate(15%, -136%)');
} else {
$('.content').css('transform', 'none');
}
});
});
.content {
height: 100px;
width: 100px;
border: 1px solid;
transition: all 1s;
}
body {
min-height: 100vh;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class='content'>Some text</div>
With animation and no extra element:
Using animations this can be done using a single element but the reverse effect is tough to achieve. We would have to write extra code for this and even then it will be complex.
.content {
position: relative;
height: 100px;
width: 100px;
border: 1px solid;
}
body:hover > .content {
animation: move 1s forwards;
}
#keyframes move {
0% {
transform: none;
}
50% {
transform: translate(0%, 300%);
}
100% {
transform: translate(0%, 300%) translate(15%, -136%);
}
}
body {
min-height: 100vh;
}
<div class='content'>Some text</div>
Animations with reverse effect:
Below is a snippet which produces the reverse effect also using CSS animations. But as you can see it is a bit complex. We can do this using a single animation also but it would become more complex.
$(document).ready(function() {
$('body').hover(function() {
$('.content').css('transform', 'none');
$('.content').removeClass('hover-out').addClass('hover-in');
}, function() {
$('.content').css('transform', 'translate(0%, 300%) translate(15%, -136%)'); /* as soon as an animation is removed, the element would snap back to original state, to avoid that we have to add final state via inline style */
$('.content').removeClass('hover-in').addClass('hover-out');
});
});
.content {
height: 100px;
width: 100px;
border: 1px solid;
}
.hover-in {
animation: hover-in 1s forwards;
}
.hover-out {
animation: hover-out 1s forwards;
}
#keyframes hover-in {
0% {
transform: none;
}
50% {
transform: translate(0%, 300%);
}
100% {
transform: translate(0%, 300%) translate(15%, -136%);
}
}
#keyframes hover-out {
0% {
transform: translate(0%, 300%) translate(15%, -136%);
}
50% {
transform: translate(0%, 300%);
}
100% {
transform: none;
}
}
body {
min-height: 100vh;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class='content'>Some text</div>