1

I'm trying to build a calendar component,and when I click next or prev I want a transition, I also want to add a transition when I slide, but I can't make vue transitions work. I only have one element(current Month) and after I click to go next or prev only thing It does is change the data. To be able to add a dynamic transform: translate, because I need to get mouse position when slide, should I create 3 elements(previous month, current month, next month)? Or can I make that work only changing data?

I tried the most simple example on vue docs and every time I click on the buttons the transition doesnt work.

 <transition name="home">
    <Calendar></Calendar>
  </transition>

.home-enter-active,
.home-leave-active {
    transition: opacity 4s;
}


.home-enter,
.home-leave-active {
    opacity: 0;
}

And this is my calendar component.

<template>
    <div id="calendar" class="calendar">
        <div>
            <div>{{ state.currentMonthName }}</div>
            <div>{{ state.currentYear }}</div>
            <div>
                <ul class="weekDays">
                    <li v-for="days in state.weekNames" :key="days">{{ days }}</li>
                </ul>
            </div>
            <div class="days">
                <span
                    @click="
                        selectDate(state.currentYear, state.currentMonth - 1, state.getLastDayOfPreviousMonth - state.startDay + day);
                        goPrev();
                    "
                    class="lastMonth"
                    v-for="day in state.startDay"
                    :key="'empty' + day"
                    >{{ state.getLastDayOfPreviousMonth - state.startDay + day }}</span
                >
                <span :class="currenDateClass(state.currentYear, state.currentMonth, day)" @click="selectDate(state.currentYear, state.currentMonth, day)" v-for="day in state.getLastDayOfMonth" :key="day">{{ day }}</span>
                <span
                    @click="
                        goNext();
                        selectDate(state.currentYear, state.currentMonth, day);
                    "
                    class="lastMonth"
                    v-for="day in 6 - state.endDay"
                    :key="'nextMonth' + day"
                    >{{ day }}</span
                >
            </div>
        </div>

        <button @click="goPrev">Prev</button>
        <button @click="goNext">Next</button>
    </div>
</template>
Joao Bairrada
  • 93
  • 1
  • 2
  • 8

1 Answers1

1

Your transition wraps the Calendar component but it never changes. Wrap only the part of the component that changes. From the docs:

Vue provides a variety of ways to apply transition effects when items are inserted, updated, or removed from the DOM

In their example, the transition wraps an element with v-if, which adds/removes items from the DOM, so the transition is applied. Move your transition inside the component around something that changes. For example, a transition on the month:

<template>
  ...
  <transition name="home">
    <div :key="state.currentMonthName">{{ state.currentMonthName }}</div>
  </transition>
  ...
</template>

Note: The transition has to wrap an element. This wouldn't work without the div or some other tag.

The key prop forces the div to rerender when the month changes so that the transition gets triggered. This is as suggested in the key documentation. Using the key, you can wrap whatever you want and it will all rerender when the key changes.

Here's a demo using your transition CSS with a simpler template:

Vue.component('Calendar', {
  template: `
    <div id="calendar" class="calendar">
        <div>
            <transition name="home">
                <div :key="indexMonth">{{ months[indexMonth] }}</div>
            </transition>
        </div>
 
        <button @click="goPrev">Prev</button>
        <button @click="goNext">Next</button>
    </div>
  `,
  data() {
    return {
      months: [ "January", "February", "March", "April", "May", "June",
               "July", "August", "September", "October", "November", "December" ],
      indexMonth: 0
    }
  },
  methods: {
    goPrev() {
        this.indexMonth = this.indexMonth === 0 ? this.months.length - 1 : this.indexMonth - 1;
    },
    goNext() {
        this.indexMonth = this.indexMonth === 11 ? 0 : this.indexMonth + 1;
    }
  }
})

/***** APP *****/
new Vue({
  el: "#app"
});
.home-enter-active,
.home-leave-active {
    transition: opacity 1s;
}

.home-enter,
.home-leave-active {
    opacity: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
    <Calendar></Calendar>
</div>
tony19
  • 125,647
  • 18
  • 229
  • 307
Dan
  • 59,490
  • 13
  • 101
  • 110
  • Thanks your solution worked. But what about dynamic transition? so lets see that I slide with my finger on phone, and I want to hold the slider at half of position, so will show half of current month and half of next month(if I slide to the right). To achieve that I need to get the position of finger etc etc.. but to be able to work do I need to render on DOM the next month, or vue detect that I make a transition and will show that for me, like the static transition as you showed in your example? – Joao Bairrada Nov 15 '20 at 12:34
  • 1
    Transitions are for animating between start and end CSS classes when inserting/removing/updating a DOM element. I think what you described with dragging is something a bit different; it's more like the user manually moving element positioning. You can tell it's not a transition because you would need to "pause" the animation to do it. – Dan Nov 15 '20 at 12:57
  • exactly, so for that I need to create a next and previous month already on DOM – Joao Bairrada Nov 15 '20 at 13:36
  • 1
    Yup, something like that. You probably wouldn't use a transition at all. – Dan Nov 15 '20 at 13:41