surmon-china / vue-awesome-swiper

🏆 Swiper component for @vuejs
https://github.surmon.me/vue-awesome-swiper
MIT License
12.82k stars 1.96k forks source link

duplicated slides rendered without content #313

Open WebKieth opened 6 years ago

WebKieth commented 6 years ago

Vue.js version and component version

"vue": "^2.5.16", "vue-awesome-swiper": "^3.1.3"

Reproduction Link

Steps to reproduce

I get ajax json from server and try to render array of slides with loop parameter and loopedSlides property

What is Expected?

full loop cloning includes content

What is actually happening?

duplicated slides have no content inside. Thats looks very strange, I tried to init swipers after pushing a content to the props (watch callback) - and this didnt work too, haveno idea how to fix it. Code:

<script lang='coffee'>
    Vue = require "vue/dist/vue.js"
    Swiper = require "swiper"
    mainslider = Vue.component "mainslider",
        props: ['content']

        data: ->
            return {
                frontSliderOption: {
                    parallax: true,
                    init: false
                    slideToClickedSlide: true
                    slidesPerView: 'auto'
                    loop: true
                    loopedSlides: 0
                    hashNavigation:
                        replaceState: true
                    pagination:
                        el: ".swiper-pagination",
                        type: 'fraction',
                        speed: 500
                },
                backSliderOption: {
                    slidesPerView: 1
                    effect: 'fade',
                    speed: 500,
                    init: false,
                    loop: true
                    loopedSlides: 0
                }
            }
        computed:
            backStage: ->
                return this.$refs.backStage.swiper

            frontStage: ->
                return this.$refs.frontStage.swiper

        watch:
            "content.slides": (newVal, oldVal) ->
                this.frontSliderOption.loopedSlides = newVal.length
                this.backSliderOption.loopedSlides = newVal.length
                this.backStage.init()
                this.frontStage.init()

        mounted: ->

            that = this
            front = this.frontStage
            back = this.backStage

            front.on "slideChange", ->
                back.slideTo front.activeIndex
                scrollToTop = ->
                    window.scrollTo(0, window.pageYOffset - 20)
                    if window.pageYOffset > 1
                        requestAnimationFrame(scrollToTop)
                requestAnimationFrame(scrollToTop)

    module.exports = mainslider
</script>
<template>
    <div class="slider mainframe__slider j-main-slider">
        <div class="slider__back">
            <swiper :options='backSliderOption' ref='backStage' class="swiper-container j-main-slider__back slider__container">
                <swiper-slide v-for='slide in content.slides' class="swiper-slide slider__slide">
                    <div class="slider__slide-pic" :style="{'background-image':'url('+slide.background+')'}"></div>
                </swiper-slide>
            </swiper>
        </div>
        <div class="slider__front">
            <swiper :options="frontSliderOption" ref='frontStage'>
                <swiper-slide v-for='slide in content.slides' class="swiper-slide slider__slide-front" :data-hash="slide.hash">
                    <div class="featured">
                        <div class="featured__plate column-xs">
                            <div class="featured__decorates-sm-more slider__decorates j-main-slider__decorates">
                                <div class="featured__rotate-icon slider__rotator j-main-slider__rotator"></div>
                            </div>
                            <div class="featured__pre-head j-main-slider__pre-head slider__pre-head">
                                <div>{{slide.preHead}}</div>
                            </div>
                            <div class="featured__shadow-title-sm-more slider__shadow-title j-main-slider__shadow-title">У нас заварилась чайная история</div>
                            <div class="featured__fraction-sm-more" data-swiper-parallax="-100%" data-swiper-parallax-opacity="0"><span class="index"></span><span class="delimeter">слайд из </span><span class="total"></span></div>
                            <div class="featured__title" data-swiper-parallax="-150%" data-swiper-parallax-opacity="0">{{slide.title}}</div>
                            <div class="featured__description" data-swiper-parallax="-150%" data-swiper-parallax-opacity="0">{{slide.desc}}</div><a class="featured__more-md-more" href="javascript:void(0)" data-swiper-parallax="-150%" data-swiper-parallax-opacity="0">Подробнее</a>
                            <div class="featured__crumbs-sm-more crumbs" data-swiper-parallax="-150%" data-swiper-parallax-opacity="0">
                                <div class="crumbs__title">На этой странице:</div>
                                <div class="crumbs__row" v-for='item in slide.crumbs'>
                                    <div class="crumbs__item">{{item}}</div>
                                </div>
                            </div><a class="featured__more-xs" href="javascript:void(0)">
                            <div v-if='slide.announce === false' v-html='content.moreIcon'></div>
                            <span>{{slide.moretext}}</span></a>
                        </div>
                    </div>
                </swiper-slide>
                <div class="swiper-pagination slider__fraction-xs j-main-slider__fraction" slot='pagination'></div>
            </swiper>
            <div class="j-main-slider__slide-line slider__slide-line-xs"></div>
        </div>
        <div class="mainframe__icon-line">
            <div class="mainframe__icon-wrapper">
                <div class="mainframe__icon" data-icon="skyfall-group"></div>
            </div>
        </div>
    </div>

</template>
WebKieth commented 6 years ago

It happens, cause after mount and initialization of mainslider with swipers init swiper-slides wasnt updated and rendered with new content. I fix it by setInterval into the mount. But this is a bad solution, cause i need to set event callbacks for elements inside slides info this setInterval function

interval = setInterval(
    ->
        frontSlides = document.querySelectorAll(".slider__front .swiper-slide")
        backSlides = document.querySelectorAll(".slider__back .swiper-slide")
        if frontSlides.length > 1 && backSlides.length > 1
            that.backStage.init()
            that.frontStage.init()
            frontSlides = document.querySelectorAll(".slider__front .swiper-slide")
            frontSlides.forEach (slide, index) ->
                links = slide.querySelectorAll("a.ss")
                    links.forEach (link, index) ->
                        link.addEventListener 'click', (e) ->
                            e.preventDefault()
                            that.scrollDown()
            clearInterval(interval)
    ,20)
surmon-china commented 6 years ago
  1. https://github.com/surmon-china/vue-awesome-swiper/issues/261#issuecomment-361946057
  2. use v-if="data.length" to control the swiper component (if you use loop: true)
ghost commented 5 years ago

loop functionality is duplicating only DOM nodes, meaning that your duplicated slides for the loop don't have their JS code appended as well, only the rendered result.

I spent 2 days trying to figure this out, so by using the original Swiper's methods and events, I came to a semi-infinite-loop feature:

add the following to your swiper's config

loop: true, loopedSlides: 1, on: { slideChange: function () { let lastVisibleItem = this.realIndex + this.params.slidesPerView let slidesLength = this.slides.length - 2 let lastVisibleIndex = this.realIndex + this.params.slidesPerView // if swiper reaches the end of displayed items, goToNext redirects swiper to the start if (lastVisibleItem > slidesLength) { this.slideTo(1) } // if swiper wants to go before the first item, then forward swiper to the last item if (lastVisibleIndex >= this.slides.length) { this.slideTo((slidesLength - this.params.slidesPerView) + 1) } } }

molcsa commented 2 years ago

The above mentioned fix didn't work for me (swiper@8.3.2), but found another workaround. Put this in the Swiper config object:

loop: true,
loopedSlides: 1,
on: {
  slideChange: (swiper) => { 
    const index = swiper.realIndex;
    const lastSlideIndex = swiper.slides.length - 3;
    if (index === 0 || index === lastSlideIndex) { 
      swiper.update();
      // for debugging:
      console.log('swiper updated');
    }
  }
}