I have a modal which is a white container on top of a semi-transparent dark backdrop. When this modal is triggered I want the backdrop to fade in, and after that I want the white container to slide up from bottom of the screen.
But while the fade-in works, the slide up doesn't. What am I doing wrong?
Template:
<transition name="modal">
<div v-if="showModal" class="backdrop">
<transition name="content">
<div v-if="showModal" #click="showModal = false" class="container">
content
</div>
</transition>
</div>
</transition>
CSS animation:
.modal-enter-active {
animation: fade-in-and-slide-up 1s;
}
.content-enter-active {
animation: wait-and-fade-in 4s;
}
#keyframes wait-and-fade-in {
0% {
opacity: 0;
}
66% {
opacity: 0;
}
100% {
opacity: 1;
}
}
#keyframes fade-in-and-slide-up {
0% {
transform: translateY(100%);
}
50% {
}
100% {
transform: translateY(0);
}
}
Codesandbox
To enable the transition on initial render, use appear:
<transition name="modal">
<div v-if="showModal" class="backdrop">
<transition name="content" appear>
👆
demo
Related
I have simple transition to toggle a div text, however the transition works only when the text is being hiding, however when I click to show the text the transition doesn't work here it is my code:
<template>
<div>
<transition name="fade">
<div v-if="this.showName">My name is Simon</div>
</transition>
<button #click="showName = !showName">Toggle</button>
</div>
</template>
<script>
export default {
data(){
return {
showName: false,
}
},
name: "StatusComponent"
}
</script>
<style scoped>
.fade-enter-from{
opacity: 0;
}
.fade-enter-to{
opacity: 1;
}
.fade-enter-active{
transition: all 2s ease;
}
.fade-leave-from{
opacity: 1;
}
.fade-leave-to{
opacity: 0;
}
.fade-leave-active{
transition: all 2s ease;
}
</style>
<div>
<p v-if="showName">My name is Simon</p>
</div>
You should open a p tag under the div tag
Brief question since I'm new to Vue transitions. My question is if it possible to apply a transition to an element/component without re-rendering it. Hence, not with the use of v-if or v-show. My use case is expanding and shrinking a div component by pressing two different title bar buttons, therefore i don't want the element to be re-rendered upon transition. Thx for answers!
from the code below i want to apply the expand transition when pressing the "maximize" button and reverse the transition with the "minimize button"
<template>
<transition name="expand">
<div>
<div class="title-bar-controls" #mousedown.stop>
<Button aria-label="Minimize" #click="windowToggle('minimize')"></Button>
<Button aria-label="Maximize" #click="windowToggle('maximize')"></Button>
<Button aria-label="Close" #click="hideContainer"></Button>
</div>
</div>
</transition>
Directly binds animation CSS class to your window.
Like below demo (binds minimize or maximize).
new Vue ({
el:'#app',
data () {
return {
classes: ''
}
},
methods: {
windowToggle(name) {
this.classes = name
}
}
})
#keyframes animate-minimize {
from {width: 400px; height: 400px;}
to {width: 0px; height: 0px;}
}
#keyframes animate-maximize {
from {width: 0px; height: 0px;}
to {width: 400px; height: 400px;}
}
.minimize {
width: 0px;
height: 0px;
animation-name: animate-minimize;
animation-duration: 2s;
border:solid 1px;
}
.maximize {
width: 400px;
height: 400px;
animation-name: animate-maximize;
animation-duration: 2s;
border:solid 1px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
<div>
<div class="title-bar-controls" #mousedown.stop>
<Button aria-label="Minimize" #click="windowToggle('minimize')">-</Button>
<Button aria-label="Maximize" #click="windowToggle('maximize')">[]</Button>
<div :class="classes">
</div>
</div>
</div>
</div>
I don't know if what I want is possible in the way I want to do it. I'm using this Vue plugin for modals: https://www.npmjs.com/package/vue-js-modal
I have a component called ModalButton that is responsible for triggering the modal:
<modal-button :modal-data="{{ collect(['account' => $account]) }}" modal-name="leave-account">Leave Account</modal-button>
As you can see, I'm sending :modal-data as props.
The ModalButton component is the following:
<template>
<button #click.prevent="show" type="button" class="px-3 py-2 bg-gray-200 font-bold text-gray-700 ">
<slot></slot>
</button>
</template>
<script>
// # is an alias to /src
export default {
name: 'ModalButton',
data() {
return {
}
},
props: {
modalName: String,
modalData: Object
},
methods: {
show () {
this.$modal.show(this.modalName, this.modalData);
},
hide () {
this.$modal.hide(this.modalName);
}
}
}
</script>
You can see on the show method which comes from the plugin is sending that data to the modal
this.$modal.show(this.modalName, this.modalData);
In my blade template (i'm using laravel), I have the <modal>:
<modal name="leave-account" class="text-center">
<div class="flex items-center justify-center h-full p-6">
<div>
<div>
<div class="text-3xl font-bold mb-2">Are you sure?</div>
<p>If you leave this account, you won't be able to join back unless invited again. </p>
</div>
<footer class="flex items-center justify-end mb-24 mt-auto">
<button class="px-3 py-2 bg-gray-200 font-bold text-gray-700 mr-3">Cancel</button>
<ajax-button
class-list="px-3 py-2 bg-primary text-white font-bold"
:route=#{{SOMEHOW ACCESS MODAL DATA}}
method="delete">
Yes, leave account
</ajax-button>
</footer>
</div>
</div>
</modal>
Inside my modal, I have another component called AjaxButton which allows me to have a reusable button that handles ajax requests.
I need a way of accessing the modal data that was passed to the modal. I'd like not to have to create an additional specific modal component to handle this.
Is this possible?
Specifically what the :route prop needs to send is the Account since my route requires model binding. i.e it'll look something like /accounts/leave/53
Edit:
So I can do this:
<button #click="$modal.hide('leave-account')" class="px-3 py-2 bg-gray-200 font-bold text-gray-700 mr-3">Cancel</button>
(that's right above my <ajax-button>)
So clearly I'm able to access the $modal properties / methods. So there has to be a way to accomplish what I want
You can use a simpler modal:
<modal-button v-model="showModal">
Leave Account
</modal-button>
<modal v-model="showModal" caption="Leave Account">
{{ collect(['account' => $account]) }}
</modal>
Then modal-button will look like this:
<template>
<button #click.stop="$emit('input',true)" type="button" class="px-3 py-2 bg-gray-200 font-bold text-gray-700 ">
<slot/>
</button>
</template>
<script>
export default
{
name: 'ModalButton',
props:
{
value:
{
type: Boolean,
default: false
}
}
}
</script>
and the modal component will be:
<template>
<transition name="modal">
<div class="modal_overlay" #click="close" v-if="value">
<div class="modal_content" style="overflow: auto;" #click.stop>
<div class="modal_title">{{ caption }} <div class="modal_close" #click="close">✖</div></div>
<slot/>
</div>
</div>
</transition>
</template>
<script>
export default
{
props:
{
value:
{
type: Boolean,
default: false
},
caption:
{
type: String,
default: ''
},
created()
{
document.addEventListener('keydown', this.modal_escape, false);
},
beforeDestroy()
{
document.removeEventListener('keydown', this.modal_escape, false);
},
methods:
{
close()
{
this.$emit('input', false);
},
modal_escape(event)
{
var e = event || window.event;
if(this.value && e.keyCode == 27)
{
this.close();
// All good browsers…
if (e.preventDefault) e.preventDefault();
// …and IE
if (e.returnValue) e.returnValue = false;
return false;
}
}
}
}
</script>
<style>
.modal_overlay
{
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
z-index: 25;
text-align: initial;
background-color: rgba(0,0,0,0.75);
}
.modal_content
{
position: relative;
display: inline-block;
left: 50%;
top: 50%;
transform: translate(-50%,-50%);
background-color: white;
border: 2px solid #000;
border-radius: 5px;
max-width: 90vw;
max-height: 90vh;
}
.modal_title
{
text-align: center;
background-color: #F27935;
color: white;
line-height: 1.65;
padding-left: 6px;
}
.modal_close
{
float: right;
display: inline-block;
cursor: pointer;
padding: 0 5px 0 4px;
}
.modal_close:hover
{
color: white;
background-color: #00B4FF;
}
.modal-enter-active
{
transition: all .4s ease;
}
.modal-leave-active
{
transition: all .2s cubic-bezier(1.0, 0.5, 0.8, 1.0);
}
.modal-enter, .modal-leave-to
{
transform: translateX(10px);
opacity: 0;
}
</style>
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>
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