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


前言

一個月前還是用vue做微信H5,后面公司業務發展,入坑小程序,做了幾款小程,跑了不少坑, 也會陸續在后面幾節跟大家分享。
在這節給大家分享這個 小程序圖片輪播實現方案

初步的實現思路

我要實現的效果

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

圖片描述

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

我們只要 實現了第一張圖的滑動,記錄這個偏移量 tranX1 = 0 + offsetX, 第二張的滑動距離 tranX2 = tranX1 + offsetX ,第三張的滑動距離 tranX3 = tranX2 + offsetX ...
相反方向 也就 遞減  tranX1 = tranX2 - offsetX ...
然后判斷臨界值就OK了
了解了大概的滑動過程現在就可以掀起袖子干了~

具體技術實現

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

構造 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

本文轉載於:猿2048⇒https://www.mk2048.com/blog/blog.php?id=h0i2cbh0cib


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM