2

Basic Information

Official introduction

Swiper is the free and most modern mobile touch slider with hardware accelerated transitions and amazing native behavior. It is intended to be used in mobile websites, mobile web apps, and mobile native/hybrid apps.

Documentation

Use scene-transition effect

  • Carousel

practice

Target

Realize a special-shaped carousel diagram. Support gestures to slide left and right to switch the center map.

image.png

The principle of realizing special-shaped rolling

Achieve alien effects

  • Center page (class: swiper-slide-active ) scale:1
  • Other pages (class: swiper-slide ) scale ( 0<x<1 ) to achieve the effect of the center page in the user's visual center.

Achieve the effect of leaving edges on both sides:

  • css sets the padding value of each swiper-slide (leave blank)
  • Set swiper's spaceBetween to a negative value to pull 3 swipe-slides onto one screen
  • Set slidesOffsetBefore and slidesOffsetAfter of swiper to eliminate the problem of not centering the first and last sheets

Code reference (partial)

index.vue:

    <div class="gallery-wrapper">
      <!-- Slider main container -->
      <div class="swiper-container">
          <!-- Additional required wrapper -->
          <div class="swiper-wrapper">
              <!-- Slides -->
              <div 
                class="swiper-slide" 
                v-for="(item,index) in images"
                :key="`${item.src}-index`"
              > 
                <img 
                  v-lazy="item.src" 
                  :key="item.src"
                  class="image-content"

                <v-touch 
                  tag="div"
                  @tap="onTap(index)"
                >
                <img 
                  src="./../../../assets/art-picture/scale-icon.png"
                  alt="放大"
                  class="scale-icon"
                />
                </v-touch>

                <!-- <div class="swiper-lazy-preloader">loading...</div> -->
            </div>
          </div>
      </div>
    </div>

style.less:

.swiper-container {
      width: 100%;
      height:100%;
    }
      
    .swiper-container > .swiper-wrapper{
      /** active 图片有阴影超出 */
      overflow-y:visible;
      /**
      * 针对swiper 调整offset
      * pre/next容器边框漏出:20
      * 设计稿边距:17.5
      * 17.5 + 20 = 37.5px
      */
      // margin-left:-0.375rem;
    }
  
    .swiper-slide {
      position:relative;
      box-sizing: border-box;
      padding:0.3rem 0.3rem 0.9rem 0.3rem;
      display: flex;
      align-items: center;
      justify-content: center;
      transition: 300ms;
      width:3rem;
      height:4.25rem;
      transform: scale(0.74);
      background-image:url('../../../assets/art-picture/pic-border.png');
      background-repeat:  no-repeat;
      background-position:center ;
      background-size:100% 100%;
      img[lazy="loading"] {
        width: 0.3rem;
        height:0.3rem;
      }
    }
    .swiper-slide:not(.swiper-slide-active){
      /**
       * 计算scale造成的边距误差 
       * 设计稿缩放:0.74 设计稿当前播放的容器宽度/屏幕宽度:300/375
       * scale产生的误差:300*(1-0.74)=78px
       * 标准值17.5
       * 消除误差:78/2- 17.5/2  = 30.25px;
       */
      // margin-left:-0.3025rem; 
      // margin-right:-0.3025rem;
      /**
      * 计算垂直居中时候背景图阴影造成的误差
      * 实际位置:365*0.74/2 = 135.5
      * 当前位置:425*0.74/2 = 157.25
      * 误差:(157.25 - 135.5)/2 = 11.1px
      */
      margin-top:-0.111rem;
    }
  
    .swiper-slide-active{
      transform: scale(1);
      width: 3rem !important;
      height:4.25rem !important;
      /**
      * 消除边距计算对当前播放容器的误差
      * 边距影响:78/2 - 30.25
      * 消除误差:17.5 - (78/2 - 30.25) = 8.75px
      */
      // margin-left:0.0875rem !important;
      // margin-right:0.0875rem !important;
    }

js:

  ...

  // 需要变化的部分配置
  get parameters() {
    const parameters = {
      spaceBetween: -0.225 * this.rootFontSize,
      slidesOffsetBefore: 0.375 * this.rootFontSize,
      slidesOffsetAfter: -0.375 * this.rootFontSize,
      width: 3 * this.rootFontSize,
      height: 4.25 * this.rootFontSize,
      observer: true // handle async
    };
    if (this.images.length === 1) {
      parameters.spaceBetween = 0;
    }
    return parameters;
  }

  public mounted() {
    this.initRootFontSize();
    this.initSwiper({
      initialSlide: this.index
    });
  }

  // get rem(root font-size)
  public initRootFontSize() {
    const html = document.getElementsByTagName('html')[0];
    const rootFontSizeStr = html.style.fontSize;
    this.rootFontSize = parseFloat(rootFontSizeStr);
  }

  public initSwiper(params= {}) {
    const mySwiper = new Swiper('.swiper-container', {
      slidesPerView: 1,
      loop: false,
      preloadImages: false, // preload images:false
      lazy: false, // lazy load images
      on: {
        slideChange: this.onSlideChange,
        sliderMove: () => {
          if (this.slideStatus !== 'fadeInOut') {
            this.slideStatus = 'fadeInOut';
          }
        },
        touchEnd: () => {
          this.slideStatus = 'fadeOut';
        },
      },
      ...this.parameters,
      ...params
    });
    this.mySwiper = mySwiper;
  }

  ...

Stepped pit

Problems with scale:

  • Although scale will reduce the size of the element, the original size space will not be released. You can refer to zoom vs transform:scale , which will produce zoom spacing between elements
  • slidesPerView: Number configuration property is set to a value, it is very likely that there will be a center page that does not always appear in the center.
  • loop:true , the three-point node will be replicated. That is to say, there are 10 items in the list, and there will be 3*10=30 nodes. When there are more, it will affect the performance.
  • loop:true switches between the very beginning and the very end. Clicking on the swiper will not trigger the click event. The click will not be triggered on swipe-slide
  • on:tap(swiper,event) This will be triggered when you click on any page. But: the event test is excellent on the PC (including the path attribute), but on the real machine, there is only isThrud. You can’t know which element the user clicked

Solution one

  • Use margin-left: x<0 ; margin-right<0 to eliminate zoom spacing [There will be problems when switching between horizontal and vertical screens]
  • slidesPerView:'auto' better to use
  • Ban loop:true
  • Use on:tap be cautious, real machine testing is indispensable

Perfectly realize special shape (support horizontal and vertical screen switching)

Although the negative margins are set through the above margins, the visually shaped effect is achieved, but the problem of the style being changed was found when the horizontal and vertical screens were switched.

Reason: The way you modify the swipe wrapper and slider by using CSS styles will not be counted by swiperJS, which causes problems in the calculation of translate.

Solution 1: Realize the center of the activity card by yourself

{
  slidesPerView: 1,
  loop: false,
  preloadImages: false, // preload images:false
  lazy: false, // lazy load images
}

Solution 2: How to remove sclae margins without using CSS styles

image.png

After using it, it is found that this will set the slider: spaceBetween. You can use this to replace the previous CSS margin setting, this distance will be calculated by swiepr.

The default of centeredSlides is left. How to solve the centering effect of the first card?

image.png

So the calculated value can be set:

{
  spaceBetween: -0.3025 * this.rootFontSize,
  slidesOffsetBefore: 0.375 * this.rootFontSize,
  slidesOffsetAfter: -0.375 * this.rootFontSize,
}

Why set slidesOffsetAfter?
If it is not set, a welt BUG will appear when dragging left and right.

Solution 3: Screen size adaptation (screens of different models | switch between horizontal and vertical screens)

  • The value swiper set in solution 2 only supports px, so the setting must be rem → px
  • You need to get the latest rem and update the swiper when switching between horizontal and vertical screens
  public mounted() {
    this.initRootFontSize();
    this.initSwiper();
    window.addEventListener('resize', this.resizeHandler, false);
  }


  public initRootFontSize() {
    const html = document.getElementsByTagName('html')[0];
    const rootFontSizeStr = html.style.fontSize;
    this.rootFontSize = parseFloat(rootFontSizeStr);
  }  

  public resizeHandler() {
    this.initRootFontSize();
    /** update swiper params */
    this.mySwiper.params.spaceBetween = -0.3025 * this.rootFontSize;
    this.mySwiper.params.slidesOffsetBefore = 0.375 * this.rootFontSize;
    this.mySwiper.params.slidesOffsetAfter = -0.375 * this.rootFontSize;
    this.mySwiper.update();// 关键API
  }

In this way, the layout display is no problem, but for the abnormal shape, the width and height of the inactive card may change. If you have strict requirements on the width and height of the slider, you need to specify the width and height in the configuration:


{
  height:4.25 * 
this.rootFontSize,
  width:3 * this.rootFontSize,
}

Functions that change configuration can be extracted:

  // 需要变化的部分配置
  get parameters() {
    return {
      spaceBetween: -0.225 * this.rootFontSize,
      slidesOffsetBefore: 0.375 * this.rootFontSize,
      slidesOffsetAfter: -0.375 * this.rootFontSize,
      width: 3 * this.rootFontSize,
      height: 4.25 * this.rootFontSize,
      observer: true // handle async
    };
  }

  public initSwiper(params= {}) {
    const mySwiper = new Swiper('.swiper-container', {
      slidesPerView: 1,
      loop: false,
      preloadImages: false, // preload images:false
      lazy: false, // lazy load images
      on: {
        slideChange: this.onSlideChange
      },
      ...this.parameters,
      ...params
    });
    this.mySwiper = mySwiper;
  }

  public resizeHandler() {
    this.initRootFontSize();
    /** update swiper params */
    Object.keys(this.parameters).forEach(key => {
      const value = this.parameters[key];
      this.mySwiper.params[key] = value;
    });
    this.mySwiper.update();
  }

Solution 4: Load data asynchronously, card layout problem

Usually the list is obtained from the interface and then set, and the card layout may be displayed abnormally. At this time, you can try the following in the configuration:

{
 observer:true
}

This will update the element layout.

Solution 5: After the above 4 steps are completed, the problem of snapping to the left under the single display

    // 处理只有一张图片的时候贴左的样式问题
    if (images.length === 1) {
      this.mySwiper.params.spaceBetween = 0;
      this.mySwiper.update();
    }

Summarize

Solutions to achieve perfect opposite sex:

  • spaceBetween/slidesOffsetBefore/slidesOffsetAfter
  • observer: true

optimization

  • v-lazy
  • v-touch

specialCoder
2.2k 声望168 粉丝

前端 设计 摄影 文学