0

I have a Vue 2 Carousel component, with Slide child components generated using v-for. Slides are displayed and hidden using v-show. I cannot get CSS transitions between Slides to work.

I tried wrapping the Slides in <transition> but mode=out-in does nothing and I end up with two Slides in the Carousel during transition, one fading in, the other out. When I wrap the Slides in a <transition-group>, CSS transitions don't show at all, Slides are just replaced.

I looked at transitioning between components, but Slides are generated based on a list of links from an API and I don't see a way of combining dynamic generation with this solution.

Here's a JSFiddle with the issue

2 Answers 2

1

If you do not intend to crossfade the images (i.e. only one image is actually on the screen at any given point of time), you can restructure your code such as only a single <Slide> component is rendered at a single time. You simply assign a unique key that is dynamically updated, e.g. your active data of the slider component.

Your template will now look like this:

<div class="carousel-wrapper" id="promotions">
    <transition name="component-fade" mode="out-in">
        <!-- Bind image url dynamically, bind key to active index -->
        <Slide class="slide" :url="imageUrl" :key="active"></Slide>
    </transition>
</div>

The imageUrl is simply a computed property that returns the image URL by index:

imageUrl() {
    return this.list[this.active].url;
}

The Slide component should be as "dumb" as we need it to: in this case, it simply receives the dynamically-bound image URL and nothing more:

<div>
    <img :src="url" class="image">
</div>

Note:

  • you should use a transition duration that is equal to, or shorter than, the time interval of your active property updates. In your original code the slides are cycled ever 3s but the transition-duration is set to 4s, leading to really janky transitions
  • the solution will be more complicated if you want a crossfade animation.

Here is a proof-of-concept example:

Vue.component('Slide', {
  template: `
	<div>
		<img :src="url" class="image">
	</div>`,
  props: ['url']
});

Vue.component('component-2', {
  template: `<div>
                    <p>component 2</p>
                 </div>`
});

new Vue({
  el: '#example',
  template: `
  <div class="carousel-wrapper" id="promotions">
		<transition name="component-fade" mode="out-in">
			<Slide class="slide" :url="imageUrl" :key="active">
			</Slide>
		</transition>
  </div>`,
  data() {
    return {
      list: [{
          url: 'https://static.pexels.com/photos/572741/pexels-photo-572741.jpeg'
        },
        {
          url: 'https://static.pexels.com/photos/575739/pexels-photo-575739.jpeg'
        },
        {
          url: 'https://static.pexels.com/photos/576832/pexels-photo-576832.jpeg'
        }
      ],
      active: 0
    }
  },
  computed: {
    numberOfSlides() {
      return this.list.length - 1
    },
    imageUrl() {
      return this.list[this.active].url;
    }
  },
  created() {
    this.rotate()
  },
  methods: {
    moveSlide(event) {
      this.active = event.srcElement.dataset.slide
      this.list.forEach(el => {
        el.active = false
      })
      this.list[event.srcElement.dataset.slide].active = true
    },
    rotate() {
      if (this.active < this.numberOfSlides) {
        this.active = this.active + 1
      } else {
        this.active = 0
      }
      let _this = this
      setTimeout(_this.rotate, 3000)
    }
  }
});
.carousel-wrapper {
  width: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  height: 100%;
}

.carousel {}

.dots {
  max-width: 100%;
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;
  position: absolute;
  bottom: 2em;
  z-index: 1;
}

.dot {
  height: 1em;
  width: 1em;
  margin: 0 1em 0 1em;
  border-radius: 50%;
  background-color: red;
}

.component-fade-enter-active,
.component-fade-leave-active {
  transition: opacity 1s ease;
}

.component-fade-enter,
.component-fade-leave-to {
  opacity: 0;
}

.slide {
  width: 100%;
  height: 650px;
}

.image {
  max-width: 100%;
  max-height: 650px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.4/vue.min.js"></script>
<div id="example">
</div>

Sign up to request clarification or add additional context in comments.

Comments

0

I think it is because you do not need to use v-show.

You should just use <transition name="slide-fade"><slides></slides></transition> and then define your transition in the CSS.

You should see this doc, it worked for me.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.