Vuejs transition is not working to show a modal - javascript

I am at a loss to why my animation is not working using vue transitions. Here is my code:
<template>
<teleport to="body">
<div class="modal-overlay" v-if="loading">
<transition name="loading">
<div v-if="loading" class="dialog">
<h1>LOADING</h1>
</div>
</transition>
</div>
</teleport>
</template>
<script>
export default {
props: ["loading"],
};
</script>
<style scoped>
.dialog {
text-align: center;
border-radius: 10px;
width: 10%;
border: 2px solid #00f6f6;
background-color: #0f0f0f;
color: #efefef;
padding: 10px;
/* animation: enter 0.25s linear; */
overflow: hidden;
}
.modal-overlay {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.4);
}
.loading-enter-active {
animation: enter 0.2s ease-out;
}
.loading-leave-active {
animation: leave 0.2s ease-in;
}
#keyframes enter {
0% {
opacity: 0%;
transform: scale(0);
}
100% {
opacity: 100%;
transform: scale(1);
}
}
#keyframes leave {
0% {
transform: scale(1);
opacity: 100%;
}
100% {
transform: scale(0);
opacity: 0%;
}
}
</style>
I am passing a prop from the parent component called "loading". This serves as a modal and it serves the purpose as the modal is it does appear and disappear based on the value of "loading" but the animation is not working. Can anyone tell me why?
Thank you!

It's because you have v-if="loading" on .modal-overlay. This doesn't give it a chance to evaluate the condition and transition the element.
Remove that and it should work:
Vue.createApp({
data() {
return {
loading: false
}
},
created() {
setInterval(() => {
this.loading = !this.loading
}, 2000)
}
}).mount('#app')
.dialog {
text-align: center;
border-radius: 10px;
width: 10%;
min-width: fit-content;
border: 2px solid #00f6f6;
background-color: #0f0f0f;
color: #efefef;
padding: 10px;
/* animation: enter 0.25s linear; */
overflow: hidden;
}
.modal-overlay {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.4);
}
.loading-enter-active {
animation: enter 0.2s ease-out;
}
.loading-leave-active {
animation: enter 0.2s ease-in reverse;
}
#keyframes enter {
0% {
opacity: 0%;
transform: scale(0);
}
100% {
opacity: 100%;
transform: scale(1);
}
}
<script src="https://unpkg.com/vue#next"></script>
<div id="app">
<teleport to="body">
<div class="modal-overlay">
<transition name="loading">
<div class="dialog" v-if="loading">
<h1>LOADING</h1>
</div>
</transition>
</div>
</teleport>
</div>

Well, I was facing similar problem when i was using Modal. From your question, i understood that you have problem with the transition, that is because of the <style scoped>.
You can go through this link https://cli.vuejs.org/guide/css.html#referencing-assets for better understanding how the CSS works in Vue/cli projects.
I am not sure why you have used <div v-if="loading" class="dialog">, first remove the v-if and then try adding a class to CSS <style lang="scss" scoped>.
If this is working fine for you, then you can add v-if or anything according to your requirements.
Thank you, I hope this will help you.

Related

Trigger css class onclick with javascript

this question is newbie but i'm stuck in this thing. I hope someone can help me.
I have this code:
HTML:
<div class='zone11'>
<div class='book11'>
<div class='cover11'></div>
<div class='page11'></div>
<div class='page11'></div>
<div class='page11'></div>
<div class='page11'></div>
<div class='page11'></div>
<div class='last-page11'></div>
<div class='back-cover11'></div>
</div>
</div>
CSS:
.zone11{
height: 700px;
display: flex;
align-items: center;
justify-content: center;
perspective: 1000px;
}
.book11{
display: flex;
align-items: center;
cursor: pointer;
}
.book11:hover .cover11{
transform: rotateX(10deg) rotateY(-180deg);
}
.book11:hover .page11{
transform: rotateX(10deg) rotateY(-180deg);
z-index: 2;
}
.cover11{
z-index: 1;
transition: all 3.5s;
}
.back-cover11{
z-index: -3;
}
.cover11, .back-cover11{
height: 450px;
width: 360px;
background: #3f0035;
border-radius: 2px 20px 20px 2px;
position: absolute;
box-shadow: 1px 1px 10px gray;
transform: rotateX(10deg);
transform-origin: center left;
}
.page11{
height: 430px;
width: 350px;
background: white;
position: absolute;
border-radius: 2px 10px 10px 2px;
transform: rotateX(10deg);
transform-origin: center left;
z-index: -1;
}
.last-page11{
height: 430px;
width: 350px;
background: white;
position: absolute;
border-radius: 2px 10px 10px 2px;
transform: rotateX(10deg);
transform-origin: center left;
z-index: -2;
}
.page11:nth-child(2){
transition-duration: 3s;
}
.page11:nth-child(3){
transition-duration: 2.6s;
}
.page11:nth-child(4){
transition-duration: 2.2s;
}
.page11:nth-child(5){
transition-duration: 1.8s;
}
.page11:nth-child(6){
transition-duration: 1.4s;
}
.book11:hover .page11:nth-child(2){
transition-duration: 6s;
}
.book11:hover .page11:nth-child(3){
transition-duration: 5.6s;
}
.book11:hover .page11:nth-child(4){
transition-duration: 5.2s;
}
.book11:hover .page11:nth-child(5){
transition-duration: 4.8s;
}
.book11:hover .page11:nth-child(6){
transition-duration: 4.4s;
}
Now, this way I have an animation of a book that when you hover, it opens to one page (with 5 pages scrolling before you get there). I want to remove the hover and make sure that it opens and closes on click.
I removed the hover from the classes. For example the cover:
.cover11__open{
transform: rotateX(10deg) rotateY(-180deg);
}
And then in the javascript:
var cover11 = document.querySelector('.cover11');
var book11= document.querySelector('.book11');
book11.addEventListener( 'click', function() {
cover11.classList.toggle('cover11__open');
});
The latter thing has worked on other occasions but it doesn't work here. I also tried something else found on the internet but nothing. Thanks.
You can try adding this to your css:
.book11 .cover11__open {
transform: rotateX(10deg) rotateY(-180deg);
}
.book11__open .page11 {
transform: rotateX(10deg) rotateY(-180deg);
z-index: 2;
}
.book11__open .page11:nth-child(2) {
transition-duration: 6s;
}
.book11__open .page11:nth-child(3) {
transition-duration: 5.6s;
}
.book11__open .page11:nth-child(4) {
transition-duration: 5.2s;
}
.book11__open .page11:nth-child(5) {
transition-duration: 4.8s;
}
.book11__open .page11:nth-child(6) {
transition-duration: 4.4s;
}
and keep every hover removes/commented there is no need for it
and add this too in the javascript instead of the current onclick
book11.addEventListener( 'click', function() {
cover11.classList.toggle('cover11__open');
book11.classList.toggle('book11__open')//this is the added line.
});
basically I just added the classes .cover11__open and .book11__open to the css with the properties given to the hover. now when clicking on the cover these two classes get added/removed. and great animation btw.

Vue transition within a transition ending the transition in the wrong place

I am trying to add a transition to a child div within another 'parent' div with its own transition, however there seems to be an issue with the child transition.
What appears to be happening is the child transition is getting confused with the parent transition and ending the transition in the wrong place.
The effect I am trying to get is when the button is clicked the Child Container A (in view) slides out left and Child Container B slides into view after Child Container A from the right.
I have replicated the issue here: https://codepen.io/BONDJAMES/pen/mdegmMe
How do I slide Child Container A out left, with Child Container B sliding into view after Child Container A, from the right, after a toggle?
HTML
<div id="app">
<div class="viewBlocks">
<transition name="slide">
<div class="left" v-if="!MaxView">
<div class="subContainer">
<div class="container">
...
</div>
</div>
</div>
</transition>
<transition name="slide">
<div class="right">
<div class="subContainer">
<button #click="toggleBtn">Toggle</button>
Parent DIV
<div class="container">
<transition name="first-slide">
<div v-if="!showMiniB" class="miniContainerA">
<button #click="slideDivs">Slide A out - B In</button>
Child Container A
</div>
</transition>
<transition name="second-slide">
<div v-if="showMiniB" class="miniContainerB">
<button #click="slideDivs">Slide B out - A</button>
Child Container B
</div>
</transition>
</div>
</div>
</div>
</transition>
</div>
</div>
CSS
body {
margin: 0;
}
* {
box-sizing: border-box;
}
.viewBlocks {
display: flex;
}
.viewBlocks > * {
flex: 1 1 0%;
overflow: hidden;
}
.viewBlocks .subContainer {
margin: 10px;
background-color: white;
min-height: 200px;
padding: 1rem;
/* prevent text wrapping during transition? */
min-width: 300px;
}
.viewBlocks .left {
background-color: blue;
}
.viewBlocks .right {
background-color: red;
}
.viewBlocks .container {
background-color: white;
}
.slide-leave-active, .slide-enter-active {
transition: 1s cubic-bezier(0.5, 0, 0.3, 1);
}
.slide-leave-to, .slide-enter {
flex-grow: 0;
}
.miniContainerA{
border: green 3px solid;
text-align: center;
height: 60px
}
.miniContainerB{
border: pink 3px solid;
text-align: center;
height: 60px
}
.first-slide-enter {
opacity: 0;
transform: translatex(-100%);
transition: all 0.7s ease-out;
}
.first-slide-enter-to {
opacity: 1;
transform: translatex(0);
transition: all 0.7s ease-out;
}
.first-slide-leave-to {
opacity: 0;
transform: translatex(-100%);
transition: all 0.7s ease-out;
}
.second-slide-enter {
opacity: 0;
transform: translatex(0);
transition: all 0.7s ease-out;
}
.second-slide-enter-to {
opacity: 1;
transform: translatex(-100%);
transition: all 0.7s ease-out;
}
.second-slide-leave-to {
opacity: 0;
transform: translatex(0);
transition: all 0.7s ease-out;
}
.second-slide-leave {
transform: translatex(-100%);
}
VUE
var app = new Vue({
el: '#app',
data: () => ({
MaxView: false,
showMiniB: false
}),
methods: {
toggleBtn(){
this.MaxView = !this.MaxView
},
slideDivs(){
this.showMiniB = !this.showMiniB
}
}
})
It's just a matter of adjusting the css a bit
for miniContainerA and miniContainerB (using just div in example below) you need to anchor to the top. You can do this by positioning the parent as absolute, and the children (miniContainers) relative
.viewBlocks .container {
background-color: white;
position: relative;
top: 0;
}
.viewBlocks .container div{
background-color: yellow;
position: absolute;
top: 0;
left:0;
width:100%;
}
then adjust your transition percentage (added 100% to your translatex values)
.second-slide-enter {
opacity: 0;
transform: translatex(100%);
transition: all 0.7s ease-out;
}
.second-slide-enter-to {
opacity: 1;
transform: translatex(0%);
transition: all 0.7s ease-out;
}
.second-slide-leave-to {
opacity: 0;
transform: translatex(100%);
transition: all 0.7s ease-out;
}
.second-slide-leave {
transform: translatex(0%);
}

transition element not working in SSR Nuxt.js

I am using <transition> in a component like below with Nuxt.js
<template>
<transition name="fade">
<div class="modal-container h-screen overflow-y-auto" v-show="!!value">
<close-button #click.native="closeModal()"></close-button>
<div class="modal w-4/5 mx-auto rounded-sm bg-white p-8" ref="modal">
<slot></slot>
</div>
</div>
</transition>
</template>
<script type="text/javascript">
import vModel from '#/helpers/v-model.mixin';
export default{
mixins: [vModel],
methods:{
closeModal(){
this.updateValue(false);
}
}
}
</script>
I am really sure I added the css for <transition name="fade">
.fade-enter{
.modal{
opacity: 0;
margin-top: -10vh;
}
}
.fade-enter-active{
.modal{
animation: drop-fade-in .5s;
}
}
.fade-enter-to{
.modal{
margin-top: 0vh;
opacity: 1;
}
}
.fade-leave{
.modal{
margin-top: 0vh;
opacity: 1;
}
}
.fade-leave-active{
.modal{
animation: drop-fade-in .5s reverse;
}
}
.fade-leave-to{
.modal{
opacity: 0;
margin-top: -10vh;
}
}
#keyframes drop-fade-in{
0%{
opacity: 0;
margin-top: -10vh;
}
100%{
opacity: 1;
margin-top: 0vh;
}
}
.modal-container{
position: fixed;
top: 0px;
left: 0px;
z-index: 99;
background-color: rgba(0,0,0,0.618);
width: 100%;
padding-top: 10vh;
.modal{
box-shadow: 0 24px 38px 3px rgba(0,0,0,0.14), 0 9px 46px 8px rgba(0,0,0,0.12), 0 11px 15px -7px rgba(0,0,0,0.2);
margin-bottom: 10vh;
transition: opacity .3s ease,
margin-top .3s ease;
}
.close-button{
position:fixed;
top: 10vh;
right: 5vw;
}
}
then I used it in nuxt-page-component
<modal v-model="show_modal">
<h1>Modal Content</h1>
</modal>
it works perfectly in SPA which the js will only run in the browser.
But when I try to use the same code in a server-side-render Nuxt.js, the transition do not work, I can't even get the <transition> component in vuejs-devtools.
How can I fix it? I already Google for it but nothing helped. Thanks a lot!

Javascript Loading screen - window.onload not working

I'm trying to make a loading screen for my project. I need the javascript to remove the CSS property "Display: none" from (page) I can't figure out why my code doesn't work.
The Problem was:
window.onload is not a function and i should use something like
"window.onload = loader;" instead. Afterwards I should make it "display:inline"
Worked perfectly!
window.onload(loader)
function loader(){
document.getElementById("page").style.removeProperty("display");
}
#page{
display: none;
}
/* Loading */
#loading{
width: 100%;
height:100vh;
background-color: #428BCA;
}
.loading_container{
margin: 0;
position: absolute;
top: 50%;
left: 50%;
margin-right: -50%;
transform: translate(-50%, -50%);
}
#spin {
border: 16px solid #f3f3f3; /* Light grey */
border-top: 16px solid #3498db; /* Blue */
border-radius: 50%;
width: 120px;
height: 120px;
animation: spin 2s linear infinite;
}
#keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
#loading h1{
color: #FFF;
margin-left:10px;
}
<div id="page">
Content!
</div>
<div id="loading">
<div class="loading_container">
<div id="spin"></div>
<h1>Loading...</h1>
</div>
</div>
window.onload is not a function.
You need to assign it to the function as follows.
window.onload = loader;
I'm assuming you want the loading animation to disappear upon successfully loading. The snippet in the loader() function emulates loading by waiting 3 seconds.
I set the css attributes differently. You initialise #page to display: none, so I simply set it to the default value inline.
document.getElementById("page").style.display = "inline";
I would favor this method instead of removing the attribute entirely and letting it default. This way you can specify a particular display value of your desire. I.e. inline-block, block etc.
window.onload = loader;
function loader(){
setTimeout(function() {
document.getElementById("loading").style.visibility = "hidden";
document.getElementById("page").style.display = "inline";
}, 3000);
}
#page{
display: none;
}
/* Loading */
#loading{
width: 100%;
height:100vh;
background-color: #428BCA;
}
.loading_container{
margin: 0;
position: absolute;
top: 50%;
left: 50%;
margin-right: -50%;
transform: translate(-50%, -50%);
}
#spin {
border: 16px solid #f3f3f3; /* Light grey */
border-top: 16px solid #3498db; /* Blue */
border-radius: 50%;
width: 120px;
height: 120px;
animation: spin 2s linear infinite;
}
#keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
#loading h1{
color: #FFF;
margin-left:10px;
}
<div id="page">
Content!
</div>
<div id="loading">
<div class="loading_container">
<div id="spin"></div>
<h1>Loading...</h1>
</div>
</div>
If I understand you correctly, you want to show the content of the page with the function 'loader', correct? If so, you can't try to get rid of the display property, because it needs to be set. You want to change it from none to initial or inline.
First of all window.onload is not a function. It's a property. You can simply assign your callback to it.
removeProperty("display"); is also wrong. It should be style.display = "none"
window.onload = loader;
function loader() {
document.getElementById("page").style.display = "none";
}
loader();
#page {
display: none;
}
/* Loading */
#loading {
width: 100%;
height: 100vh;
background-color: #428BCA;
}
.loading_container {
margin: 0;
position: absolute;
top: 50%;
left: 50%;
margin-right: -50%;
transform: translate(-50%, -50%);
}
#spin {
border: 16px solid #f3f3f3;
/* Light grey */
border-top: 16px solid #3498db;
/* Blue */
border-radius: 50%;
width: 120px;
height: 120px;
animation: spin 2s linear infinite;
}
#keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
#loading h1 {
color: #FFF;
margin-left: 10px;
}
<div id="page">
Content!
</div>
<div id="loading">
<div class="loading_container">
<div id="spin"></div>
<h1>Loading...</h1>
</div>
</div>

Multiple CSS animations: How to avoid re-triggering one of them?

I am trying to build an animated menu for mobile apps similar to Pinterest's radial menu. I have managed to get the behaviour to where I want it except for one minor detail: when the menu opens, the items shoot out as I want them to, and when you hover on them, they transform as I want them to. problem is, after the cursor leaves the items, they re-trigger their original animation, instead of just returning to their previous state. I realise this is a problem to do with the class being used for the animation and I have tried a number of solutions, including deleting the class and adding a new one .onmouseover() and changing animation running state on hover/mousover. I am probably missing something simple and idiotic, but I just cannot see it. can anybody help?
The following code is just the way I had it before trying to implement solutions.
HTML:
<!--Footer-->
<div class="footer">
<!--RADIAL NAV MENU-->
<div id="navContainer">
<!--Buttons-->
<div id="workouts" class="sml smlOne">Go there</div>
<div id="home" class="sml smlTwo">Go here</div>
<div id="profile" class="sml smlThree">Go somewhere</div>
<!--Burger-->
<div class="burger-menu">
<div id="top" class="bar barTop"></div>
<div id="middle" class="bar barMid"></div>
<div id="bottom" class="bar barBot"></div>
</div>
</div>
</div>
CSS:
.footer
{
position: fixed;
bottom: 0%;
width: 100%;
height: 50px;
background-color: #d36363;
box-shadow: 0px -6px 6px #888888;
z-index: +2;
}
/* Burger menu section */
#navContainer
{
text-align: center;
font-size: 10px;
}
.burger-menu
{
position: relative;
margin: auto;
height: 100%;
width: 50px;
}
.bar
{
height: 6px;
width: 100%;
background-color: white;
}
#top
{
position: relative;
top: 5px;
}
#middle
{
position: relative;
top: 15px;
}
#bottom
{
position: relative;
top: 25px;
}
.barTop, .barMid, .barBot
{
-webkit-transition: all 0.1s ease;
-moz-transition: all 0.1s ease;
-o-transition: all 0.1s ease;
-ms-transition: all 0.1s ease;
transition: all 0.1s ease;
}
.barTopOn
{
transform: rotate(45deg) translateY(12px) translateX(11px);
}
.barMidOn
{
opacity: 0;
}
.barBotOn
{
transform: rotate(-45deg) translateY(-12px) translateX(11px);
}
/* Navigation buttons section */
#navContainer
{
position: relative;
margin: auto;
width: 50px;
}
.sml
{
border: 2px solid #58a7dd;
height: 50px;
width: 50px;
border-radius: 50%;
background-color: white;
box-shadow: 6px 6px 6px #888888;
transform: scale(0);
}
#workouts
{
position: absolute;
bottom: 10px;
left: -60px;
}
#home
{
position: absolute;
bottom: 50px;
}
#profile
{
position: absolute;
bottom: 10px;
left: 60px;
}
.smlOnOne
{
animation: pop, slideOne 0.1s ease-in;
animation-fill-mode: forwards;
}
.smlOnTwo
{
animation: pop, slideTwo 0.1s ease-in;
animation-fill-mode: forwards;
}
.smlOnThree
{
animation: pop, slideThree 0.1s ease-in;
animation-fill-mode: forwards;
}
.smlOnOne:hover
{
background-color: red;
border: none;
box-shadow: 6px 10px 18px #686868;
animation: whopL 0.2s;
animation-fill-mode: forwards;
}
.smlOnTwo:hover
{
background-color: red;
border: none;
box-shadow: 6px 10px 18px #686868;
animation: whopC 0.2s;
animation-fill-mode: forwards;
}
.smlOnThree:hover
{
background-color: red;
border: none;
box-shadow: 6px 10px 18px #686868;
animation: whopR 0.2s;
animation-fill-mode: forwards;
}
#keyframes pop
{
100%
{
transform: scale(1,1);
}
}
#keyframes slideOne
{
0%
{
bottom: -20px;
left: 0px;
}
100%
{
bottom: 10px;
left: -60px;
}
}
#keyframes slideTwo
{
0%
{
bottom: -20px;
}
100%
{
bottom: 50px;
}
}
#keyframes slideThree
{
0%
{
bottom: -20px;
left: 0px;
}
100%
{
bottom: 10px;
left: 60px;
}
}
#keyframes whopL
{
0%
{
transform: scale(1,1) translateY(0px) translateX(0px);
}
100%
{
transform: scale(1.5) translateY(-10px) translateX(-10px);
}
}
#keyframes whopC
{
0%
{
transform: scale(1,1) translateY(0px) translateX(0px);
}
100%
{
transform: scale(1.5) translateY(-10px);
}
}
#keyframes whopR
{
0%
{
transform: scale(1,1) translateY(0px) translateX(0px);
}
100%
{
transform: scale(1.5) translateY(-10px) translateX(10px);
}
}
JS/jQuery:
$(".burger-menu").click(function()
{
$(".barTop").toggleClass("barTopOn");
$(".barMid").toggleClass("barMidOn");
$(".barBot").toggleClass("barBotOn");
$(".smlOne").toggleClass("smlOnOne");
$(".smlTwo").toggleClass("smlOnTwo");
$(".smlThree").toggleClass("smlOnThree");
});
Here is a working demo:
https://codepen.io/BGGrieco/pen/NgjxXq
You have an element that is a set of #-webkit-keyframes to animate in. On hamburger-menu click, these keyframes run, and that works well.
Next, you have a second set of #-webkit-keyframes on hover, so on hover works well too.
However, the instant the mouse is away from the element, the first (intro) set of keyframes gets run again. You don't want it to run after it first runs.
Here is what I was able to accomplish:
https://codepen.io/CapySloth/pen/RgxKEb
<div id="workouts" class="sml smlOne">
<div class="test1">
Go there
</div>
</div>
Instead of stacking classes which contain keyframe animations onto the one ".sml" class, I have split the task between two elements. ".sml" now acts as a wrapper which takes care of the "hamburger-menu open" animation and "test1 a" takes care of the "whop" animation.
If you can find a way to hide/show parents of the "test1 a/test2 a/test3 a" then you will have what you want.
You can use .stop() before your .toggleClass.
$("#example").stop().toggleClass("class");

Categories

Resources