I have read vue transitions documentation and is pretty clear how to apply them to the desired elements. But how to make height variations smooth to the parent container (grey) when a new child (green) is added/removed to a flex column? I have tried to animate max-height of the new child from 0, but isn't working. Here is my case:
fiddle: link
code:
HTML
<html>
<body>
<div id="app" class="demo">
<div>
<button v-on:click="on=!on">
toggle
</button>
</div>
<div class="background">
<div class="container">
<transition name="fade-outin" mode="out-in">
<red v-if="on"></red>
<blue v-else></blue>
</transition>
<transition name="fade-down">
<div v-if="!on">
<div style="height: 25px"></div>
<green></green>
</div>
</transition>
</div>
</div>
</div>
</body>
</html>
JS
var red = {
template: "<div style='width:150px; height:150px; border:10px solid red;'></div>"
};
var blue = {
template: "<div style='width:150px; height:150px; border:10px solid blue;'></div>"
};
var green = {
template: "<div style='width:150px; height:150px; border:10px solid green;'></div>"
};
var app = new Vue({
el: '#app',
data: {
on: true
},
components: {
red,
blue,
green
}
})
CSS
body {
padding: 20px;
}
.background {
border: 1px solid #000;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
width: 100%;
height: 500px;
overflow: hidden;
}
.container {
background-color: #abc;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 20px;
}
.fade-outin-enter-active, .fade-outin-leave-active {
transition: opacity 0.5s ease-in-out;
}
.fade-outin-enter, .fade-outin-leave-to {
opacity: 0;
}
.fade-down-enter-active {
transition: all 0.5s ease-in-out 0.5s;
}
.fade-down-leave-active {
transition: all 0.5s ease-in-out;
}
.fade-down-enter, .fade-down-leave-to {
max-height: 0;
opacity: 0;
transform: translateY(-65px);
}
Thanks for your time,
H25E
You can't use transition with height: auto, the CSS need a value to apply the transitions.
In this case, you must to use the max-height for animate your component. See this article for more details: https://dev.to/sarah_chima/using-css-transitions-on-the-height-property-al0
I do some changes in your code. Try something like this, but you can improve:
<html>
<body>
<div id="app" class="demo">
<div>
<button v-on:click="toggle">
toggle
</button>
</div>
<div class="background">
<div class="container" ref="container">
<transition name="fade-outin" mode="out-in">
<red v-if="on"></red>
<blue v-else></blue>
</transition>
<transition name="fade-down">
<div v-if="!on">
<div style="height: 25px"></div>
<green></green>
</div>
</transition>
</div>
</div>
</div>
</body>
</html>
var red = {
template: "<div style='width:150px; height:150px; border:10px solid red;'></div>"
};
var blue = {
template: "<div style='width:150px; height:150px; border:10px solid blue;'></div>"
};
var green = {
template: "<div style='width:150px; height:150px; border:10px solid green;'></div>"
};
var app = new Vue({
el: '#app',
data: {
on: true
},
components: {
red,
blue,
green
},
methods: {
toggle() {
if (this.on) {
this.$refs.container.style['max-height'] = '500px';
} else {
this.$refs.container.style['max-height'] = '170px';
}
this.on = !this.on;
}
}
})
.container {
background-color: #abc;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 20px;
max-height: 170px;
transition: max-height 3s;
}
Related
I am facing an issue in swiper js. When it has only 2 slides it duplicates the slides one more time. I don't need loop and when it is 3 and less than 3 i don't need navigation too. I have tried loopDestroy() function, but when i use this it displays blank space after last slide. Here's the code.
var swiperproject = new Swiper(".project-details-swiper", {
navigation: {
nextEl: ".swiper-button-next",
prevEl: ".swiper-button-prev",
},
slidesPerView: 3,
spaceBetween: 30,
slidesPerGroup: 1,
//loop: true,
loopFillGroupWithBlank: true,
});
loop by is nature "duplicate" slides.
You can add your own logic and pass loop true/false related to this logic.
let loop_slider = true;
let num_of_slides = 4;
let num_of_slides_before_init = document.querySelectorAll(".swiper-slide").length;
if(num_of_slides_before_init < num_of_slides){
loop_slider = false;
}
Snippet example:
let loop_slider = true;
const num_of_slides = 4;
let swiper_sliders = document.querySelectorAll(".swiper");
// forEach method, could be shipped as part of an Object Literal/Module
// for class
for (const this_slider of swiper_sliders){
//work as per usual
let num_of_slides_before_init = this_slider.querySelectorAll(".swiper-slide").length;
if(num_of_slides_before_init < num_of_slides){
loop_slider = false;
}
var swiper = new Swiper(this_slider, {
slidesPerView: 3,
loop: loop_slider,
spaceBetween: 30,
pagination: {
el: ".swiper-pagination",
clickable: true,
},
});
};
html,
body {
position: relative;
height: 100%;
}
body {
background: #eee;
font-family: Helvetica Neue, Helvetica, Arial, sans-serif;
font-size: 14px;
color: #000;
margin: 0;
padding: 0;
}
.swiper {
width: 100%;
height: 100%;
}
.swiper-slide {
text-align: center;
font-size: 18px;
background: #fff;
/* Center slide text vertically */
display: -webkit-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
-webkit-justify-content: center;
justify-content: center;
-webkit-box-align: center;
-ms-flex-align: center;
-webkit-align-items: center;
align-items: center;
}
.swiper-slide img {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}
[scroll]{
min-height: 100vh;
background: gray;
}
<link href="https://cdn.jsdelivr.net/npm/swiper#8.4.5/swiper-bundle.min.css" rel="stylesheet"/>
<h1>Loop true (Dynamic)</h1>
<div class="swiper mySwiper">
<div class="swiper-wrapper">
<div class="swiper-slide">Slide 1</div>
<div class="swiper-slide">Slide 2</div>
<div class="swiper-slide">Slide 3</div>
<div class="swiper-slide">Slide 4</div>
</div>
<div class="swiper-pagination"></div>
</div>
<br><br>
<h1>Loop false (Dynamic)</h1>
<div class="swiper mySwiper">
<div class="swiper-wrapper">
<div class="swiper-slide">Slide 1</div>
</div>
<div class="swiper-pagination"></div>
</div>
<script src="https://cdn.jsdelivr.net/npm/swiper#8/swiper-bundle.min.js"></script>
Trying to implement the overlay fullscreen menu with Vue.js, html/css/js is [this solution]https://www.w3schools.com/howto/tryit.asp?filename=tryhow_js_overlay2
But stuck with how to toggle div height from 0% to 100%. I did create 2 classes,
'navbar'
'navbar_open'
but I am not sure how to toggle between them in Vue. I could see that once #click triggered, the class="navbar navbar_open"
<div id="myNav" class="navbar navbar_open">
<div class="closebtn">
<i class="fa fa-times"></i>
</div>
<div class="navbar-content">
<a>Home</a><a>Contact</a>
</div>
</div>
below is the App.vue file,
<template>
<div id="app">
<!-- Banner/Header -->
<!-- Hamburger -->
<div id="myNav" class="navbar" :class="{ navbar_open: showNavbar }">
<div class="closebtn" v-on:click="showNavbar = !showNavbar">
<i class="fa fa-times"></i>
</div>
<div class="navbar-content">
<a>Home</a>
<a>Contact</a>
</div>
</div>
<div class="openbtn">
<i class="fa fa-bars" v-on:click="showNavbar = !showNavbar"></i>
</div>
<router-view />
</div>
</template>
<script>
export default {
data() {
return {
showNavbar: false
};
},
methods: {
toggleNavbar() {
this.showNavbar = !showNavbar;
},
setHeightValue(showNavbar) {
const heightValue = this.showNavbar ? '100%' : '0%';
return heightValue;
}
},
created() {
this.setHeightValue();
}
};
</script>
<style>
#import url('https://use.fontawesome.com/releases/v5.9.0/css/all.css');
.navbar_open {
width: 100%;
height: 100%;
position: fixed;
z-index: 1;
top: 0;
left: 0;
background-color: rgb(0, 0, 0);
background-color: rgba(0, 0, 0, 0.9);
overflow-y: hidden;
transition: 0.5s;
background-color: #2b4c72;
}
.navbar {
width: 100%;
height: 0%;
position: fixed;
z-index: 1;
top: 0;
left: 0;
background-color: rgb(0, 0, 0);
background-color: rgba(0, 0, 0, 0.9);
overflow-y: hidden;
transition: 0.5s;
background-color: #2b4c72;
}
.navbar-content {
position: relative;
top: 25%;
width: 100%;
text-align: center;
margin-top: 30px;
}
.navbar a {
padding: 8px;
text-decoration: none;
font-size: 36px;
color: #818181;
display: block;
transition: 0.3s;
}
.navbar a:hover,
.navbar a:focus {
color: #f1f1f1;
}
.navbar .closebtn {
position: absolute;
top: 20px;
left: 45px;
font-size: 60px;
color: white;
cursor: pointer;
}
.openbtn {
font-size: 30px;
}
#media screen and (max-height: 450px) {
.navbar {
overflow-y: auto;
}
.navbar a {
font-size: 20px;
}
.navbar .closebtn {
font-size: 40px;
top: 15px;
right: 35px;
}
}
</style>
I see that you have unused method toggleNavbar. You call setHeightValue only when your component is created and then never again (I think you can remove this method). This menu is not 100% height probably because you embedded this component in another container (<div id="app:...) which has smaller height so height: 100% will be 100% height of parent container. Instead of 100% you can use 100vh (vh - is a viewport unit and it will always take 100% viewport height [if 50vh then 50% of total viewport height]). You should also apply only navbar_open but now you are applying bot classes if your navbar is open: navabar navbar_open so you should add this classes conditionally:
:class="{ 'navbar_open': showNavbar, 'navbar': !showNavbar }"
You have also Two different buttons responsible for two different actions:
closebtn -> close navigation
openbtn -> open navigation
So you should create two different methods openNavigation closeNavigation
<div id="app">
<!-- Banner/Header -->
<!-- Hamburger -->
<div id="myNav" :class="{ 'navbar_open': showNavbar, 'navbar': !showNavbar }">
<div class="closebtn" #click="closeNavigation">
<i class="fa fa-times"></i>
</div>
<div class="navbar-content">
<a>Home</a>
<a>Contact</a>
</div>
</div>
<div class="openbtn">
<i class="fa fa-bars" #click="showNavigation"></i>
</div>
<router-view />
</div>
</template>
<script>
export default {
data() {
return {
showNavbar: false
};
},
methods: {
openNavigation() {
this.showNavbar = true;
},
closeNaviagation() {
this.showNavbar = false;
}
}
};
</script>
<style>
...
.navbar_open {
width: 100%;
height: 100vh; // <--- 100vh instead of 100%
position: fixed;
...
</style>
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
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>
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.