微信小程序实现上下左右滑动触发联动选项卡、绝对值、事件、parse、stringify、Math、atan、abs、findIndex

news2025/1/10 20:39:08

文章目录

  • 序言
  • 1、HTML部分
  • 2、JavaScript部分(上下左右滑动均触发)
  • 3、JavaScript部分(左右滑动触发)
  • 4、效果演示

序言

最近在写原生微信小程序项目的时候遇到了左右滑动内容更新数据,同时改变tabBar的高亮效果。于是就写了这篇文章,关于文章的css不再此文章中展示,因为都是公共的自定义类名css,所以通过类名大概就能推敲出css的值了。


1、HTML部分

<view class="font_size_36">
  <!-- tabBar选项卡 -->
  <view class="dis_r_c">
    <view class="grid_c3_230 grid_row_gap_10 grid_column_gap_10">
      <view class="border_777" wx:for="{{demoTabBar}}" wx:key="id">
        <view class="height_90 line_height_90 text_align_center {{isH===item.id?'color_royalblue':''}}" data-item="{{item}}" catchtap="catchtapTabBar">{{item.title}}</view>
      </view>
    </view>
  </view>

  <!-- 内容 -->
  <view class="margin_t_26 padding_b_36" bindtouchstart="slideStart" bindtouchmove="slideDirection" bindtouchend="slideEnd">
    <view>
      <view class="border_777 {{index!==0?'margin_t_16':''}}" wx:for="{{list}}" wx:key="id">
        <view class="ellipsis">{{item.title}}</view>
      </view>
    </view>
  </view>
</view>

2、JavaScript部分(上下左右滑动均触发)

第一步:在包裹内容的最外层HTML标签上定义滑动事件。如果页面同时存在横向滑动的选项卡,最好不要把滑动事件绑定到页面的最外层标签上。上下左右滑动均触发的功能只适合那种类似于看小说的页面,因为看小说要么往左滑,要么往右滑,要么往下滑,要么往上滑,要么斜着滑,页面上除了返回按钮,就没有其它复杂的功能了。所以适合这个上下左右滑动均触发的模式。
bindtouchstart="slideStart":开始滑动。
bindtouchmove="slideDirection":滑动事件,只要滑动就会触发。
bindtouchend="slideEnd":滑动结束,即手指离开屏幕时触发。
等号前面是微信小程序提供的绑定事件的属性,等号后面是自定义的事件名。

第二步:在全局引入页面结构数据,并解构。

第三步:data中的变量及其作用。
demoTabBar:选项卡数据。
originObjList:源数据对象,存放选项卡对应的所有数据内容。
isH:使用数据id设置选项卡的高亮值。
list:存储渲染列表的数据,也就是在切换选项卡时获取对应的数据值。
startX:开始滑动的X轴坐标值。
startY:开始滑动的Y轴坐标值。
direction:滑动方向,L向左滑,R向右滑。

第四步:页面初始化,定义一个名为init的函数,并在onLoad生命周期函数中调用init函数来实现页面初始化。在init函数中使用JSONparsestringify方法克隆数据,以保证数据的完全分离状态。接着便是初始化赋值,分别是tabBar赋值,originObjList数据源赋值,这个数据包含所有的tabBar类目的数据值;isH赋值,获取tabBar数据第一项的id值;list赋值,通过tabBar的第一项数据id获取newTabBar中对应的值。

第五步:绑定选项卡的点击事件,事件名称为catchtapTabBar。在选项卡标签上自定名为item的变量来向函数传递值。在函数中通过解构获取点击项的值,获取点击项中的id与源数据匹配选项卡的值,最后把得到的值和id值赋给data中的listisH两个变量。

第六步:绑定开始滑动事件,事件名称为slideStart。在函数中通过event获取开始滑动的clientXclientY的值。最后把值分别赋给data中的startXstartY,这个值也就是开始滑动的坐标值。

第七步:绑定滑动中的事件,事件名称为slideDirection。在函数中通过event获取实时滑动的坐标值,使用Math中的atanPI两个方法计算角度,通过角度的变化确定滑动的方向。Math.abs(angle) > 5050这个值如果越大,方向就越不容易被改变,值越大说明需要滑动更远的距离才能改变方向,同时把方向值赋给data中的direction变量等待滑动结束事件使用。

第八步:绑定滑动结束事件,定义事件名为slideEnd。在函数中准备好数据,首先判断方向,方向分别使用LR表示,也就是在滑动函数中改变的方向值。L表示向右滑相当于上一页,R表示向左滑相当于下一页。当方向值为L的时候需要i < tabBarLen - 1,当方向值为R的时候需要i > 0。其中tabBarLen - 1表示已经滑动到tabBar的最后一项,不能再向左滑动了。i > 0表示已经滑到tabBar的第一项,不能再向右滑动了。其中i的值通过findIndex方法和isH获取当前对应tabBar中的下标值,tabBarLen的值就是tabBar数据的长度。如果if判断成立,那么就获取对应的内容赋值给list,并且把isH的值同时更新为当前值。如果if条件不成立,那么就给datadriection变量赋值为空字符串。

const {
  globalData: {
    jsonData: {
      tabBar,
      objList
    },
    showToast
  }
} = getApp();

Page({

  /**
   * 页面的初始数据
   */
  data: {
    demoTabBar: [],
    originObjList: [],
    isH: '',
    list: [],
    startX: 0,
    startY: 0,
    direction: null
  },
  
  // 滑动结束
  slideEnd() {
    let self = this,
      selfData = self.data,
      direction = selfData.direction,
      tabBar = selfData.demoTabBar,
      tabBarLen = tabBar.length,
      originObjList = selfData.originObjList,
      isH = selfData.isH,
      i = tabBar.findIndex(item => item.id === isH),
      id = '';

    if (direction === 'R' && i > 0) { // 向右滑相当于上一页
      i -= 1;
    } else if (direction === 'L' && i < tabBarLen - 1) { // 向左滑相当于下一页
      i += 1;
    } else { // 滑动不成立,清空driection
      return self.setData({
        direction: ''
      });
    }
    
    id = tabBar[i].id;

    self.setData({
      list: originObjList[id],
      isH: id
    });
  },

  // 滑动中判断滑动方向
  slideDirection({ changedTouches }) {
    let self = this,
      selfData = self.data,
      // 开始x坐标
      startX = selfData.startX,
      // 开始y坐标
      startY = selfData.startY,
      // 活动变化坐标
      touchMoveX = changedTouches[0].clientX,
      // 滑动变化坐标
      touchMoveY = changedTouches[0].clientY,
      direction = '',
      _x = touchMoveX - startX,
      _Y = touchMoveY - startY,
      // Math.atan()返回数据的反正切值
      // 这里算的就是角度
      angle = 360 * Math.atan(_Y / _X) / (2 * Math.PI);

    // 滑动角度超过50retrun
    if (Math.abs(angle) > 50) return;

    if (touchMoveX > startX) { // 向右滑
      direction = 'R';
    } else { // 向左滑
      direction = 'L';
    }

    self.setData({
      direction
    });
  },

  // 开始滑动 
  slideStart(e) {
    this.setData({
      startX: e.changedTouches[0].clientX,
      startY: e.changedTouches[0].clientY
    })
  },
  
  // tabBar选项卡
  catchtapTabBar({ currentTarget: { dataset: { item } } }) {
    let self = this,
      selfData = self.data,
      originObjList = selfData.originObjList;

    self.setData({
      list: originObjList[item.id],
      isH: item.id
    });
  },

  // 初始化
  init() {
    let newTabBar = JSON.parse(JSON.stringify(tabBar)),
      originObjList = JSON.parse(JSON.stringify(objList)),
      id = newTabBar[0].id;

    this.setData({
      demoTabBar: newTabBar,
      originObjList,
      isH: id,
      list: originObjList[id]
    });
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad(options) {
    this.init();
  },
})

3、JavaScript部分(左右滑动触发)

第一步:这步与上下左右滑动均触发案例的区别就是没有了滑动事件,因为不需要使用滑动事件。

第二步:这步与上下左右滑动均触发案例一样。

第三步:这步data中的变量与上下左右滑动均触发案例相比少了startXstartYdirection三个变量。同时增加了startPageX变量,这个变量用来记录滑动开始的pageX值,这次获取的不再是clientX的值。

第四步:页面初始化与上下左右滑动均触发案例相同。

第五步:绑定选项卡的点击事件与上下左右滑动均触发案例相同。

第六步:绑定开始滑动事件,事件名称为slideStart。在函数中通过event获取开始滑动的pageX值,通过parseInt方法舍弃小数部分(这步不是必须,只是个人偏好)。最终把值赋给data中的startPageX变量。

第七步:绑定滑动结束事件,定义事件名为slideEnd。在函数中准备好数据,itabBarLen变量值的获取和作用与上下左右滑动均触发案例相同,这里不再累赘解释。定义endPageXslidingDistancedirection三个变量,endPageX变量通过解构获取pageX赋值,也就是滑动结束的值,同样需要parseInt方法舍弃小数部分(这步不是必须,只是个人偏好)。slidingDistance变量存储endPageX(滑动结束值)和startPageX(滑动开始值)的差值。direction变量存储方向值,这个方向值是通过获取slidingDistance值的第一个字符决定,如果第一个字符是减号,说明向左滑动,否则向右滑动。if (Math.abs(slidingDistance) < 100) return false;通过Mathabs方法获取slidingDistance变量的绝对值与100比较,如果slidingDistance的值小于100,说明滑动距离太短,通过return中断函数,后续代码不再执行。如果slidingDistance的值大于100,后续代码继续执行,执行逻辑与上下左右滑动均触发案例的第八步类似。

const {
  globalData: {
    jsonData: {
      tabBar,
      objList
    },
    showToast
  }
} = getApp();

Page({

  /**
   * 页面的初始数据
   */
  data: {
    demoTabBar: [],
    originObjList: [],
    isH: '',
    list: [],
    startPageX: 0
  },

  // 开始滑动 
  slideStart({ changedTouches }) {
    let pageX = changedTouches[0].pageX;

    pageX = parseInt(pageX);

    this.setData({
      startPageX: pageX
    });
  },

  // 滑动结束
  slideEnd({ changedTouches }) {
    let self = this,
      selfData = self.data,
      endPageX = changedTouches[0].pageX,
      tabBar = selfData.demoTabBar,
      tabBarLen = tabBar.length,
      originObjList = selfData.originObjList,
      isH = selfData.isH,
      startPageX = selfData.startPageX,
      i = tabBar.findIndex(item => item.id === isH),
      id = '',
      slidingDistance = 0,
      direction = '';

    // 滑动结束,并舍弃小数点
    endPageX = parseInt(endPageX);
    // 滑动距离
    slidingDistance = endPageX - startPageX;
    // 滑动方向
    direction = String(slidingDistance)[0];

    if (!Math.abs(slidingDistance) > 100) return false;

    if (direction === '-' && i < tabBarLen - 1) { // 向左滑动
      i += 1;
    } else if (direction !== '-' && i > 0) { // 向右滑动
      i -= 1;
    } else {
      return false;
    }

    id = tabBar[i].id;

    self.setData({
      list: originObjList[id],
      isH: id
    });
  },

  // tabBar
  catchtapTabBar({ currentTarget: { dataset: { item } } }) {
    let self = this,
      selfData = self.data,
      originObjList = selfData.originObjList;

    self.setData({
      list: originObjList[item.id],
      isH: item.id
    });
  },

  // 初始化
  init() {
    let newTabBar = JSON.parse(JSON.stringify(tabBar)),
      originObjList = JSON.parse(JSON.stringify(objList)),
      id = newTabBar[0].id;

    this.setData({
      demoTabBar: newTabBar,
      originObjList,
      isH: id,
      list: originObjList[id]
    });
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad(options) {
    this.init();
  }
})

4、效果演示

X2_1_5s

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/146408.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

B. Camp Schedule(KMPnext数组)

Problem - 1137B - Codeforces 在全国范围内广为人知的春季编程训练营即将开始。因此&#xff0c;所有友好的策展人和教师团队开始组成营地的时间表。经过不断的讨论&#xff0c;他们想出了一个时间表&#xff0c;可以表示为一个二进制字符串&#xff0c;其中第i个符号是 "…

前后端的身份认证

1、Web 开发模式 目前主流的 Web 开发模式有两种&#xff0c;分别是&#xff1a; 基于服务端渲染的传统 Web 开发模式基于前后端分离的新型 Web 开发模式 1.1、服务端渲染的 Web 开发模式 服务端渲染的概念&#xff1a;服务器发送给客户端的 HTML 页面&#xff0c;是在服务器…

【Linux】进程间通信(万字详解) —— 下篇

&#x1f387;Linux&#xff1a; 博客主页&#xff1a;一起去看日落吗分享博主的在Linux中学习到的知识和遇到的问题博主的能力有限&#xff0c;出现错误希望大家不吝赐教分享给大家一句我很喜欢的话&#xff1a; 看似不起波澜的日复一日&#xff0c;一定会在某一天让你看见坚持…

我的周刊(第073期)

我的信息周刊&#xff0c;记录这周我看到的有价值的信息&#xff0c;主要针对计算机领域&#xff0c;内容主题极大程度被我个人喜好主导。这个项目核心目的在于记录让自己有印象的信息做一个留存以及共享。&#x1f3af; 项目zlib-searcher[1]zlib 开源搜索方案&#xff08;zli…

基于朴素贝叶斯算法的激光雷达点云分类

前言激光雷达技术是一种采集三维数据的、重建三维模型的手段&#xff0c;运用在各个行业&#xff0c;随着激光雷达技术的发展与广泛运用本文采用监督分类中的朴素贝叶斯算法进行地基于激光雷达的地物分类。首先根据点云的几何位置建立邻域范围&#xff0c;借助邻域点的集合计算…

分享98个PHP源码,总有一款适合您

PHP源码 分享98个PHP源码&#xff0c;总有一款适合您 PHP源码下载链接&#xff1a;https://pan.baidu.com/s/1ZNcdj0bLY51UXNoXq8tgFg?pwdwn4b 提取码&#xff1a;wn4b 下面是文件的名字&#xff0c;我放了一些图片&#xff0c;文章里不是所有的图主要是放不下...&#xff0…

ORB-SLAM2 --- LoopClosing::ComputeSim3 函数

目录 1.什么是sim3&#xff0c;为什么要做sim3 2.函数流程 3.code 4.函数解析 4.1 准备工作 4.2 遍历闭环候选帧集&#xff0c;初步筛选出与当前关键帧的匹配特征点数大于20的候选帧集合&#xff0c;并为每一个候选帧构造一个Sim3Solver 4.3 对每一个候选帧用Sim3Sol…

实例分析Linux内存泄漏检测方法

一、mtrace分析内存泄露 mtrace&#xff08;memory trace&#xff09;&#xff0c;是 GNU Glibc 自带的内存问题检测工具&#xff0c;它可以用来协助定位内存泄露问题。它的实现源码在glibc源码的malloc目录下&#xff0c;其基本设计原理为设计一个函数 void mtrace ()&#x…

解决Tinkphp的success跳转“使用路由别名后模块和路由器访问不了”问题

遇到的问题&#xff1a;我的thinkphp5网站添加了以下路由别名&#xff1a;Route::alias([ index>index/index, ]);使用http://域名/Index/user/password.html访问正常但使用http://域名/index/user/password.html就访问失败使用$this->success(修改密码成功);进行提示跳转…

Java 链表与LinkedList

链表的组合形式 ①有头结点、无头结点 ②单向链表、双向链表 ③循环链表、非循环链表 根据自由组合&#xff0c;可以得到8种不同形式的链表&#xff0c;那么在刷题种常碰到的是不带有头结点的单向非循环链表和不带头结点的双向非循环链表。 模拟实现不带头结点的单向非循环链表…

Notes可以手动签名了

大家好&#xff0c;才是真的好。 Notes/Domino 12.0.2陆续有人下载测试了&#xff0c;关于Notes的新功能中&#xff0c;我们上一篇也介绍到了可以手动签名。 字面上的意思&#xff0c;就是你可以调出手写板&#xff0c;然后使用触屏或鼠标来进行签名&#xff0c;可以在Nomad …

javaEE 初阶 — 定时器

文章目录定时器1 什么是定时器2 标准库中定时器3 实现一个定时器3.1 实现的思路3.2 为什么要使用优先级队列来保存任务3.3 开始实现定时器 1 什么是定时器 定时器 类似于一个 “闹钟”&#xff0c;达到一个设定的时间之后&#xff0c;就执行某个指定好的代码。 定时器是一种实…

印染行业APS智能排程排产的应用意义

不得不说的印染之“痛” 在印染行业&#xff0c;因排产无法自动化、智能化&#xff0c;企业在交期、成本、生产管理方面承受着巨大的压力&#xff0c;尤其当下印染企业生产管理正从传统的粗放式转向精细化&#xff0c;这些痛点愈加凸显。 一方面&#xff0c;客户和企业面临一个…

httpd安装

一、离线安装 1、去 https://pkgs.org/ 下载httpd所依赖的7个rpm包 [基于CentOS 7 x86_64系统&#xff0c;如需其他环境可前往官网直接下载] apr-1.4.8-5.el7.x86_64.rpm apr-util-1.5.2-6.el7.x86_64.rpm apr-util-ldap-1.5.2-6.el7.x86_64.rpm postgresql-libs-9.2.24-1.el…

互联互通-标准化成熟度指标分析(未完成)

整体分析1 医疗机构基本情况2 数据资源标准化建设情况&#xff08;30 分&#xff09;2.1数据集标准化情况&#xff08;15 分&#xff09;2.1.1电子病历基本数据集 第1部分&#xff1a;病历概要&#xff08;1-4数据集&#xff09;2.1.2电子病历基本数据集 第2部分&#xff1a;门…

Jetpack Compose UI创建布局绘制流程+原理 —— 内含概念详解(手撕源码)

本文是我去年首发于稀土掘金平台的文章 全文较长&#xff1a;共1万5千字&#xff0c;适合有耐心❤️的人学习 有些概念不懂的可以去4.部分概念详解这个目录先稍微学习一下 Compose源码基于最新的Compose 版本&#xff1a;1.0.1 系统源码基于最新的Android11 版本 注意&#xff…

【蓝桥杯基础题】2020年省赛填空题—回文日期

&#x1f451;专栏内容&#xff1a;蓝桥杯刷题⛪个人主页&#xff1a;子夜的星的主页&#x1f495;座右铭&#xff1a;前路未远&#xff0c;步履不停 目录一、题目背景二、题目描述1.问题描述2.输入格式3.输出格式4.一个例子5. 评测用例规模与约定三、题目分析1.获取位数2.回文…

236页10万字精选数据中台建设方案2022版

【版权声明】本资料来源网络&#xff0c;知识分享&#xff0c;仅供个人学习&#xff0c;请勿商用。【侵删致歉】如有侵权请联系小编&#xff0c;将在收到信息后第一时间删除&#xff01;完整资料领取见文末&#xff0c;部分资料内容&#xff1a; 目录 1. 数据中台平台建设方案 …

数据存储大小端 网络字节序

一、概念 大端模式&#xff1a;数据的低位存放在内存的高地址中 小端模式&#xff1a;数据的低位存放在内存的低地址中 二、数据的高低位 首先需要清楚一段数据存储高低位区分 联想记忆最右边为16^0 下来16^1 比如下图二进制为例&#xff1a; 三、内存的高低位 以vs2019为…

JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配

文章目录前言一、排序规范1、happens-before原则2、找文档位置二、对象创建的过程&#xff08;后面回答的就是这几个问题&#xff09;1、一线互联网企业面试题&#xff1a; 关于对象2、对象创建过程三、对象在内存中的存储布局1、1.8版本虚拟机配置2、对象的内存布局a、普通对象…