微信小程序实现tap切换(可手势滑动、导航跟随滚动)

2020-04-24  本文已影响0人  Zzc皮卡丘
demo效果.gif

开篇

直接说重点,本篇内容主要实现的功能有:导航栏tap切换、导航栏跟随滚动居中显示、手势滑动切换。
目前网上能看的讲解的多多少少有些问题,功能不够完整,走了很多弯路才调整成比较稳定效果和体验,开发过程中也借用了一些别人的案例。下面页面实现代码全部贴上,可直接copy套用。

 

1、js实现

js这部分\color{rgb(255,0,0)}{特别强调}页面触摸事件,通过触摸判断手势滑动的方向,重点在\color{rgb(255,0,0)}{获取手势触摸XY轴坐标}位置。

list页面不需要上下滚动,固定一屏的大小,只有左右滑动请使用:
this.starthDotX = e.touches[0].pageX;
this.starthDotY = e.touches[0].pageY;

list页面需要上下滚动,类似scroll-view效果,需上下左右滚动请使用:
如果list页面需要滚动,使用e.touches[0].pageX方法。list页面会上下滚动到某一个点的时候XY轴数据与实际滑动距离不符合,导致判断方法失效,上下滑动的同时突然执行左右滑动的操作,滑动的时候乱窜的情况
this.starthDotX = e.touches[0].clientX;
this.starthDotY = e.touches[0].clientY;

判断触摸手势方向的逻辑:
if(Math.abs(X) > Math.abs(Y) && X > 0 && Math.abs(X) > minOffset) {
console.log("向右滑动");
}else if(Math.abs(X) > Math.abs(Y) && X < 0 && Math.abs(X) > minOffset) {
console.log("向左滑动");
}else if(Math.abs(X) < Math.abs(Y) && Y > 0) {
console.log("向下滑动");
}else if(Math.abs(X) < Math.abs(Y) && Y < 0) {
console.log("向上滑动");
}

上代码:

var minOffset = 30;//最小偏移量,低于这个值不响应滑动处理

var DtaList0 = [{Name: "锦绣未央", url:"/image/timg0.jpg"},{Name: "锦民国往事", url:"/image/timg0.jpg"},{Name: "陈情令", url:"/image/timg0.jpg"}];
var DtaList1 = [{url:"/image/timg1.jpg", Name: "笑傲江湖"},{url:"/image/timg1.jpg", Name: "绣春刀"},{url:"/image/timg1.jpg", Name: "白夜追凶"},{url:"/image/timg1.jpg", Name: "三生三世"}];
var DtaList2 = [{url:"/image/timg2.jpg", Name: "法证先锋"},{url:"/image/timg2.jpg", Name: "无心法师"},{url:"/image/timg2.jpg", Name: "我是余欢水"}];
var DtaList3 = [{url:"/image/timg3.jpg", Name: "东宫"},{url:"/image/timg3.jpg", Name: "我只喜欢你"},{url:"/image/timg3.jpg", Name: "我只喜欢你"},{url:"/image/timg3.jpg", Name: "爱情公寓"}];
var DtaList4 = [{url:"/image/timg4.jpg", Name: "刘老根3"},{url:"/image/timg4.jpg", Name: "镇魂街"},{url:"/image/timg4.jpg", Name: "仙剑奇侠3"},{url:"/image/timg4.jpg", Name: "寻情记"}];
var DtaList5 = [{url:"/image/timg5.jpg", Name: "人民的正义"},{url:"/image/timg5.jpg", Name: "亮剑"}];
var DtaList6 = [{url:"/image/timg6.jpg", Name: "楚汉传奇"},{url:"/image/timg6.jpg",Name: "少帅"},{url:"/image/timg6.jpg",Name: "步步惊心"}];
var DtaList7 = [{url:"/image/timg7.jpg", Name: "大唐女法医"},{url:"/image/timg7.jpg", Name: "十二传说"}];
var DtaList8 = [{url:"/image/timg8.jpg", Name: "甄嬛传"},{url:"/image/timg8.jpg", Name: "萧十一郎"},{url:"/image/timg8.jpg", Name: "开国大典"}];
var DtaListArray = [DtaList0, DtaList1, DtaList2, DtaList3, DtaList4, DtaList5, DtaList6, DtaList7]

Page({
  /*
   * 页面的初始数据
   */
  data: {
    DtaList: [{}], //存放每个页面list数据
    activeId: "", //记录导航栏tap的id
    tabIndex: 0,//记录导航栏点击位置
    starthDotX : 0, //触摸事件开始X轴的位置
    starthDotY : 0,//触摸事件开始Y轴的位置

    TypeList: [{posterId: 10561005, typeDesc: "央视网"},{posterId: 10561003, typeDesc: "爱奇艺"},{posterId: 10561008, typeDesc: "腾讯视频"},{posterId: 10561010, typeDesc: "PPTV"},{posterId: 10561002, typeDesc: "优酷"},{posterId: 10561007, typeDesc: "哔哩哔哩"}, {posterId: 10561006, typeDesc: "芒果TV"}, {posterId: 10561004, typeDesc: "西瓜视频"}]   //导航栏假数据
  },

/*****************************tap切换点击事件*****************************/
  changeTab: function (e) {
    var id = e.target.dataset.type;
    var index = e.target.dataset.index;

    //禁止tap重复点击
    if(index == this.data.tabIndex){
      return
    }

    //改变tap点击位置
    this.setData({
      activeId: id,
      tabIndex:index, 
      DtaList: DtaListArray[index]
    });

    //当前tab点击栏居中显示
    var singleNavWidth = this.data.windowWidth / 5;
    this.setData({
      navScrollLeft: (index - 2) * singleNavWidth
    }) 
  },

  /*****************************触摸事件*****************************/
  //触摸开始
  touchstart: function(e){
    // this.starthDotX = e.touches[0].pageX;
    // this.starthDotY = e.touches[0].pageY;
    this.starthDotX = e.touches[0].clientX;
    this.starthDotY = e.touches[0].clientY;
  },
    
  //触摸进行中
  touchmove: function(e){
    var Direction = ""
    // var moveDotX = e.touches[0].pageX;
    // var moveDotY = e.touches[0].pageY;
    var moveDotX = e.touches[0].clientX;
    var moveDotY = e.touches[0].clientY;
    
    var X = moveDotX - this.starthDotX;
    var Y = moveDotY - this.starthDotY;

    if(Math.abs(X) > Math.abs(Y) && X > 0 && Math.abs(X) > minOffset) {
      Direction = "右滑"
    }else if(Math.abs(X) > Math.abs(Y) && X < 0 && Math.abs(X) > minOffset) {
      Direction = "左滑"
    }else if(Math.abs(X) < Math.abs(Y) && Y > 0) {
      Direction = "下滑"
    }else if(Math.abs(X) < Math.abs(Y) && Y < 0) {
      Direction = "上滑"
    } 

    //记录当前滑动方向
    this.setData({
      glideDirection: Direction 
    })
  },

  //触摸结束
  touchend: function(e){
    if(this.data.glideDirection == "右滑"){
      if(this.data.tabIndex !== 0){
        var typeData = this.data.TypeList[this.data.tabIndex - 1];
        var posterId = typeData.posterId;

        //改变tap点击位置
        this.setData({
          activeId: posterId,
          tabIndex: this.data.tabIndex - 1,
          DtaList: DtaListArray[this.data.tabIndex - 1]
        })

        //新增tab点击当前栏居中显示
        var singleNavWidth = this.data.windowWidth / 5;
        this.setData({
           navScrollLeft: (this.data.tabIndex - 2) * singleNavWidth
        }) 
      }else{
        console.log("我是第一个滑不动啦,不要滑啦!!!")
      }
    }else if(this.data.glideDirection == "左滑"){
      if(this.data.tabIndex !== this.data.TypeList.length -1){
        var typeData = this.data.TypeList[this.data.tabIndex + 1];
        var posterId = typeData.posterId;
        //改变tap点击位置
        this.setData({
          activeId: posterId,
          tabIndex: this.data.tabIndex + 1,
          DtaList: DtaListArray[this.data.tabIndex + 1]
        })

        //tab点击当前栏居中显示
        var singleNavWidth = this.data.windowWidth / 5;
        this.setData({
           navScrollLeft: (this.data.tabIndex - 2) * singleNavWidth
        }) 
      }else{
        console.log("已经是最后一个啦,不要滑啦!!!")
      }
    }
  },


  //列表cell点击事件
  clickBrand: function (e) {
    var index = e.currentTarget.id;
    var listData = this.data.DtaList[index];
    console.log("当前点击的是:" + listData.Name)
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    //改变tap点击位置
    this.setData({
      activeId: '10561005',
      DtaList: DtaList0
    });
    
    //新增获取屏幕Width
    wx.getSystemInfo({
      success: (res) => {
          this.setData({
              windowWidth: res.windowWidth
          })
      },
    })       
  },

  /**
   * 生命周期函数--监听页面初次渲染完成
   */
  onReady: function () {
    
  },
  
  /**
   * 生命周期函数--监听页面显示
   */
  onShow: function () {//页面已经显示出来
    
  }
})

 

2、wxml实现

这部分没有太多难度,跟搭积木一样,一个view一个view的嵌套。

导航栏部分
原理:通过<scroll-view>控件实现,for循环每一个按钮,实现点击/滑动切换。
控件属性:scroll-left="{{navScrollLeft}} -> 用来控制当前点击栏滚动的位置,滚动居中效果。

list列表部分
原理:没有使用系统swiper组件,而是通过for循环<view>的形式实现。
控件属性:bindtouchstart="touchstart" / bindtouchmove="touchmove"/ bindtouchend="touchend" -> 监听list页面的触摸动作

上代码:

<scroll-view scroll-x="true" class="ip_tab_comtainer" scroll-with-animation="{{true}}" scroll-left="{{navScrollLeft}}">
  <block wx:for="{{TypeList}}" wx:for-item="item" wx:ky = "*this" wx:for-index = "index">
    <view class="ip_tab_item_n {{activeId == item.posterId?'active':''}}" bindtap="changeTab" data-type="{{item.posterId}}" data-index="{{index}}">{{item.typeDesc}}
    </view>
  </block>
</scroll-view>

<view class = "hotView" bindtouchstart="touchstart" bindtouchmove="touchmove" bindtouchend="touchend">
    <view class = "hotCell" wx:for = "{{DtaList}}"  wx:for-item='item' wx:for-index="dindex" wx:ky = "*this" bindtap = "clickBrand" id = "{{dindex}}">
        <view class='imageBottomView'>
          <image src="{{item.url}}"></image>
        </view>
        <view class='textBottom'>
          <view class ="title" style="opacity:{{1}}">{{item.Name}}</view>
        </view>
    </view>
</view>

 

3、wxss实现

这部分没有太多可说的,样式自己慢慢去调。

上代码:

page{
  background-color: #F1F1F1; 
}

::-webkit-scrollbar{/*隐藏scrollerView滚动条*/
    width: 0;
    height: 0;
    color: transparent;
}

.ip_tab_comtainer {
  width: 100%;
  display: flex;
  position:fixed;
  margin-bottom: 6rpx;
  white-space: nowrap;
  background-color: #ffffff;
}

.ip_tab_item_n {
  display: inline-block;
  margin: 0rpx 25rpx 0rpx 25rpx;
  padding: 28rpx 0rpx 20rpx 0rpx;
  color: #666666;
  font-size: 32rpx;
  text-align: center;
  overflow: hidden;
  text-overflow: ellipsis;
}

.active{
  color: #F7353B;
  border-bottom: 6rpx solid #F7353B; 
}

.hotView {
  display: flex;
  flex-wrap: wrap;
  padding-top: 120rpx;
}

.hotView .hotCell {
  width: 46%;
  text-align: center;
  margin-bottom: 20rpx;
  margin-left: 15rpx;
  margin-right: 15rpx;
}

.imageBottomView{
  width: 100%;
  height: 450rpx;
}

.imageBottomView image{
  width: 100%;
  height: 100%;
  border-radius: 5px 5px 0px 0px;
}


.textBottom{
  height: 65rpx;
  display: flex;
  align-items: center;
  background-color: white;
  border-radius: 0px 0px 5px 5px;
}
.textBottom .title{
  width: 65%;
  color: #5E5E5E;
  text-align: left;
  font-size: 30rpx;
}

如有不足欢迎留言一起补充探讨 ~

上一篇 下一篇

猜你喜欢

热点阅读