I want to add some animations to my website. My goal is to expand the center div while the sidebars are sliding away. So far I've only accomplished the movement of the sidebars, but I'm struggling to make the center go along with them.
I've tried to add a Transition to the center div as well, but I realized it doesn't go into a enter / leave state like the sidebars so I tried to use a watcher after that but I've been
stuck for a while.
var app = new Vue({
el: '#app',
data: {
showSidebars: true,
},
methods: {
animate() {
this.showSidebars = !this.showSidebars;
}
}
});
.container {
display: flex;
width: 600px;
height: 200px;
margin-bottom: 1em;
}
.sidebar {
flex-grow: 1;
background-color: #564D95;
}
.center {
flex-grow: 3;
background-color: #242C44;
transition: all 0.3s linear;
}
.center:hover {
flex-grow: 5;
}
/* Vue Transitions */
.expand-left-enter-active {
animation: expand-left 0.3s ease reverse;
}
.expand-left-leave-active {
animation: expand-left 0.5s ease;
}
.expand-right-enter-active {
animation: expand-right 0.3s ease reverse;
}
.expand-right-leave-active {
animation: expand-right 0.5s ease;
}
#keyframes expand-left {
0% {
opacity: 1;
transform: translate(0%);
}
100% {
opacity: 0;
transform: translate(-100%);
}
}
#keyframes expand-right {
0% {
opacity: 1;
transform: translate(0%);
}
100% {
opacity: 0;
transform: translate(100%);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div class="container">
<!-- left -->
<transition name="expand-left">
<div v-show="showSidebars" class="sidebar"></div>
</transition>
<!-- center -->
<div class="center"></div>
<!-- right -->
<transition name="expand-right">
<div v-show="showSidebars" class="sidebar"></div>
</transition>
</div>
<button #click="animate">Animate</button>
</div>
Thanks to this question I was able to find a solution. I just added a class with a computed and made the transition there.
var app = new Vue({
el: '#app',
data: {
showSidebars: true,
},
methods: {
animate() {
this.showSidebars = !this.showSidebars;
}
},
computed: {
expanded() {
return !this.showSidebars ? 'expanded' : '';
}
}
});
.container {
display: flex;
width: 600px;
height: 200px;
margin-bottom: 1em;
}
.sidebar {
flex-grow: 1;
background-color: #564D95;
}
.center {
flex-grow: 3;
background-color: #242C44;
transition: all 0.5s linear;
}
.center.expanded {
flex-grow: 25;
}
/* Vue Transitions */
.expand-left-enter-active {
animation: expand-left 0.3s ease reverse;
}
.expand-left-leave-active {
animation: expand-left 0.5s ease;
}
.expand-right-enter-active {
animation: expand-right 0.3s ease reverse;
}
.expand-right-leave-active {
animation: expand-right 0.5s ease;
}
#keyframes expand-left {
0% {
opacity: 1;
transform: translate(0%);
}
100% {
opacity: 0;
transform: translate(-100%);
}
}
#keyframes expand-right {
0% {
opacity: 1;
transform: translate(0%);
}
100% {
opacity: 0;
transform: translate(100%);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div class="container">
<!-- left -->
<transition name="expand-left">
<div v-show="showSidebars" class="sidebar"></div>
</transition>
<!-- center -->
<div class="center" :class="expanded"></div>
<!-- right -->
<transition name="expand-right">
<div v-show="showSidebars" class="sidebar"></div>
</transition>
</div>
<button #click="animate">Animate</button>
</div>
Related
I'm trying to create a draft for a mobile website interface but I'm having some strange issues with the menu. Initially when the page loads it should display a blank footer except for a plus icon. When clicked, the plus icon expands and various other icons pop up that link to different parts of the interface. However, when I load the page these icons are visible. Further, when I try to expand and shrink the menu bar for the first time they disappear. However, if I then expand it again they pop in for a moment before vanishing then expanding like I expect.
I'm not sure why the transition effect works one way and not the other. I've tried messing with the timing of the opacity and visibility properties in the keyframes but so far it's still displaying this weird behavior. I've also tried adding a class that makes the elements hidden on page load and removing it the first time they're animating but that doesn't fix the pop in on subsequent cycles. I've included the relevant sections of the code below and a codepen of everything. I've slowed down the icon animations for the moment so it's easier to see their behavior.
JS
const mobile_menu = (() => {
const expandNavigation = (navButton, navBar, buttons) => {
navButton.addEventListener('click', () => {
let buttonType = ['slow', 'fast', 'fast', 'slow'];
if (navButton.classList.contains('clicked')) {
navButton.classList.add('unclicked');
navButton.classList.remove('clicked');
navBar.classList.add('retract');
navBar.classList.remove('expand');
for (let i = 0; i < buttons.length; i++) {
showButton(buttons[i], buttonType, 'retract');
}
}
else {
navButton.classList.add('clicked');
navButton.classList.remove('unclicked');
navBar.classList.add('expand');
navBar.classList.remove('retract');
for (let i = 0; i < buttons.length; i++) {
showButton(buttons[i], buttonType, 'expand');
}
}
});
}
const showButton = (button, type, direction) => {
if (direction === 'retract') {
if (type === 'slow') {
button.classList.remove('icon_appear_slow');
button.classList.add('icon_vanish_slow');
}
else {
button.classList.remove('icon_appear_fast');
button.classList.add('icon_vanish_fast');
}
}
else {
if (type === 'slow') {
button.classList.remove('icon_vanish_slow');
button.classList.add('icon_appear_slow');
}
else {
button.classList.remove('icon_vanish_fast');
button.classList.add('icon_appear_fast');
}
}
}
return { expandNavigation };
})();
HTML
<div class="screen">
<div class="page">Content</div>
<div class="nav_menu">
<span id="nav_bar"></span>
<div class="nav_button">
<i class = "material-icons call">call</i>
</div>
<div class="nav_button">
<i class = "material-icons message">chat</i>
</div>
<div class="nav_icon">
<i id="open_close" class = "material-icons unclicked">add</i>
</div>
<div class="nav_button">
<i class = "material-icons account">person</i>
</div>
<div class="nav_button">
<i class = "material-icons settings">settings</i>
</div>
</div>
</div>
CSS
.icon_appear_slow {
animation: grow 1s ease-in-out 2s 1 normal forwards;
}
.icon_appear_fast {
animation: grow 2s ease-in-out 1s 1 normal forwards;
}
.icon_vanish_slow {
animation: shrink 1s ease-in-out 2s 1 normal forwards;
}
.icon_vanish_fast {
animation: shrink 2s ease-in-out 1s 1 normal forwards;
}
#keyframes grow {
0% {
transform: rotate(0deg) scale(0.1);
visibility: hidden;
opacity: 0;
}
1% {
visibility: visible;
}
100% {
transform: rotate(360deg) scale(1);;
opacity: 1;
}
}
#keyframes shrink {
0% {
transform: rotate(0deg) scale(1);
visibility: visible;
opacity: 1;
}
99% {
visibility: hidden;
}
100% {
transform: rotate(-360deg) scale(0.1);
opacity: 0;
}
}
Change id="nav_bar" to class="nav_bar"
i dont really know but its not saving the changes with id
on the js part:
just change const navBar = document.getElementById('nav_bar');
to
const navBar = document.querySelector('.nav_bar');
on the css part:
.retract {
height: 60px;
width: 350px;
animation: retraction 0.3s ease-in-out 2.5s 1 normal forwards;
}
.icon_vanish_fast {
opacity:1;
animation: shrink 2s ease-in-out 1s 1 normal forwards;
}
.icon_vanish_slow {
opacity:1;
animation: shrink 1s ease-in-out 2s 1 normal forwards;
}
.nav_bar {
display: table-cell;
vertical-align: middle;
height: 50px;
width: 50px;
text-align: center;
background: rgb(30, 134, 30);
border-radius: 50%;
display: inline-block;
font-weight: bold;
font-size: 1.2em;
margin: 0 auto;
position: absolute;
}
.nav_button {
opacity:0;
}
I want my drawer to animate on the way in, and on the way out and when the animation ends, to turn to display: none but when the drawer is closed, it disappears and doesn't animate out.
const Drawer = ({ closeDrawer, isDrawerOpen }) => {
const [isAnimating, setIsAnimating] = useState()
let drawerClassName
if (isDrawerOpen) {
drawerClassName = "drawer-in"
} else if (!isDrawerOpen && isAnimating) {
drawerClassName = "drawer-animating"
} else if (!isDrawerOpen && !isAnimating) {
drawerClassName = "drawer-out"
}
return (
<>
<div
className={`drawer ${drawerClassName}`}
onAnimationStart={() => setIsAnimating(true)}
onAnimationEnd={() => setIsAnimating(false)}
></div>
<div onClick={closeDrawer}></div>
</>
)
}
CSS:
.drawer {
height: 100%;
width: 60%;
background-color: #fff;
position: absolute;
right: 0;
top: 0;
opacity: 1;
z-index: 3;
transform: translateX(100%);
}
.drawer-in {
animation: 0.7s drawerIn;
transform: translateX(0);
display: block;
}
.drawer-animating {
animation: 0.7s drawerOut;
display: block;
}
.drawer-out {
animation: 0.7s drawerOut;
display: none;
}
#keyframes drawerIn {
0% {
transform: translateX(100%);
}
1% {
transform: translateX(100%);
}
100% {
transform: translateX(0);
}
}
#keyframes drawerOut {
0% {
transform: translateX(0);
}
99% {
transform: translateX(100%);
}
100% {
transform: translateX(100%);
}
}
it's because when you set the drawer-out class to the element display: none; is activated immediately and you don't see the animation.
One solution is to run a setTimeout function within your JavaScript to wait for the animation to finish, then change the elements display property to none. This will allow your 'closing animation' to complete before removing the element. See my snippet below for working example.
Basically what's happening in my example snippet is I'm triggering the closing animation by adding a class associated with the animation. Then I'm setting a time out function that waits for the animation to complete (set time out in milliseconds to match your animation time within your CSS). Once the timeout is complete, the animation class is removed and the element's data attribute is set to closed which will trigger the display none. Hope this helps.
const menu = document.querySelector('.menu');
const menuToggle = document.querySelector('.menu_toggle');
menuToggle.checked=false
menuToggle.addEventListener('change',(e)=>{
menuToggle.disabled=true
let menuState = menu.dataset.menuState
if(menuState==='closed'){
menu.dataset.menuState='opened'
setTimeout(() => {
menuToggle.disabled=false
}, 500);
}else{
menu.classList.add('animate_close')
setTimeout(() => {
menu.classList.remove('animate_close')
menu.dataset.menuState='closed'
menuToggle.disabled=false
}, 500);
}
})
body {
background-color: rgb(235, 235, 235);
}
.menu {
background-color: black;
color: white;
width: fit-content;
}
.menu {
transition: all .5s ease-in-out;
}
.menu[data-menu-state="closed"] {
background-color: red;
display: none;
}
.menu[data-menu-state="opened"] {
animation: openMenu .5s ease-in-out;
transform: translateX(100%);
background-color: green;
}
.menu.animate_close{
background-color: rgb(0, 30, 128);
animation: closeMenu .5s ease-in-out;
opacity: 0;
}
#keyframes openMenu {
0% {
transform: translateX(0);
}
100% {
transform: translateX(100%);
}
}
#keyframes closeMenu {
0% {
transform: translateX(100%);
}
100% {
transform: translateX(0);
}
}
<body>
<label for="menu_toggle">Menu Toggle</label>
<input id="menu_toggle" type="checkbox" class="menu_toggle">
<div class="menu_container">
<div class="menu" data-menu-state="closed">
<ul>
<li>Item1</li>
<li>Item2</li>
<li>Item3</li>
</ul>
</div>
</div>
<script src="main.js"></script>
</body>
I'm trying to make an splash loading with CSS/HTML/JS, but am having some problems.
The problem is when trying to make the splash screen disappear with a transition effect, but the transition effect isn't applying.
I am sure my JavaScript is work properly, as it appends the new class not-displayed to the div element.
const splash = document.querySelector('.splash');
console.log(splash);
document.addEventListener('DOMContentLoaded', (e) => {
setTimeout(() => {
splash.classList.add('not-displayed');
}, 2000);
});
.splash {
z-index: 100000;
position: fixed;
height: 100vh;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
background-color: #ffff;
}
//all of these code not working
.splash.not-displayed {
z-index: 20;
opacity: 0;
position: fixed;
height: 100vh;
width: 100%;
background-color: #f06c65;
transition: all 0.5298s ease-out;
-webkit-transition: all 0.5298s ease-out;
-moz-transition: all 0.5298s ease-out;
-o-transition: all 0.5298s ease-out;
}
#keyframes fadein {
to {
opacity: 1;
}
}
.fade-in {
opacity: 0;
animation: fadein 1s ease-in forwards;
}
<div class="splash">
<h1 class="fade-in">
hello
</h1>
</div>
You have two things going on here, a transition and an animation. First I removed a lot of unnecessary CSS code to make things clearer.
Your code is working as expected. When the page loads, the "fadein" animation is triggered by the fade-in class. The text "hello" fades in from opacity 0 to opacity 1 over the course of a second, as expected.
Meanwhile, your Javascript triggers on page load and adds the class not-displayed to the outer div after two seconds. This triggers the transition effect, which after half a second applies a red background to the div as it fades the div out, bringing it back to opacity 0.
I'm not sure what specifically you are trying to achieve here, but you have wired up a successful transition and animation effect.
const splash = document.querySelector('.splash');
console.log(splash);
document.addEventListener('DOMContentLoaded', () => {
setTimeout(() => {
splash.classList.add('not-displayed');
}, 2000);
});
.splash.not-displayed {
opacity: 0;
background-color: #f06c65;
transition: all 0.5298s ease-out;
}
.fade-in {
opacity: 0;
animation: fadein 1s ease-in forwards;
}
#keyframes fadein {
to {
opacity: 1;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="splash">
<h1 class="fade-in">
hello
</h1>
</div>
In your code
document.addEventListener('DOMContentLoaded', () => {
setTimeout(() => {
splash.classList.add('not-displayed');
}, 2000);
});
you are adding new class to remove the .splash and then add new class not-displayed
Everything is working just fine, except you have given opacity: 0 to the not-displayed class.
const splash = document.querySelector('.splash');
console.log(splash);
document.addEventListener('DOMContentLoaded', (e) => {
setTimeout(() => {
splash.classList.add('not-displayed');
}, 2000);
});
.splash {
z-index: 100000;
position: fixed;
height: 100vh;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
background-color: #ffff;
}
.not-displayed {
z-index: 20;
/* opacity: 0; */
position: fixed;
height: 100vh;
width: 100%;
background-color: #f06c65;
transition: all 0.5298s ease-out;
-webkit-transition: all 0.5298s ease-out;
-moz-transition: all 0.5298s ease-out;
-o-transition: all 0.5298s ease-out;
}
#keyframes fadein {
to {
opacity: 1;
}
}
.fade-in {
opacity: 0;
animation: fadein 1s ease-in forwards;
}
<div class="splash">
<h1 class="fade-in">
hello
</h1>
</div>
Codepen
You should set the transition to .splash not to .splash.not-displayed
I am creating a hamburger menu component in Vue but for some reason the animations do not work. It gets to what should be the end result of the animation, but there is no animation.
I trigger the animation through a class change.
I've read up some on Vue transitions but to me it seems like those are mostly used when actually removing or adding elements, which I am not doing, just changing styling in an element.
I include the whole component below, if it is of any help.
<template>
<div id="hamburgerWrapper" #click="changeClass()">
<div id="hamburger1" :class="classObject"></div>
<div id="hamburger2"></div>
<div id="hamburger3" :class="classObject"></div>
</div>
</template>
<script>
export default {
name: "hamburger",
data() {
return {
active: false
};
},
computed: {
classObject: function(){
return {
in: this.active,
out: !this.active
}
}
},
methods: {
changeClass(){
this.active = !this.active
}
}
};
</script>
<style lang="scss">
$height: 300px;
$width: $height;
#hamburgerWrapper{
margin-top: 30px;
margin-left: 30px;
width: $width;
height: $height;
background-color: rgb(192, 192, 192);
position: relative;
cursor: pointer;
#hamburger1,#hamburger2,#hamburger3{
position: absolute;
height: $height * 0.2;
width: $width;
background-color: blue;
border-radius: $height * 0.1;
}
#hamburger1{
top: 0;
//transform: translateY(($height/2)-($height * 0.1));
&.in{
animation: topIn 1s ease forwards;
}
&.out{
animation: topIn 1s ease reverse forwards;
}
}
#hamburger2{
top: 50%;
transform: translateY(-50%);
}
#hamburger3{
bottom: 0;
&.in{
animation: botIn 1s ease forwards;
}
&.out{
animation: botIn 1s ease reverse forwards;
}
}
}
#keyframes topIn {
0% {transform: translateY(0) rotate(0deg)}
50% {transform: translateY(($height/2)-($height * 0.1)) rotate(0deg)}
100% {transform: translateY(($height/2)-($height * 0.1)) rotate(45deg)}
}
#keyframes botIn {
0% {transform: translateY(0) rotate(0deg)}
50% {transform: translateY(-(($height/2)-($height * 0.1))) rotate(0deg)}
100% {transform: translateY(-(($height/2)-($height * 0.1))) rotate(-45deg)}
}
</style>
I changed the "out"-animation to not do the reversed "in"-animation, but instead just an own animation. That somehow worked.
I'm trying to make element dissapear at the end of animation but it doesn't work, can someone explain how to make exit animation with element dissappearing at the end of it?:
var test = document.getElementById("test");
test.addEventListener("click", displayOpacity);
function displayOpacity(event){
event.target.style.animation = "changeOpacity 1s linear";
if(event.target.style.opacity === 0){
event.target.style.display = "none";
}
}
.container .test {
text-align: center;
padding: 100px;
color: #fff;
background-color: #00f;
max-width: 500px;
-webkit-animation-fill-mode: forwards;
animation-fill-mode: forwards;
}
#-webkit-keyframes changeOpacity {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
#keyframes changeOpacity {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
<body>
<div class="container">
<div class="test" id="test">Custom Text</div>
</div>
<script src="main.js"></script>
</body>
Since you have tagged jQuery, wouldn't be it easier to do it with .fadeOut()? The display attribute of the element is being set to none just after the animation has ended.
$('#test').click(function(){
$(this).fadeOut();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id='test'>Text</div>
Your issue is because the animation-fill-mode is not being respected as you're overwriting it by setting the animation rule directly on the element itself.
To fix this change your JS code to add a class on the element, and put the animation rule in there, along with the required fill mode:
var test = document.getElementById("test");
test.addEventListener("click", displayOpacity);
function displayOpacity(event) {
this.classList.add('changeOpacity');
}
.container .test {
text-align: center;
padding: 100px;
color: #fff;
background-color: #00f;
max-width: 500px;
}
.changeOpacity {
animation: changeOpacity 1s linear forwards;
}
#-webkit-keyframes changeOpacity {
0% { opacity: 1; }
100% { opacity: 0; }
}
#-webkit-keyframes changeOpacity {
0% { opacity: 1; }
100% { opacity: 0; }
}
#keyframes changeOpacity {
0% { opacity: 1; }
100% { opacity: 0; }
}
<div class="container">
<div id="test" class="test">
Test
</div>
</div>
The animation time is 1 second
event.target.style.animation = "changeOpacity 1s linear";
so just make a timeout
setTimeout(function(){
event.target.style.display = "none";
},1000)