Make div follow slide transition in vue - vue.js

When using transitions and v-if, it seems that the div is created and then animation happens within that div. Is it possible to make div follow the text during the animation?
Example provided, when clicking on the button, the button jumps to side and then text slides into meet it. What I'm aiming for is the button to slide with text when button is clicked.
Example: https://codepen.io/tadhglydon/pen/WNyVQZa
<div id="app">
<div class="container"><div class="filler"></div>
<button v-on:click="SlideIn">Test</button>
<transition name="slide">
<div v-if="toggle">Status update</div>
</transition>
</div>
</div>
.container {
width: 200px;
height: auto;
display: flex;
}
.filler{
flex-grow: 1;
}
.slide-leave-active,
.slide-enter-active {
transition: 1s;
}
.slide-enter {
transform: translate(100%, 0);
}
.slide-leave {
transform: translate(-100%, 0);
}
var app = new Vue({
el: "#app",
data: {
toggle: false
},
methods: {
SlideIn: function () {
this.toggle = !this.toggle;
}
}
});

Fixed this by using CSS and letting Vue controlling the transitions.
In the HTML I have
<div class="slider" :class="toggle ? 'slided' : ''">
And then in the CSS I have:
.slider {
width: 0px;
overflow: hidden;
transition: width 900ms ease;
-moz-transition: width 900ms ease;
-ms-transition: width 900ms ease;
-o-transition: width 900ms ease;
-webkit-transition: width 900ms ease;
}
.slided {
width: 100px;
}

Related

VueJS transition-group is not working on images, how can I fade images?

I'm trying to create an image slider, which fades the images. Image 1 should fade-out at the same moment as image 2 fades in. In other words: there shouldn't be a gap between them. Right now it does nothing like fading. The code is working, as so far that when the user clicks "next", the current images disappears, 0.9s later the next image appears. There is a delay of 0.9s between them (the same amount as declared in the CSS), so somehow it recognizes the transition time. It is only not fading, after clicking the button it immediately disappears. What am I missing?
My code
<template>
<div>
<transition-group name='fade' tag='div'>
<div v-for="i in [currentIndex]" :key='i'>
<img :src="currentImg" />
</div>
</transition-group>
<a class="prev" #click="prev" href='#'>❮</a>
<a class="next" #click="next" href='#'>❯</a>
</div>
</template>
<script>
export default {
data() {
return {
images: [
'https://cdn.pixabay.com/photo/2015/12/12/15/24/amsterdam-1089646_1280.jpg',
'https://cdn.pixabay.com/photo/2016/02/17/23/03/usa-1206240_1280.jpg',
'https://cdn.pixabay.com/photo/2015/05/15/14/27/eiffel-tower-768501_1280.jpg',
'https://cdn.pixabay.com/photo/2016/12/04/19/30/berlin-cathedral-1882397_1280.jpg'
],
timer: null,
currentIndex: 0,
show: true
};
},
mounted: function () {
this.startSlide();
},
methods: {
startSlide: function () {
this.timer = setInterval(this.next, 4000);
},
next: function () {
this.currentIndex += 1;
},
prev: function () {
this.currentIndex -= 1;
},
},
computed: {
currentImg: function () {
return this.images[Math.abs(this.currentIndex) % this.images.length];
},
},
};
</script>
<style>
.fade-enter-active,
.fade-leave-active {
transition: all 0.9s ease;
overflow: hidden;
visibility: visible;
position: absolute;
width:100%;
opacity: 1;
}
.fade-enter,
.fade-leave-to {
visibility: hidden;
width:100%;
opacity: 0;
}
img {
height:600px;
width:100%
}
.prev, .next {
cursor: pointer;
position: absolute;
top: 40%;
width: auto;
padding: 16px;
color: white;
font-weight: bold;
font-size: 18px;
transition: 0.7s ease;
border-radius: 0 4px 4px 0;
text-decoration: none;
user-select: none;
}
.next {
right: 0;
}
.prev {
left: 0;
}
.prev:hover, .next:hover {
background-color: rgba(0,0,0,0.9);
}
</style>
Change .fade-enter to .fade-enter-from.
See example
The position:absolute was messing things up here... Removed this line, now it works like a charm!

How to prevent Vue.js hidden element pop-in on page load?

I'm new to Vue.js and I have a (block) element that should be initially hidden on page load. I'm coming from a pure JS mixed with JQuery background so normally I would initially set display:none on the element use JQuery's show/hide methods etc.
I have the showing and hiding working correctly with Vue but a side effect is that the element flashes on the screen briefly on page load until the Vue setup is complete and it knows to hide the element. Setting display:none breaks the show/hide presumably because the elements class prop has higher precedence. Setting opacity:0 also seems to be overriding anything Vue is doing so that breaks the show/hide too. !important on the Vue animation classes does not help either.
The embedded sandbox below might not be the best way to reproduce this, and I suppose it might be system dependent too (speed, memory etc.) but surely this must be a common enough situation with some solution that I've missed.
VUE = new Vue({
el: '#app',
data: {
showFullpageSpinner: false
}
});
setTimeout(function() {
VUE.showFullpageSpinner = true;
setTimeout(function() { VUE.showFullpageSpinner = false; }, 1500);
}, 1500);
.fullpage-spinner-underlay {
position: fixed;
width: 100%;
height: 100%;
left: 0;
top: 0;
background: rgba(0,0,0,0.65);
z-index: 9999;
}
.fullpageSpinner-enter-active, .fullpageSpinner-leave-active {
transition: opacity .25s;
}
.fullpageSpinner-enter, .fullpageSpinner-leave-to {
opacity: 0;
}
.css-spinner {
position: absolute;
display: inline-block;
}
.css-spinner:before {
content: 'Loading...';
position: absolute;
}
.css-spinner:not(:required):before {
content: '';
border-radius: 50%;
border-top: 3px solid #daac35;
border-right: 3px solid transparent;
animation: spinner .7s linear infinite;
-webkit-animation: spinner .7s linear infinite;
}
#keyframes spinner {
to {-ms-transform: rotate(360deg);}
to {transform: rotate(360deg);}
}
#-webkit-keyframes spinner {
to {-webkit-transform: rotate(360deg);}
to {transform: rotate(360deg);}
}
#-moz-keyframes spinner {
to {-moz-transform: rotate(360deg);}
to {transform: rotate(360deg);}
}
.fullpage-loading-spinner {
left: 50%;
top: 45%;
margin-left: -40px;
margin-top: -55px;
}
.fullpage-loading-spinner:BEFORE {
width: 55px;
height: 55px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<transition name="fullpageSpinner">
<div v-if="showFullpageSpinner" class="fullpage-spinner-underlay">
<div class="css-spinner fullpage-loading-spinner"></div>
</div>
</transition>
</div>
Your problem seems to be solvable with the v-cloak directive.
This directive will remain on the element until the associated Vue instance finishes compilation. Combined with CSS rules such as [v-cloak] { display: none }, this directive can be used to hide un-compiled mustache bindings until the Vue instance is ready.
Example:
[v-cloak] {
display: none;
}
<div v-if="showFullpageSpinner" class="fullpage-spinner-underlay" v-cloak>
<div class="css-spinner fullpage-loading-spinner"></div>
</div>

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.

How to use Vue JS transition "inside" another transition?

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>

Vue I want an image to follow the mouse

I am having trouble with a Vue component https://jsfiddle.net/shawnswebsites/fep1p02c/20/. I have a div in the component and when the user's mouse enters the div I want an image to be shown and I want the image to follow the mouse.
new Vue({
el: '#app',
data: {
showImage: false,
page: {
left : 0,
top: 0
},
},
methods: {
onMouseMove(e) {
console.log('page x: ' + this.page.left);
this.page.left = e.pageX;
this.page.top = e.pageY;
}
}
})
.container {
height: 400px;
width: 400px;
border: 1px solid #FFF;
background-color: grey;
margin: 40px;
}
.image {
position: absolute;
z-index: 1000;
overflow:hidden;
}
<script src="https://unpkg.com/vue"></script>
<div id="app">
<div class="container"
#mouseenter="showImage = true"
#mousemove.self="onMouseMove($event)"
#mouseleave="showImage = false">
</div>
<img v-show="showImage" class="image" src="http://via.placeholder.com/350x150" :style="{ left: page.left + 'px', top: page.top + 'px' }">
</div>
I use a #mouseenter to show the image and #mouseleave to hide the image. However, #mouseleave is still being called as I scroll over the div, which is causing the image to blink on and off. Can any help?
As said Oxcarga in the comment below you need to add pointer-events: none; to your image style:
.image {
position: absolute;
z-index: 1000;
overflow:hidden;
pointer-events: none;
}