How to use Vue JS transition "inside" another transition? - vue.js

Say I have a normal transition, which works perfectly fine (following vue docs). But I would like to have another transition INSIDE that one.
So for example, an element slides in, but then the text within that fades in at the same time?
I can't get the inside child transition to animate. It's not getting fired? I've tried "appear" also thinking the node is new.
There's almost no information out there on this.
<div id="demo">
<transition name="slide">
<div v-if="show">
<transition name="slide-fade">
<p>hello</p>
</transition>
</div>
</transition>
</div>
Transition effects
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
.slide-fade-enter-active {
transition: all 0.3s ease;
}
.slide-fade-leave-active {
transition: all 0.8s cubic-bezier(1, 0.5, 0.8, 1);
}
.slide-fade-enter,
.slide-fade-leave-to {
transform: translateX(10px);
opacity: 0;
}

You did not seem to add the transition CSS for the slide component. The following should work:
const vm = new Vue({
el: '#demo',
data() {
return {
show: true
}
}
})
.slide-fade-enter-active,
.slide-fade-leave-active {
transition: opacity 0.5s;
}
.slide-fade-enter,
.slide-fade-leave-to {
opacity: 0;
}
.slide-enter-active {
transition: all 0.3s ease;
}
.slide-leave-active {
transition: all 0.8s cubic-bezier(1, 0.5, 0.8, 1);
}
.slide-enter,
.slide-leave-to {
transform: translateX(10px);
opacity: 0;
}
/* Some styling to make them noticeable */
.parent {
background-color: lightgray;
padding: 2px;
}
.child {
background-color: yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="demo">
<transition name="slide">
<div v-if="show" class="parent">
<transition name="slide-fade">
<p class="child">hello</p>
</transition>
</div>
</transition>
<button #click="show = !show">Toggle</button>
</div>

Related

How do I open and close a modal programmatically using Vue.js?

I've read through innumerable posts about how to do this with jquery, but Vue.js jealously owns and manages the dom, so I will need a solution that uses standard Vue.js components or libraries.
Thus far, I've located several examples that are what I'd call 'button-event-driven' solutions, but I will need to programmatically handle open and close of the modal.
Problem / Design Requirement: When a public user attempts to interact with a tempting button or other function on my application, and they are not yet logged in, I wish to programmatically launch a modal dialogue to then ask them to log in.
Once successfully, I'll need to programmatically close the same dialogue modal. Or, of course, they can choose to cancel and continue browsing as a public user without the ability to do those functions.
Other Helpful Information: I'm using bootstrap 4.4.1
You can use a watch property. If a user is not logged in as login=false then the modal shows.
// register modal component
Vue.component("modal", {
template: "#modal-template"
});
// start app
new Vue({
el: "#app",
data: {
showModal: false,
login: null
},
created() {
this.login = false;
},
watch: {
"login": function(val) {
this.showModal = !val;
}
}
});
.modal-mask {
position: fixed;
z-index: 9998;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: table;
transition: opacity 0.3s ease;
}
.modal-wrapper {
display: table-cell;
vertical-align: middle;
}
.modal-container {
width: 300px;
margin: 0px auto;
padding: 20px 30px;
background-color: #fff;
border-radius: 2px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.33);
transition: all 0.3s ease;
font-family: Helvetica, Arial, sans-serif;
}
.modal-header h3 {
margin-top: 0;
color: #42b983;
}
.modal-body {
margin: 20px 0;
}
.modal-default-button {
float: right;
}
/*
* The following styles are auto-applied to elements with
* transition="modal" when their visibility is toggled
* by Vue.js.
*
* You can easily play with the modal transition by editing
* these styles.
*/
.modal-enter {
opacity: 0;
}
.modal-leave-active {
opacity: 0;
}
.modal-enter .modal-container,
.modal-leave-active .modal-container {
-webkit-transform: scale(1.1);
transform: scale(1.1);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<!DOCTYPE html>
<html>
<head>
<title>Modal Component</title>
<link rel="stylesheet" type="text/css" href="/style.css" />
<!-- template for the modal component -->
<script type="text/x-template" id="modal-template">
<transition name="modal">
<div class="modal-mask">
<div class="modal-wrapper">
<div class="modal-container">
<div class="modal-header">
<slot name="header">
default header
</slot>
</div>
<div class="modal-body">
<slot name="body">
default body
</slot>
</div>
<div class="modal-footer">
<slot name="footer">
default footer
<button class="modal-default-button" #click="$emit('close')">
OK
</button>
</slot>
</div>
</div>
</div>
</div>
</transition>
</script>
</head>
<body>
<!-- app -->
<div id="app">
<!-- use the modal component, pass in the prop -->
<modal v-if="showModal" #close="showModal = false">
<!--
you can use custom content here to overwrite
default content
-->
<h3 slot="header">custom header</h3>
</modal>
</div>
</body>
</html>
I was able to construct this using a project with a (relatively) recent example. Here is the component, as well as a 'Tester.vue' view that uses that component:
LoginModal.vue:
<template>
<transition name="modal-fade">
<div class="modal-backdrop">
<div
class="modal"
role="dialog"
aria-labelledby="modalTitle"
aria-describedby="modalDescription"
>
<header class="modal-header" id="modalTitle">
<slot name="header">
</slot>
</header>
<section class="modal-body" id="modalDescription">
<slot name="body">
Your Login Form Goes Here
<button type="button" v-on:click="validateLoginForm">
Log In
</button>
<button type="button" #click="close" aria-label="Close modal">
Cancel
</button>
</slot>
</section>
<footer class="modal-footer">
<slot name="footer"> </slot>
</footer>
</div>
</div>
</transition>
</template>
<script>
export default {
name: "loginModal",
data() {
return {
loginValidationAlerts: [],
};
},
methods: {
close() {
this.$emit("close");
},
validateLoginForm() {
//Login Form Validations go here
},
clearAllLoginValidationErrors() {
this.loginValidationAlerts = [];
},
attemptLogin() {
// Your login code
},
},
};
</script>
<style>
.modal-fade-enter,
.modal-fade-leave-active {
opacity: 0;
}
.modal-fade-enter-active,
.modal-fade-leave-active {
transition: opacity 0.5s ease;
}
.modal-backdrop {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: rgba(0, 0, 0, 0.3);
display: flex;
justify-content: center;
align-items: center;
}
.modal {
background: #ffffff;
box-shadow: 2px 2px 20px 1px;
overflow-x: auto;
display: flex;
flex-direction: column;
}
.modal-header,
.modal-footer {
padding: 15px;
display: flex;
}
.modal-header {
border-bottom: 1px solid #eeeeee;
color: #4aae9b;
justify-content: space-between;
}
.modal-footer {
border-top: 1px solid #eeeeee;
justify-content: flex-end;
}
.modal-body {
position: relative;
padding: 20px 10px;
}
.btn-close {
border: none;
font-size: 20px;
padding: 20px;
cursor: pointer;
font-weight: bold;
color: #4aae9b;
background: transparent;
}
.btn-green {
color: white;
background: #4aae9b;
border: 1px solid #4aae9b;
border-radius: 2px;
}
</style>
Tester.vue:
<template>
<div>
Test Page
<div>
<button type="button" class="btn" #click="showModal">
Open Modal!
</button>
</div>
<div>
<LoginModal v-show="isModalVisible" #close="closeModal"></LoginModal>
</div>
</div>
</template>
<script>
import LoginModal from "#/components/LoginModal.vue";
export default {
components: {
// eslint-disable-next-line vue/no-unused-components
LoginModal,
},
data() {
return {
isModalVisible: false,
};
},
methods: {
showModal() {
// Do something here to determine
// if you should show modal
this.isModalVisible = true;
},
closeModal() {
// this will catch the close event
// after you're done processing the login in the component
this.isModalVisible = false;
},
},
};
</script>
<style scoped></style>
The appearance of the view and its component is pretty rough and without much formatting, but you get the picture.
Hopefully this helps somebody else if they're looking for a straight-forward modal in Vue; I believe it has to be a very common design requirement, and this approach worked for me.
The simple solution is:
add a reference to modal component
<share-modal ref="share-modal-ref"/>
import Modal like this:
import { Modal } from 'bootstrap'
then in your method do this:
let element = this.$refs.listModal.$el
let shareModal = new Modal(element, {})
shareModal.show()
It works on Vue 3 & Boostrap 5

How to handle transitions and animations in Gridsome

Currently i have been using this code in the inline CDN version of VueJS and it works fine, but when i add this to gridsome the click event on the animation to open the slide out doesn't work.
I have looked up that animations for page enter is working but i am not sure how to handle component animations.
Are animations working in Gridsome?
<template>
<nav class="navbar fixed-top navbar-expand-lg navbar-light bg-light">
<div class="row">
<div class="col">
<button
#click="show = !show"
class="navbar-toggler"
type="button"
>
<span class="navbar-toggler-icon"></span>
</button>
</div>
<div class="col">
<a class="navbar-brand" href="/">Navbar</a>
</div>
</div>
<div>
<transition name="slidein">
<div v-if="show" class="nav-bar">
<button
#click="show = !show"
class="btn-close pl-4 p-3"
type="button"
></button>
<span class="close-btn">X</span>
<div class="d-flex flex-column"></div>
</div>
</transition>
<transition name="fadein">
<div v-if="show = show" class="bck-close" #click="show = !show"></div>
</transition>
</div>
</nav>
</template>
<script>
export default {
name: "headNav",
data: {
show: false
},
};
</script>
<style>
.nav-bar {
height: 100vh;
width: 80vw;
background: #fff;
position: absolute;
left: 0;
z-index: 10000;
overflow-x: hidden;
overflow-y: auto;
top: 0;
}
.btn-close {
background: none;
border: none;
font-size: 1em;
}
.bck-close {
height: 100vh;
width: 100vw;
background: rgba(0, 0, 0, 1);
position: fixed;
z-index: 9999;
left: 0;
top: 0;
opacity: 0.1;
cursor: pointer;
}
.slidein-enter-active {
animation: slidein 0.2s;
}
.slidein-leave-active {
animation: slidein 0.2s reverse;
}
#keyframes slidein {
from {
left: -100%;
width: 0%;
}
to {
left: 0;
width: 80vw;
}
}
.fadein-enter-active {
animation: fadein 0.2s;
}
.fadein-leave-active {
animation: fadein 0.2s reverse;
}
#keyframes fadein {
from {
opacity: 0;
}
to {
opacity: 0.1;
}
}
</style>
Got the data part wrong, animations do work!
<script>
export default {
name: "headNav",
data () {
return {
show: false
}
},
};
</script>

How to use Vue transition to expand and shrink a div

Using Vue, I have two divs that I want to toggle with a transition. I want slowly expand the div to my desired width on click, and then shrink on another click. I can get the div to expand on one click, but can't figure out how to shrink it on the second click.
Not really clear if just specifying the width of the divs is enough, or if I also have to specify the same width in the css transition classes.
This fiddle shows what I've been trying: https://jsfiddle.net/vxmh8auo/1/
JS
new Vue({
el: '#app',
data: {
showButton: true
},
methods: {
randomise () { this.n = Math.random() }
},
components:{'input-div':blah}
});
CSS
.interaction {
border: 10px solid lightgreen;
display: flex;
flex: 1 0 auto;
max-height: 225px;
transition: max-height 0.25s ease-out;
}
.default {
width: 20px;
}
.bigger{
width: 200px;
}
.expand-enter-active, .expand-leave-active {
transition-property: width;
transition-duration: 5s;
}
.expand-leave-to {
width: 200px;
}
.expand-enter{
width: 20px;
}
HTML
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div id="app">
<div class="interaction">
<button #click="showButton=!showButton">
<transition name="expand" mode="out-in">
<div v-if="showButton" v-bind:class="showButton ? 'default':'bigger'" key="small"> B </div>
<div v-else class="bigger" key="big"> Bigger </div>
<!--<input-div><</input-div>-->
</transition>
</button>
</div>
</div>
You can do something like this, add class 'default' and toggle class 'bigger':
<button #click="toggleBigger">
<transition name="expand" mode="out-in">
<div class="default" v-bind:class="{ bigger: showButton }" key="small"> B </div>
<!--<input-div><</input-div>-->
</transition>
</button>
Add method toggleBigger, its more readable this way:
methods: {
toggleBigger(){
this.showButton = !this.showButton;
}
And add transitions on your css classes directly like this:
.default {
transition: max-height 0.25s ease-out;
width: 20px;
transition-property: width;
}
.bigger{
transition: max-height 0.25s ease-out;
width: 200px;
transition-duration: 5s;
transition-property: width;
}
You can see working fiddle HERE
PS I did not removed unnecessary classes from fiddle.
This seems so hackey and containing a lot of unnecessary classes, but it works: https://jsfiddle.net/df70pk68/
Again, my use case is a button that expands to a component with an input box and then shrinks again. My solution was to immediately make the component opacity zero, then slowly shrink it. The prevents the input box from breaching the border of the shrinking div. I would love it if someone could figure out a more elegant way to do this....
HTML
<div id="app">
<div class="interaction">
<button #click="showButton=!showButton">
<transition name="fade" mode="out-in">
<div v-if="showButton" class="default" key="small"> B </div>
<input-div class="bigger" v-else><</input-div>
</transition>
</button>
</div>
</div>
CSS
.interaction {
border: 10px solid lightgreen;
display: flex;
flex: 1 0 auto;
max-height: 225px;
}
JS
const blah = Vue.component('input-div',{
template: '<div><input type="text" readonly></div>'
});
new Vue({
el: '#app',
data: {
showButton: true
},
components:{'input-div':blah}
});
.default {
width: 20px;
}
.bigger{
width: 250px;
}
.fade-leave-active {
transition: all 5s ease;
}
.fade-leave-to{
width: 300px;
}
.bigger.fade-leave-to{
width:20px;
opacity: 0
}
.bigger.fade-leave-active{
transition: opacity 0s ease;
transition: width 5s ease;
}
maybe you should forget vue's support on this, only use css3 can do that, and very simple.
first ,this is your div's code
<div id="my-div"></div>
#my-div{
transition: width 0.5s; /* this is the key code you need */
}
then, you can change the width of my-div use vue or js-dom or whatever, the magic thing will happen.

Vue transitions

Dear developers, I have a question. Below code from Vue comonent
<transition-group name="fading">
<div v-for="item, id in b_data" :id="id" :dataName="item" v-if="address == item" class="card-body transition" key="d">
<p class="text-info align-middle">{{ item }} address</p>
</div>
</transition-group>
and this is code does't work. But if I change v-if to v-show all work properly. Why?
Code with v-show
<transition-group name="fading">
<div v-for="item, id in b_data" :id="id" :dataName="item" v-if="address == item" class="card-body transition" key="d">
<p class="text-info align-middle">{{ item }} address</p>
</div>
</transition-group>
and css code:
.fading-enter,
.fading-leave-to
{
opacity: 0;
}
.fading-enter-active
{
-webkit-transition: all 1.5s ease-in-out;
-moz-transition: all 1.5s ease-in-out;
-ms-transition: all 1.5s ease-in-out;
-o-transition: all 1.5s ease-in-out;
transition: all 1.5s ease-in-out;
animation: bounce-in 1s;
}
.fading-leave-active
{
animation: bounce-in 1s reverse;
}
#keyframes bounce-in {
0% {
transform: scale(0);
}
50% {
transform: scale(1.5);
opacity: .5;
}
100% {
transform: scale(1);
opacity: .2;
}
}
What I doing wrong? Why v-if doesn't work?

Vue Accordion with transition

I'm trying to integrate the Accordion component with a body transition, but without success :( . All is working as well except the animation.
template:
<div class="accordion">
<div class="accordion-title" #click="isOpen = !isOpen" :class="{'is-open': isOpen}">
<span>{{title}}</span>
<i class="ic ic-next"></i>
</div>
<div class="accordion-body" :class="{'is-open': isOpen}">
<div class="card">
<slot name="body"></slot>
</div>
</div>
</div>
component:
props: {
title: {
type: String,
default: 'Title'
}
},
data() {
return {
isOpen: false
}
}
And styles:
.accordion-body {
font-size: 1.3rem;
padding: 0 16px;
transition: .3s cubic-bezier(.25,.8,.5,1);
&:not(.is-open) {
display: none;
height: 0;
overflow: hidden;
}
&.is-open {
height: auto;
// display: block;
padding: 16px;
}
}
.card {
height: auto;
}
I tried to use <transition> but it doesn't work with height or display properties.
Help please!
display:none will remove your content and avoid the animation, you should trick with opacity, overflow:hidden and height, but you ll be forced to do a method for that.
For example (not tested, but inspiring):
in template:
<div class="accordion" #click="switchAccordion" :class="{'is-open': isOpen}">
<div class="accordion-title">
<span>{{title}}</span>
<i class="ic ic-next"></i>
</div>
<div class="accordion-body">
<p></p>
</div>
</div>
in component (add a method):
methods: {
switchAccordion: function (event) {
let el = event.target
this.isOpen = !this.isOpen // switch data isOpen
if(this.isOpen) {
let childEl1 = el.childNodes[1]
el.style.height = childEl1.style.height
} else {
let childEl2 = el.childNodes[2]
el.style.height = childE2.style.height // or .clientHeight + "px"
}
}
}
in style:
.accordion {
transition: all .3s cubic-bezier(.25,.8,.5,1);
}
.accordion-body {
font-size: 1.3rem;
padding: 0 16px;
opacity:0
}
.is-open .accordion-body {
opacity:0
}
In this case, your transition should work as you want.
The javascript will change the height value and transition transition: all .3s cubic-bezier(.25,.8,.5,1); will do the animation