3

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;
}
drake035
  • 3,955
  • 41
  • 119
  • 229

2 Answers2

3

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

Igor Moraru
  • 7,089
  • 1
  • 12
  • 24
  • Works, thanks! SO doesn't let me grant bounty now, have to wait 4 hours. One question though, in my sandbox I actually (1) set transition to swipe, (2) remove item in next update cycle, (3) set transition back to fade in next update cycle. Why doesn't this work? I understand it's unnecessary to do things in so many successive update cycles, but it should still work since each step is done in chronological order of update cycles isn't it? – drake035 May 27 '21 at 12:03
  • 1
    in your code both nextTick() calls (for item removal and switching the transition) are executed synchronously, thus when they are registered they both are waiting for the same update cycle. to be successive, the next nextTick() should be called from within the previous update cycle. this is why you have to nest them. think of them as async function calls. not sure I've explained well, have to admit they are tricky to understand – Igor Moraru May 27 '21 at 13:19
  • No I kind of get you, thanks for the clarification :) – drake035 May 27 '21 at 21:06
0

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.

MT_dev
  • 163
  • 1
  • 10