來吧,一起給小程式原生的swiper化個妝,讓她變成更漂亮點的banner吧(基於mpvue)

飄逸丶麥子發表於2018-09-13

    前些日子老闆大人又安排了新的任務——banner的開發,拿到設計妹子給的設計圖之後,我的第一感覺就是——呵!看起來不難嘛!不就是用touchStart,touchmove,touchend組合+一些transition效果+一些位置計算就能實現了嗎???看我兩天內不用輪子直接手寫一個實現它!

        。
        。
        。
    一天半後
        。
        。
        。
    在嘗試了wx.createAnimation()中的translateX/left+原生的css3的transition之後,我拿起了鏡子,摸了摸被自己打腫的臉,心裡滿是委屈:這特喵的效果怎麼都不按照預想的來啊!是我的寫法不對還是因為mpvue各種坑使???我開始深深滴懷疑自己。。。在基本確定此路不通之後(後來瞭解到其實應該是mpvue資料更新機制的問題,每次修改一個data()裡面的值,都會重新整理其他的值,不信你可以在mpvue中去試試用scroll-view元件的scroll-top屬性實現返回頂部試試。。。),我開啟了技術群,丟擲了問題,然後有人指出其實用小程式的swiper應該也可以實現的,這個想法我之前在某一瞬間曾經有過——swiper設定好基礎引數,然後額外加一些樣式,應該可以實現。這位仁兄的思路和我不謀而合(咳咳咳,我先不要臉,你們隨意),我也重拾信心,下面呢,就是具體的實現方法以及效果了:

1.先看下效果吧

    動圖已放(LICEcap製作)!感謝評論裡的@wangx老哥!想看具體的效果的小夥伴們可以去我github的專案:mpvue-banner上下載並且跑起來看一下,如果這個banner不是你想要的效果,您可以選擇不再往下看,或者可以看看實現的思路~具體效果如下:

來吧,一起給小程式原生的swiper化個妝,讓她變成更漂亮點的banner吧(基於mpvue)
banner就在上圖中紅色框內,可以看到此款banner並沒有像大多數banner一樣佔據整個螢幕的寬度,而是前後的banner圖都露出了一部分,而且展示的banner圖比前後的banner圖片大小上還放大了一定的倍數,這樣看下來是不是覺得整體設計上要比預設的banner效果好了不少呢?如果還有興趣的話,請繼續往下看具體的實現思路以及程式碼

2.看完了效果,我們再來看下mpvueswiper元件吧

    mpvue的該元件也是基於小程式原生的swiper元件實現的,具體的屬性我就不再挨個介紹了,畢竟官方文件裡寫的很清楚了~這裡就主要說下我們要實現上圖中的banner要依賴的最重要的兩個屬性previous-marginnext-margin,前者主要作用是「露出前一項的一小部分」,後者主要作用是「露出後一項的一小部分」,好了,我們先把mpvue-swiper元件介紹中的程式碼copy 過來:

<template>
  <div class="page">
    <view class="page__hd">
      <view class="page__title">Swiper</view>
      <view class="page__desc">滑塊檢視容器,這裡採用小程式原生 swiper 元件實現。</view>
    </view>
    <div class="page__bd page__bd_spacing">
      <swiper :indicator-dots="indicatorDots" 
        :autoplay="autoplay" 
        :interval="interval" 
        :duration="duration" 
        :circular="circular" 
        @change="swiperChange" 
        @animationfinish="animationfinish">
        <div v-for="item in imgUrls" :key="index">
          <swiper-item>
            <image :src="item" class="slide-image" />
          </swiper-item>
        </div>
      </swiper>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      indicatorDots: true,
      autoplay: true,
      interval: 5000,
      duration: 900,
      circular: true,
      imgUrls: [
        'http://img02.tooopen.com/images/20150928/tooopen_sy_143912755726.jpg',
        'http://img06.tooopen.com/images/20160818/tooopen_sy_175866434296.jpg',
        'http://img06.tooopen.com/images/20160818/tooopen_sy_175833047715.jpg'
      ]
    }
  },
  methods: {
    swiperChange(e) {
      console.log('第' + e.mp.detail.current + '張輪播圖發生了滑動');
    },
    animationfinish(e) {
      console.log('第' + e.mp.detail.current + '張輪播圖滑動結束');
    }
  }
}
</script>
<style>
.slide-image {
  width: 100%;
  height: 100%;
}
</style>
複製程式碼

粘完這些程式碼,你能實現一個很常規的banner了,然後我們加上剛剛我們提到的那兩個屬性:

    <swiper :indicator-dots="indicatorDots" 
        :autoplay="autoplay" 
        :interval="interval" 
        :duration="duration" 
        :circular="circular" 
        :previous-margin="'60rpx'"
        :next-margin="'60rpx'"
        @change="swiperChange" 
        @animationfinish="animationfinish">
        <div v-for="item in imgUrls" :key="index">
          <swiper-item>
            <div class="img-wrapper">
                <image :src="item" class="slide-image" />
            </div>
          </swiper-item>
        </div>
    </swiper>
複製程式碼

這時候你就實現了一個能將前一項和後一項各露出60rpx的banner了,只不過此時各項的圖片大小都是相同的,那怎麼實現主項的圖片大小的放大呢,當然是使用css的transform給圖片標籤加各放大的樣式,且往下看程式碼:

    <swiper :indicator-dots="indicatorDots" 
        :autoplay="autoplay" 
        :interval="interval" 
        :duration="duration" 
        :circular="circular" 
        :previous-margin="'60rpx'"
        :next-margin="'60rpx'"
        @change="swiperChange" 
        @animationfinish="animationfinish">
        <div v-for="item in imgUrls" :key="index">
          <swiper-item>
            <div class="img-wrapper"
                :style="{
                    boxSizing: 'border-box', 
                    width: '100%', 
                    height: '100%', 
                    display: 'flex', 
                    justifyContent: 動態值,需要根據設計圖以及banner圖片的個數以及位置進行計算得出, 
                    padding: 動態值,需要根據設計圖以及banner圖片的個數以及位置進行計算得出
                }">
                <image :src="item" 
                    class="slide-image" 
                    :style="{
                        transform: currentIndex===bannerIndex?'scale(' + scaleX + ',' + scaleY + ')':'scale(1,1)',
                        transitionDuration: '.3s',
                        transitionTimingFunction: 'ease'
                    }"/>
            </div>
          </swiper-item>
        </div>
    </swiper>
複製程式碼

其中幾個出現的引數:
currentIndex:即當前展現的banner項的索引
bannerIndex:即banner項在整個圖片列表中的索引
scaleX以及scaleY:即你希望的主項的放大的倍數,此項的值可能需要我們根據螢幕寬度以及設計稿的展示來進行計算
這幾個樣式就是:將當前展示的圖片放大一定的倍數
到了這裡,我們需要的結構以及style上的程式碼基本上都有了,下面主要是script裡對一些關鍵的引數進行控制,這裡有個比較重要的函式@change

<script>
data () {
    return {
        autoplay: false,
        interval: 3000,
        duration: 300,
        circular: true,
        currentIndex: 0,
        scaleX: (634 / 550).toFixed(4),
        scaleY: (378 / 328).toFixed(4)
    }
},
methods: {
    // 控制currentIndex以及動畫執行索引descIndex的值
    swiperChange (e) {
        const that = this
        this.currentIndex = e.mp.detail.current
        this.scaleX = (634 / 550).toFixed(4)
        this.scaleY = (378 / 328).toFixed(4)
    }
}
</script>
複製程式碼

3.注意點

至此呢,主圖中的banner的主要效果基本已經實現了,看下來其實並不是很難,主要是一些細節需要特別注意:

3.1:previous-margin和next-margin的值

它們的值並不是隨便寫的,需要你根據設計圖去進行細微的計算

3.2:swiper-item內,image標籤外的class名為img-wrapper的div容器的樣式

其中我沒有寫出具體值的兩項屬性:justifyContent與padding,他們的具體值同樣需要你去進行計算,此時的計算不止會涉及到設計稿,他們的值還會根據當前展示出來的三張圖片在整個imgList(至少三項)中的順序的不同而不同,在我的實現中我使用了超長的三目運算子來保證每個圖片的具體的屬性值。。。

3.小結

    專案已經上傳至github:mpvue-banner,歡迎fork或者討論,由於可定製性太強,就不寫成npm包了,大家需要的話自己去研究吧!
    由於mpvue的一些的坑的存在,導致我們像自己實現一些輪子確實很困難,在這種情況下,我們就要學會如何利用現成的輪子去改造,進而去實現我們自己所需的輪子。造不出輪子不要緊,但我們要保持一顆研(mo)究(fang)進(gai)取(zao)的❤️!
    這個介紹到這而就打住了,其實這期也沒什麼東西,我想對於很多人來說只要肯花時間和精力,實現這樣的效果就是小菜一碟!
    “我們不造輪子,我們只做輪子的改造者與搬運工”

相關文章