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

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

实现效果如下:

页面区域及支持手势

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

原理实现

主要是根据事件系统的事件来自行处理页面应当如何响应,原理其实同原生的差不多。 主要涉及 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;
}
相关推荐
软件聚导航9 小时前
「聚小软 AI 助手」技术升级:从数据库检索到 RAG 知识库问答
前端·数据库·mysql·微信小程序·小程序·ai编程·rag
一叶星殇12 小时前
微信小程序 Vant Popup 滚动穿透:页面级与组件级解决方案详解
微信小程序·小程序
2601_9638699518 小时前
【计算机毕业设计】基于微信小程序的点餐传菜系统设计与实现
微信小程序·小程序·课程设计
北漂燕郊杨哥21 小时前
VS Code 安装微信小程序 MCP 介绍
vscode·微信小程序·小程序·mcp
维双云2 天前
2026微信小程序开店用哪个平台?长期稳定运营的系统选择方法
微信小程序·小程序
辛迪聊物业数字化2 天前
【智能楼宇系统有什么用?赋能园区高效 节能 安全】
经验分享·安全·微信小程序·php
Coodor2 天前
使用web也可以写NFC微信小程序拉取
前端·微信小程序·小程序·nfc拉起小程序
太阳之神aboluo3 天前
微信小程序 (1) 基础用法
微信小程序·小程序
黑马程序员毕设3 天前
基于B/S架构的“指尖乡味”助农电商小程序系统设计与实现
spring boot·微信小程序·小程序·架构·课程设计·毕设
编程小橘子4 天前
基于微信小程序健康心理测试、设计与实现 (源码+lw+参考文档+核心代码讲解等)
微信小程序·小程序·毕业设计·毕设