美文网首页微信小程序开发微信小程序开发
自定义组件之 Swiper 特效组件

自定义组件之 Swiper 特效组件

作者: 夏海峰 | 来源:发表于2019-08-27 22:46 被阅读0次
    效果演示
    一、需求说明

    使用微信小程序原生代码,实现如上图的交互效果。具体要求如下:

    • 当手指触发(tap / touch) Tab区块时,页面自动向上滚动,滚动至视窗的顶部。
    • Tab区块,最多只能滚动到视窗的顶部,不可以继续向上滚动。
    • 当用户向下滑动时,Tab区块正常向下滚动。
    • 当Tab区块里的内容超出一屏的高度时,只能在 Tab区块的内部进行滚动。
    二、封装自定义组件 <my-swiper>

    根据以上需求,使用原生小程序组件和代码,封装一个 <my-swiper> 组件,完整代码如下所示。其中的要点(坑)罗列如下:

    • 当Tab区块置顶时,<my-swiper>组件的高度必须等于屏幕视窗的高度,因此将使用wx.getSystemInfo来动态获取屏幕尺寸(单位是px),通过计算后,手动地设置自定义组件<my-swiper> 和 原生组件 <swiper>的高度,分别是h1h2。详细代码见下方的myswiper.js
    • 小程序原生组件<swiper>是有默认高度的,必须手动设置其高度h2
    • <swiper-item>默认是position:absolute;overflow:hidden;的,我们必须更改其默认样式(见下方的 myswiper.wxss文件),否则当<swiper-item>内部内容较多时会被隐藏掉。另外,为了让<swiper-item>中的内容滚动更流畅,还要为其加上 -webkit-overflow-scrolling:touch;属性。
    • Tab项可能多于3个,或者是 4个,因此建议使用 flex 来动态布局,以确保 Tabs的数量是可变的。
    • 使用 slot 插槽来动态渲染 Tab区块中的内容,在组合使用<slot name='item' />来定义插槽。
    • 为<my-swiper> 组件绑定 tap``touch事件,并判断当用户点击、上滑时,让页面滚动 <my-swiper> 的位置。详细逻辑见下方的 myswiper.js文件。
    # myswiper.wxml
    
    <view class='myswiper' style="height: {{h1}}px;" bindtap='tabTouch' bindtouchstart='touchStart' bindtouchend='touchEnd'>
      <!-- Tabs -->
      <view class="tabs">
        <view class="tab {{curIdx == index ? 'on' : ''}}" wx:for="{{tabArr}}" wx:key="{{index}}" wx:for-item="item" data-index="{{index}}" bindtap="tabClick" >
          <text class="tab-title">{{item}}</text>
          <text class="tab-line"></text>
        </view>
      </view>
      <!-- swiper -->
      <swiper class='swiper' current="{{curIdx}}" bindchange="swiperChange" style="height: {{h2}}px;">
        <swiper-item wx:for="{{tabArr}}" wx:key="{{index}}">
          <slot name='item{{index}}'></slot>
        </swiper-item>
      </swiper>
    </view>
    
    
    # myswiper.wxss
    
    .myswiper {
      position: relative;
      /* height: 1200rpx; */
      background: white;
    }
    .tabs{
      width: 100%;
      height: 100rpx;
      background-color: #eeeeee;
      display: flex;
    }
    .tab{
      flex: 1;
      font-size:28rpx;
      font-family:PingFangSC;
      font-weight:400;
      color:rgba(102,102,102,1);
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      font-weight: bold;
    }
    .tab>text{
      display: block;
    }
    .on .tab-line{
      width:90rpx;
      height:4rpx;
      background:rgba(255,117,56,1);
      border-radius:3rpx;
      margin-top: 5rpx;
    }
    .on .tab-title{
      font-size:32rpx;
      font-family:PingFangSC;
      font-weight:500;
      color:rgba(255,117,56,1);
    }
    .swiper swiper-item {
      /* 开启swiper-item滚动,touch流畅滚动 */
      overflow: scroll;
      -webkit-overflow-scrolling:touch;
    }
    
    # myswiper.js
    
    let startY = 0;
    Component({
      options: {
        multipleSlots: true // 启用多slot支持
      },
      properties: {
        tabArr: {   /* Tab项的名称列表 */
          type: Array,
          value: []
        }
      },
      data: {
        h1: '',   // my-swiper 组件的高度
        h2: '',   // swiper 组件的高度
        curIdx: 0 // tab 索引号
      },
    
      attached: function() {
        const that = this
        // 获取信息,动态设置组件高度
        wx.getSystemInfo({
          success: function(e) {
            const w = e.windowWidth
            const h = e.windowHeight
            // 100 是Tab的默认高度,单位rpx
            // 把 rpx 转化为 px
            const tabH = 100 * w / h
            that.setData({ h1: h, h2: h - tabH })
          }
        });
      },
      methods: {
        /* my-swiper 组件的系列事件 */
        tabTouch: function(e) {
          // 当用户点击my-swiper组件时,将其置顶
          const top = e.currentTarget.offsetTop
          // 滚动到顶部
          wx.pageScrollTo({scrollTop: top});
        },
        touchStart: function(e) {
          startY = e.changedTouches[0].clientY
          console.log('start', startY)
        },
        touchEnd: function(e) {
          const endY = e.changedTouches[0].clientY
          // 当手势向上滑动时,把my-swiper组件置顶
          if (startY - endY > 10) {
            const top = e.currentTarget.offsetTop
            wx.pageScrollTo({scrollTop: top});
          }
          console.log('end', endY)
        },
        // 点击标签事件
        tabClick:function(e){
          let data = e.currentTarget.dataset;
          this.setData({ curIdx: data.index })
        },
        // swiper切换事件
        swiperChange:function(e){
          this.setData({ curIdx:e.detail.current })
        },
      }
    })
    
    # myswiper.json
    
    { "component": true }
    
    三、使用 <my-swiper> 组件,进行测试

    <my-swiper>组件封装好了,测试代码如下。我们只需要传递一个 tabArr 标签列表进去即可。再使用 slot 插槽动态地插入多个区块的内容。

    # demo.json
    
    {
      "navigationBarTitleText": "自定义Swiper组件及特效",
      "usingComponents": {
        "my-swiper": "/component/myswiper/myswiper"
      }
    }
    
    # demo.wxml
    
    <view>
      <view class='other'>
        <image mode='aspectFit' src='./a.png' />
      </view>
    
      <my-swiper tabArr="{{['人物','自然','汽车','美女']}}">
        <view slot='item0' class='content'>人物</view>
        <view slot='item1' class='content'>自然</view>
        <view slot='item2' class='content'>汽车</view>
        <view slot='item3' class='content'>美女</view>
      </my-swiper>
    </view>
    
    # demo.wxss
    
    .other image {
        display: block;
        width: 100%;
    }
    .content {
        font-size: 80rpx;
        text-align: center;
        line-height: 200rpx;
    }
    
    # demo.js
    
    Page({
      data: {},
      onLoad: function() {},
    })
    
    四、总结

    <my-swiper>组件的封装,填平了一些坑,比如 swiper默认的样式问题。通过这个组件,我们至少学到了如下前端知识:

    • 什么是组件?如何自定义组件?
    • 如何使用小程序的原生组件,并修改其默认的样式?
    • 学会使用 slot 插槽,实现组件内容的差异化
    • 学会使用小程序原生 api 获取手机信息,用 js 改变组件样式
    • 学会使用 touch 事件,区分e.touchese.changedTouches
    • 学会pxrpx单位之间的区别和相互转化
    • 学会使用小程序 api 来手动地滚动页面,等。

    以上完整代码,复制黏贴即可实现这个效果,相关要点已经在代码中进行了注释。事实上,要总结的内容还有很多,时间有限就不啰嗦了。好了,本篇就总结到这里。

    往期 相关的小程序demo笔记:


    本篇结束 2019-08-27

    相关文章

      网友评论

        本文标题:自定义组件之 Swiper 特效组件

        本文链接:https://www.haomeiwen.com/subject/xxoyectx.html