Can't change transition on the fly for a transition group - vue.js

In my app, clicking a modal's close button makes it disappear with a fade animation whereas swiping it down makes it disappear with a swipe animation. This is done by changing the modal's <transition name> based on event.
The same thing doesn't seem to work with a transition group. Am I doing something wrong, or is it actually not possible?
CodeSandbox
Template:
<transition-group :name="itemTransition">
<div
v-for="item in items"
:key="item.id"
v-hammer:swipe.up="() => onSwipeUp(notification.id)"
>
</div>
</transition-group>
Script:
export default {
data () {
return {
applySwipeTransition: false
}
},
computed: {
itemTransition () {
return this.applySwipeTransition ? 'swipe' : 'fade'
}
},
methods: {
onSwipeUp (id) {
this.applySwipeTransition = true
this.$nextTick(() => {
this.closeItem(id)
this.applySwipeTransition = false
})
}
}
}
CSS:
.fade-leave-active {
animation: fade-out .75s;
}
.swipe-leave-active {
animation: slide-up .25s;
}

The problem lies in the timing of component update. You are switching the transition mode back to fade in the same update cycle as when the element is closed. Thus, when the next component update is triggered (by removal of the item), the transition is already switched back to fade. At this point, you may have guessed that all that needs to be done, is to switch the transition back in the next update, triggered by removal of the item:
onSwipeUp (id) {
this.applySwipeTransition = true
this.$nextTick(() => {
this.closeItem(id)
this.$nextTick(()=>{
this.applySwipeTransition = false
})
})
}
Since there are no reasons to wait for component update to close the item, you can simplify the code a bit:
onSwipeUp (id) {
this.applySwipeTransition = true
this.closeItem(id)
this.$nextTick(() => {
this.applySwipeTransition = false
})
}
Here is your working sandbox: https://codesandbox.io/s/vue-template-forked-60lkk?file=/src/App.vue

So, I've worked around with your CSS by manually changing the name of the <transition-group to either fade or swipe to see if the there's a problem with the CSS animations.
Verdict: The fade works. swipe only transitions the list-item off the page by a click and drag, not true swipe, if that concerns you (by the way, my swipe is MacOS swipe - two-finger, no click)
Still, without changing the CodePen, the issue seems to be with your computed property where there's nothing telling the name to change dynamically even though you've bound it to a computed property - the logic for itemTransition() seems to always default to fade because the applySwipeTransition would never equal to "swipe", given that the CSS does work when you manually change name to swipe (see "Verdict)".
To see where the underlying issue was, I worked around with your itemTransition():
computed: {
itemTransition() {
return this.applySwipeTransition ? "fade" : "swipe";
},
Switching the order of the fade and swipe now makes swipe work. I hope this gives you some insight into the issue. You may need to create a custom Vue directive or event to handle the swipe / fade logic if needed.

Related

How to replace a navbar with a search div when scrolling down in VueJS3?

I am new to vuejs, I am trying to make my search component to replace my fixed-top navbar when the searchdiv hit the top of the screen while scrolling down, and when I scroll up, the navbar will only appear when the user is on top. I have seen this kind of functionaliy like google search. please see:
This is the normal view:
This is the scrolled view:
and this is mine:
Here is my code:
created() {
window.addEventListener('scroll', this.handleScroll);
},
destroyed() {
window.removeEventListener('scroll', this.handleScroll);
},
mounted() {
console.log("HomeLandingComponent mounted");
},
methods: {
handleScroll() {
let scrollY = window.scrollY
if (scrollY > this.startY) {
this.navbar_visible = false;
} else {
this.navbar_visible = true;
}
this.startY = scrollY;
}
}
what is does is just basically hide the navbar when scroll down and show navbar on scroll up. Is there a way on how to achieve it?
Simple.
Ensure that the search bar comes after the navbar and that the search bar is not nested; then on the search bar, position fixed; z-index: 1; you can use JavaScript to add a new class to give background a new colour.
Shouldn't be too hard. I'll write the code once I'm on my PC.

How to disable transition-group only on page load?

I have a transition-group that renders a div, and within that is a component with a v-for attribute that renders several items. I then have a button that adds a new item to the beginning of the array. That transition works perfectly.
The only thing I don't like, is that the entire list loads with the transition on page load, and I'd like to disable it only on page load. I've searched Stack and Google but couldn't find a way. Is there a way to do this, so that transitions still works on button click, but is disabled for page load?
<transition-group
name="item-list"
tag="div">
<item-row
v-for="item in items"
:key="item.id"
:item="item" />
</transition-group>
.item-list-enter-active,
.item-list-leave-active,
.item-list-move {
transition : 250ms cubic-bezier(0.59, 0.12, 0.34, 0.95);
transition-property: opacity, transform;
}
.item-list-enter {
opacity : 0;
transform: translateX(50px) scaleY(0.5);
}
.item-list-enter-to {
opacity : 1;
transform: translateX(0) scaleY(1);
}
.item-list-leave-active {
position: absolute;
}
.item-list-leave-to {
opacity : 0;
transform : scaleY(0);
transform-origin: center top;
}
I wish I could've found a more "Vue-y" way of handling this, however I ended up going this route. Essentially I added a class to the body and removed all transitions. Then on the created lifecycle of my component, I remove that class. This removes the transition on page load, but still keeps the transition on click of the button like I want.
You can dynamically change the name value of the transition-group. Maybe on page load have a value different from the value that has the correct class name that the CSS targets. Then in the mounted lifecycle hook you can change it back to the correct class name.
You need to bind the duration for transition-group
template:
<transition-group
:duration="duration"
name="item-list"
tag="div">
<item-row
v-for="item in items"
:key="item.id"
:item="item" />
</transition-group>
script:
data() {
return {
duration: 0,
items: [
{id: 1},
{id: 2}
]
}
},
methods: {
add() {
if(this.duration===0) this.duration = 250
this.items.push({id: 'xxx'})
}
}
In case anyone comes across this like I did.
I ended up achieving this by having a transitionsOn flag added to the data (didn't seem to matter what it was initialised to), and a computed name for the transition, i.e.
<transition-group :name="transitionName">
in computed, I then had, for a transition called 'flash'
computed: {
transitionName() {
return this.transitionsOn ? 'flash' : 'disabled';
},
},
I would then set this.transitionsOn = true when I wanted it to fire.
Took a lot of fiddling about to figure this out but it seems to work OK

Vue.js transition mode not working?

I am currently working on a slider component but it seems that my transition mode is not working. I have used:
<transition name="fade" mode="out-in">
To my understanding this should force the element going out to first complete it's entire animation before the element in is being rendered.
This is an example of my slider component:
https://www.webpackbin.com/bins/-KfMMcLvpUA6LGOFL3vM
I could fix it by overlaying images absolute but that is obviously not something that I want to do when there could be a neater solution.
The transition CSS is included in the header of the index page:
.fade-enter-active, .fade-leave-active {
transition: opacity .5s;
}
.fade-enter, .fade-leave-to {
opacity: 0;
}
I have it up the as easily usable as possible, if somebody could take a look that would be awesome.
Your understanding that one transition will complete before another starts is wrong. It might be true if you were changing out an element within a single transition tag, but it is not true when each element is wrapped in its own transition tag.
The easiest thing you could do is use setTimeout to make the assignment of the next activeSlide wait for the transition.
methods: {
prevSlide() {
const nextSlide = this.activeSlide === this.firstSlide ? this.lastSlide : this.activeSlide - 1;
this.activeSlide = null;
setTimeout(() => {
this.activeSlide = nextSlide;
}, 500);
},
nextSlide() {
const nextSlide = this.activeSlide === this.lastSlide ? this.firstSlide : this.activeSlide + 1;
this.activeSlide = null;
setTimeout(() => {
this.activeSlide = nextSlide;
}, 500);
}
}
That requires that your timeout value and your CSS transition length be kept in sync.
Somewhat more work would be to use the transition hooks to emit events when a leave transition started and ended.
<transition name="fade" mode="out-in" #leave="transitionStarted" #after-leave="transitionComplete">
You would catch them in the top-level code, (not in slider, because slot events don't propagate to the slot container)
<slide src="http://lorempixel.com/196/196" alt="" #transition-started="holdUp" #transition-complete="okGo"></slide>
In the handlers (holdUp and okGo) you would set a boolean data item that you would pass to slider as a prop. In slider, nextSlide and prevSlide would calculate what the next value of activeSlide will be, but will not set it. It would get set when the prop indicated that the transition was finished.

Action when modal closes in vuejs

I am using Modal component from vue strap. I want to know if an action/method can be run if modalshow is false. Basically I want to do a status check and then refresh the page when the modal closes.
use watch in parent component:
watch: {
'showModal': function (newVal, oldVal) {
if(newVal == false)
doSomething();
}
},
when modal show status changes this event should fire up.
Remember to use twoWay binding in modal component for showModal:
<modal large :show.sync="showModal" effect="zoom">

router navigation doesnt work after a number of clicks

I have a bug in navigation panel.
<div class="wrapper" id="mainWrapper" data-bind="router: {
transition: 'fader',
duration: 300,
fadeOnly: true,
cacheViews: router.activeItem.cacheViewDuringComposition,
alwaysAttachView: router.activeItem.alwaysAttachView }">
I quickly clicked several times on navigation panel and navigation freezes.
property isProcessing always == true in router
function dequeueInstruction()
{
if (isProcessing()) {
return;
} ...
this is issue with durandal.
set the cacheView to true then it will not freez.
hope it will work.