小程序手势冲突做不了?不存在的!

原生的应用经常会有页面嵌套列表,滚动列表能够改变列表大小,然后还能支持列表内下拉刷新等功能。看了很多的小程序好像都没有这个功能,难道这个算是原生独享的吗,难道是由于手势冲突无法实现吗,冷静的思考了一下,又看了看小程序的手势文档(文档地址),感觉我又行了。

实现效果如下:

页面区域及支持手势

  • 红色的是列表未展开时内容展示,无手势支持
  • 绿色部分是控制部分,支持上拉下拉手势,对应展开列表及收起列表
  • 蓝色列表部分,支持上拉下拉手势,对应展开列表,上拉下拉刷新等功能
  • 浅蓝色部分是展开列表后的小界面内容展示,无手势支持

原理实现

主要是根据事件系统的事件来自行处理页面应当如何响应,原理其实同原生的差不多。 主要涉及 touchstart、touchmove、touchend、touchcancel 四个

另外的scrollview的手势要借助于 scroll-y、refresher-enable 属性来实现。

之后便是稀疏平常的数学加减法计算题环节。根据不同的内容点击计算页面应当如何绘制显示。具体的还是看代码吧,解释起来又要吧啦吧啦了。

Talk is cheap, show me the code

代码部分

wxml

html 复制代码
<!--index.wxml-->
<view>
  <view class="header" style="opacity: {{headerOpacity}};height:{{headerHeight}}px;"></view>
  <view 
    class="toolbar"
    data-type="toolbar"
    style="bottom: {{scrollHeight}}px;height:{{toolbarHeight}}px;"
    catch:touchstart="handleToolbarTouchStart"
    catch:touchmove="handleToolbarTouchMove"
    catch:touchend="handleToolbarTouchEnd"
    catch:touchcancel="handleToolbarTouchEnd"></view>
  <scroll-view 
    class="scrollarea" 
    type="list"
    scroll-y="{{scrollAble}}"
    refresher-enabled="{{scrollAble}}"
    style="height: {{scrollHeight}}px;"
    bind:touchstart="handleToolbarTouchStart"
    bind:touchmove="handleToolbarTouchMove"
    bind:touchend="handleToolbarTouchEnd"
    bind:touchcancel="handleToolbarTouchEnd"
    bindrefresherrefresh="handleRefesh"
    refresher-triggered="{{refreshing}}"
    >
    <view class="item" wx:for="{{[1,2,3,4,5,6,7,8,9,0,1,1,1,1,1,1,1]}}">
      
    </view>
  </scroll-view>

  <view 
    class="mini-header"
    style="height:{{miniHeaderHeight}}px;"
    wx:if="{{showMiniHeader}}">
    
  </view>
</view>

ts

typescript 复制代码
// index.ts
// 获取应用实例
const app = getApp<IAppOption>()

Component({
  data: {
    headerOpacity: 1,
    scrollHeight: 500,
    windowHeight: 1000,
    isLayouting: false,
    showMiniHeader: false,
    scrollAble: false,
    refreshing: false,
    toolbarHeight: 100,
    headerHeight: 400,
    miniHeaderHeight: 200,
    animationInterval: 20,
    scrollviewStartY: 0,
  },
  methods: {
    onLoad() {
      let info = wx.getSystemInfoSync()
      this.data.windowHeight = info.windowHeight
      this.setData({
        scrollHeight: info.windowHeight - this.data.headerHeight - this.data.toolbarHeight
      })
    },
    handleToolbarTouchStart(event) {
      this.data.isLayouting = true
      let type = event.currentTarget.dataset.type
      if (type == 'toolbar') {

      } else {
        this.data.scrollviewStartY = event.touches[0].clientY 
      }
    },
    handleToolbarTouchEnd(event) {
      this.data.isLayouting = false
      
      let top = this.data.windowHeight - this.data.scrollHeight - this.data.miniHeaderHeight - this.data.toolbarHeight
      if (top > (this.data.headerHeight - this.data.miniHeaderHeight) / 2) {
        this.tween(this.data.windowHeight - this.data.scrollHeight, this.data.headerHeight + this.data.toolbarHeight, 200)
      } else {
        this.tween(this.data.windowHeight - this.data.scrollHeight, this.data.miniHeaderHeight + this.data.toolbarHeight, 200)
      }
    },
    handleToolbarTouchMove(event) {
      if (this.data.isLayouting) {
        let type = event.currentTarget.dataset.type
        if (type=='toolbar') {
          this.updateLayout(event.touches[0].clientY + this.data.toolbarHeight / 2)
        } else {
          if (this.data.scrollAble) {
            return
          } else {
            this.updateScrollViewLayout(event.touches[0].clientY)
          }
        }
      }
    },
    handleRefesh() {
      let that = this
      setTimeout(() => {
        that.setData({
          refreshing: false
        })
      }, 3000);
    },
    updateLayout(top: number) {
      if (top < this.data.miniHeaderHeight + this.data.toolbarHeight) {
        top = this.data.miniHeaderHeight + this.data.toolbarHeight
      } else if (top > this.data.headerHeight + this.data.toolbarHeight) {
        top = this.data.headerHeight + this.data.toolbarHeight
      }
      let opacity = (top - (this.data.miniHeaderHeight + this.data.toolbarHeight)) / (this.data.miniHeaderHeight + this.data.toolbarHeight)
      let isReachTop = opacity == 0 ? true : false
      this.setData({
        scrollHeight: this.data.windowHeight - top,
        headerOpacity: opacity,
        showMiniHeader: isReachTop,
        scrollAble: isReachTop
      })
    },
    updateScrollViewLayout(offsetY: number) {
      let delta = offsetY - this.data.scrollviewStartY
      if (delta > 0) {
        return
      }
      delta = -delta
      if (delta > this.data.headerHeight - this.data.miniHeaderHeight) {
        delta = this.data.headerHeight - this.data.miniHeaderHeight
      }
      
      let opacity = 1 - (delta) / (this.data.headerHeight - this.data.miniHeaderHeight)
      let isReachTop = opacity == 0 ? true : false
      this.setData({
        scrollHeight: this.data.windowHeight - this.data.headerHeight - this.data.toolbarHeight + delta,
        headerOpacity: opacity,
        showMiniHeader: isReachTop,
        scrollAble: isReachTop
      })
    },
    tween(from: number, to: number, duration: number) {
      let interval = this.data.animationInterval
      let count = duration / interval
      let delta = (to-from) / count
      this.tweenUpdate(count, delta, from)
    },
    tweenUpdate(count: number, delta: number, from: number) {
      let interval = this.data.animationInterval
      let that = this
      setTimeout(() => {
        that.updateLayout(from + delta)
        if (count >= 0) {
          that.tweenUpdate(count-1, delta, from + delta)
        }
      }, interval);
    }
  },
})

less

less 复制代码
/**index.less**/
.header {
  height: 400px;
  background-color: red;
}
.scrollarea {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: blue;
}
.toolbar {
  height: 100px;
  position: fixed;
  left: 0;
  right: 0;
  background-color: green;
}
.mini-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 200px;
  background-color: cyan;
}
.item {
  width: 670rpx;
  height: 200rpx;
  background-color: yellow;
  margin: 40rpx;
}
相关推荐
QQ_216962909620 小时前
基于微信小程序的智能膳食分析系统
java·大数据·spring boot·微信小程序·小程序·旅游
QQ_216962909620 小时前
基于C#(Asp.net)电竞陪玩信息管理系统的设计与实现
java·大数据·spring boot·微信小程序·c#·云计算·asp.net
m0_462803882 天前
名单制签到怎么落地:拆解“仅名单内人员签到“的配置链路与匹配逻辑
微信小程序
卓怡学长3 天前
w192基于springboot“考研情报站”微信小程序设计与实现
java·spring boot·spring·微信小程序·intellij-idea
Q26433650235 天前
【有源码】基于uni-app的旅游行程规划小程序-基于微信小程序的智慧文旅综合服务平台
java·微信小程序·小程序·uni-app·毕业设计·springboot·源代码
网硕互联的小客服5 天前
微信小程序多种跳转页面方式教学
微信小程序·小程序·notepad++
m0_462803885 天前
会议签到里,手写签名怎样从“额外动作“变成流程环节
微信小程序
醒目806 天前
从图像到拼豆图纸:像素化生成与色板匹配技术实践
微信小程序
愚公搬代码7 天前
【愚公系列】《微信小程序项目实战(AI编程+视频图解)》002-一张新地图:AI创业的核心分析框架
人工智能·微信小程序·ai编程
ZeroNews内网穿透7 天前
企业内网穿透别再用随机域名!自有域名实现 OA/ERP 公网安全访问
微信小程序·内网穿透·erp·zeronews·运维实战·企业oa·自有域名