小程序圖片輪播特效swiper(純手打)

前言

一個月前仍是用vue作微信H5,後面公司業務發展,入坑小程序,作了幾款小程,跑了很多坑, 也會陸續在後面幾節跟你們分享。
在這節給你們分享這個 小程序圖片輪播實現方案

初步的實現思路

我要實現的效果

能看到 左右兩邊若是有圖,會顯示一部分出來,原本也想用 小程序自帶的swiper 組件,沒法達到,左右兩邊 看到上(下)圖片的效果,沒法改變他組件原來的設置(坑)vue

圖片描述

在開始以前,先想一想h5是怎麼實現的,用原生寫,能夠在某個固定框的盒子裏,再用一個div 包住 x 張圖,而後改變 ontouchmove 時改變 left(或者translateX) ,判斷用戶手指滑動足夠距離,就滑到下(上)一張,
最後判斷到最後一張或者第一張,不滑動就好(在這裏不作無縫滾動)。
jquery,vue 不少庫都可以實現jquery

咱們只要 實現了第一張圖的滑動,記錄這個偏移量 tranX1 = 0 + offsetX, 第二張的滑動距離 tranX2 = tranX1 + offsetX ,第三張的滑動距離 tranX3 = tranX2 + offsetX ...
相反方向 也就 遞減  tranX1 = tranX2 - offsetX ...
而後判斷臨界值就OK了
瞭解了大概的滑動過程如今就能夠掀起袖子幹了~

具體技術實現

寫了個 swiper 的方法,用原形鏈繼承的方式,與小程序的page({})裏面代碼儘可能分開, 用慣了Vue的雙向綁定,特別討厭小程序的 this.setData 並且是異步的(坑),this有時候要用變量保留,
先定義了幾個變量,初始化一些必要參數,
記錄 touch 過程當中 滑動開始的座標,滑動過程當中,和滑動結束時的座標,git

構造 swiper 函數裏面

opt 配置
swiperIndex 用於記錄 滑動到的圖片索引,默認值爲0
singleOffsetX 滑一張的tranX 偏移量
boxTranslateX 盒子容器總偏移量 默認0
_x_start (單位px)記錄touchstart裏的e.touchs[0].pageX
_y_start (單位px)記錄touchstart裏的e.touchs[0].pageY
_x_move (單位px)記錄touchsmove裏的e.touchs[0].pageX
_x_offset 臨時變量,記住 _x_move-_x_start 手指移動距離
_x_end (單位px)記錄touchend裏的 e.changedTouches[0].pageX
_x_realOffset (單位px)臨時變量,根據臨界值 圖片真實移動
screenWidth = 750 屏幕寬度默認 iphone6 750rpx
screenHeight = 1210 屏幕高度默認 iphone6 1210rpx
pixelRatio 根據微信api裏 systemInfo設備屏幕寬度 計算比例
maxTranX 根據圖片的數量計算最大偏移量,用於臨界判斷圖片描述
width 盒子的總寬度(用於view 上面綁定 單位:rpx )根據圖片數量計算盒子的總寬度 = 首末兩邊(100)x 2+ 圖片與圖片之間的間距(60)* (圖片數量 -1)+ 圖片寬度 x 圖片數量

(this.width = 100 2 + (this.opt.imgLength - 1) 60 + 550 * this.opt.imgLength)

而後執行函數 computedScreenRatio 根據寬轉換比例

代碼以下

'use strict';
var swiper = function(opt){
  // 保存設置
  this.opt =opt
  // 當前 移動塊停留索引
  this.swiperIndex = 0
  // 盒子容器總偏移量
  this.boxTranslateX = 0 
  // 每移動一張圖會增長的偏移量
  this.singleOffsetX = -610
  // 觸摸
  this._x_start = 0 
  this._y_start = 0 
  // 移動
  this._x_move = 0 
  // 離開
  this._x_end = 0 
  // 偏移量
  this._x_offset = 0
  // move 過程當中真實偏移
  this._x_realOffset = 0

  this.screenWidth = 750
  this.screenHeight = 1210
  this.pixelRatio = 2
  // rpx
  this.maxTranX = this.singleOffsetX * (this.opt.imgLength -1  ) 
  // 容器寬度 width 100 是兩邊靠牆
  this.width = 100 * 2 + (this.opt.imgLength - 1) *60 + 550 * this.opt.imgLength

  // 計算寬轉換比例
  this.computedScreenRatio(opt.systemInfo)
}

rpx(responsive pixel): 能夠根據屏幕寬度進行自適應。規定屏幕寬爲750rpx。如在 iPhone6

上,屏幕寬度爲375px,共有750個物理像素,則750rpx = 375px = 750物理像素,1rpx = 0.5px = 1物理像素。算法

iphone6 1rpx = 0.5px , iphone5 1rpx = 0.42px 根據寬度作兼容

// 計算寬轉換比例
swiper.prototype.computedScreenRatio = function (systemInfo){
  this.pixelRatio = systemInfo.pixelRatio
  this.screenWidth = systemInfo.screenWidth
  this.screenHeight = systemInfo.screenHeight  
  //this.singleOffsetX = -610
  // 奇怪的i5 按照算法會 多偏移2 px
  if (systemInfo.model.indexOf('iPhone 5') > -1 ){
    this.singleOffsetX = -(610 * (this.screenWidth / 750) -2  ).toFixed()
  }
  else{
    this.singleOffsetX = -610 * (this.screenWidth / 750)
  }
  this.maxTranX = this.singleOffsetX * (this.opt.imgLength - 1) 
  // 回調綁定vm ,渲染盒子寬度
  this.opt.success && this.opt.success(this.width)
}

扯一下小程序

  • bindtouchstart : ontouchstart
  • bindtouchmove: ontouchmve
  • bindtouchend : ontouchend 同樣是有e.touches e.changedTouches

下面是從滑動開始到結束的處理過程代碼分析

keepStartX(touchstart): 記錄開始狀態 比較簡單就不講了

moveBox (touchend)是一個手指滑動,全部圖片(盒子)跟着手指滑動的過程

  • 1.手指向右滑,_x_offset > 0 ,滑向上一張圖
  • 2.手指向左滑,_x_offset < 0 ,滑向下一張圖
  • 按邏輯來講,手指水平方向上pageX 的距離, 圖片盒子就應該跟着向(左)或者向右偏移同樣的量, 可是 從最後一張 開始 向左滑,後面已經沒有圖了,(或者 從第一張圖開始已經向右滑,前面已經沒有圖了 ),這時候就要作臨界值判斷了,結果就有手指滑動距離 _x_offset ,還有盒子滑動應該滑的準確距離 lastTranX

touchEnd

computedTranX:

  • 根據 滑動方向,臨界值 計算出 手指滑動時,盒子的應該滑動的方向與距離 tranX (注: 這裏tranX 從第一張開始是0 ,偏移量是負數,因此_x_realOffset永遠是 <0 )

計算最後手指離開的座標,_x_realOffset用來保存計算 真實的在x方向上的距離R小程序

// bindtouchstart
swiper.prototype.keepStartX = function(e, callback){
  this._x_start = e.touches[0].pageX
  this._y_start = e.touches[0].pageY
  callback && callback()
}
// bindtouchmove
/*
  函數說明 moveBox
  @params e  事件
  @params {function} callback 圖片盒子的偏移量改變,綁定到vm 上面,視圖發生變化
*/
swiper.prototype.moveBox = function (e, callback) {
  this._x_move = e.touches[0].pageX 
  this._x_offset = this._x_move - this._x_start 
  var lastTranX = this.computedTranX(this._x_offset)
  this._x_realOffset = lastTranX
  callback && callback(lastTranX)
}
/* 
    函數說明 根劇移動偏移量臨界值計算出真實位移用
    滑下一張 判斷是否超出臨界值,超出則等於臨界值
     若不超過則偏移量 = 手指(touch)移動距離 + 當前盒子偏移量
*/
swiper.prototype.computedTranX = function ( offsetX ){
    // 滑向上一張
    if( offsetX > 0  ){
      return offsetX + this.boxTranslateX 
    }
    // 滑動下一張
    else if (offsetX < 0 ){ 
      if ( Math.abs(offsetX - this.boxTranslateX) > Math.abs(this.maxTranX - 50) ){
        return this.maxTranX
      }else{
        return offsetX + this.boxTranslateX 
      }
    }
}

// bindtouchend 
swiper.prototype.ontouchEnd = function(e, callback){

  // 記錄手指離開屏幕的座標
  this._x_end = e.changedTouches[0].pageX
  this._x_realOffset = this._x_end - this._x_start
  // 判斷滑動距離超過 指定值 輪播一張圖
  var canExchangeImgFromOffset = Math.abs(this._x_realOffset) >= 50 ? true : false
  // 滑動距離達到 換圖的要求
  if ( canExchangeImgFromOffset ){
    // 變量說明 計算總偏移
    var lastTotalTranlateX ;
    //  滑向上一張
    if (this._x_offset > 0) {
      console.log('//  滑向上一張')
      // 判斷是否已經在第一張圖,若不是,則滑動上一張
      if (Math.abs(this.boxTranslateX) > 0) {
        // lastTotalTranlateX 遞增
        // this.singleOffsetX 爲負數 -- 得 +
        this.swiperIndex -= 1
        lastTotalTranlateX = this.boxTranslateX - this.singleOffsetX
      } 
      else {
        // 已在第一張圖 ,動畫恢復原位
        this.swiperIndex = 0
        lastTotalTranlateX = 0
      }
    }
    //  滑向下一張
    else {
      console.log('//  滑向下一張')
      if ( Math.abs(this.boxTranslateX) < Math.abs(this.maxTranX) ) {
        // lastTotalTranlateX 遞增
        this.swiperIndex += 1
        lastTotalTranlateX = this.boxTranslateX + this.singleOffsetX
      } else {
        console.log('到底圖了')
        this.swiperIndex = this.opt.imgLength -1
        lastTotalTranlateX = this.maxTranX 
      }  
    }
    this.boxTranslateX = lastTotalTranlateX
    /* callback 說明
      @參數1  是否執行動畫
      @參數2  若參數1爲true, 則盒子的tranX 應該變成 lastTotalTranlateX,動畫飛到對應的圖 
       小程序 執行 wx.createAnimation 
    */
    callback && callback(true, lastTotalTranlateX)
    // reset 全部 與touch 相關參數
    this.resetParams()
  }
  else{
    callback && callback(false)
    // reset 全部 與touch 相關參數
    this.resetParams()
  }
}
源碼地址 https://gitee.com/adfasdfasdfas/XiaoChengXu-swiper-LunBodemo
相關文章
相關標籤/搜索