移动端滑动(touch)选项并实现多选效果

  • 移动端滑动选项实现多选效果
  • 通过 touchstarttouchmovetouchendtouchcancel 事件实现
  • 通过父元素代理事件的方式实现子组件点击选中选项
  • 如果选项添加 disabled 属性将不会被选中
  • 移动端拖拽 .box.options 元素时,是有拖拽效果的,去除拖拽效果有两种方式:
    ① 用 css pointer-events: none; 阻止拖拽,.box 元素使用的是这种方式
    ② 用 js event.preventDefault() 阻止元素的事件, .options 元素使用的是这种方式
  • 如果需要通过鼠标滚轮来切换选项,可以使用 wheel 来实现,本案例没有实现
  • touchcancel 事件一直没有办法触发,所以没有验证效果

在这里插入图片描述

  • css

    * {
      margin: 0;
      padding: 0;
    }
    
    ul,
    li {
      list-style: none;
    }
    
    .box {
      height: 300px;
      width: 400px;
      margin: 50px auto;
      border: 1px solid #000;
      border-radius: 12px;
      position: relative;
      background-color: #f5f5f5;
      touch-action: none; /* 元素不能滑动 */
    }
    
    .box .columns {
      overflow: hidden;
      height: 100%;
    }
    
    .box .columns .options {
      transition-timing-function: cubic-bezier(0.23, 1, 0.68, 1);
      transition-duration: 0ms;
      transition-property: all;
      height: 100%;
    }
    
    .box .columns .options li {
      height: 60px;
      line-height: 60px;
      text-align: center;
      display: flex;
      justify-content: space-between;
      padding: 0 15px;
    }
    
    .box .columns .options li[disabled] {
      cursor: not-allowed;
      opacity: 0.3;
    }
    
    .box .columns .options li.selected {
      font-weight: bold;
    }
    
    .box .columns .options li.selected::after {
      content: '√';
      font-size: 16px;
      color: red;
    }
    
  • html

    <div class="box">
      <div class="columns">
        <ul class="options">
          <li data-index="0">选项一</li>
          <li disabled data-index="1">选项2</li>
          <li data-index="2">选项3</li>
          <li data-index="3">选项4</li>
          <li data-index="4">选项5</li>
          <li data-index="5">选项6</li>
          <li data-index="6">选项7</li>
          <li data-index="7">选项8</li>
          <li data-index="8">选项9</li>
        </ul>
      </div>
    </div>
    
  • javascript

    const boxEl = document.querySelector('.box')
    const optionsEl = document.querySelector('.options')
    const columnsEl = document.querySelector('.columns')
    const liFirstEl = document.querySelector('.options li:first-child')
    
    const boxHeight = boxEl.clientHeight
    const liHeight = liFirstEl.offsetHeight
    const liAllEl = optionsEl.querySelectorAll('li')
    const liCount = liAllEl.length
    const boxContainsLi = Math.floor(boxHeight / liHeight)
    
    let startY = 0
    let endY = 0
    let currentY = 0 // 当前Y轴移动的位置
    let isTouch = false // 是否是滑动事件
    const initPositionY = 0 // 进入页面时Y轴开始的位置
    let beginPositionY = initPositionY // 每次滚动后Y轴开始的位置
    const maxTranslateY = liHeight / 2 // Y轴最大移动距离(向下移动)
    const minTranslateY =
      liCount * liHeight > boxHeight
        ? -(liCount * liHeight - boxHeight + liHeight / 2)
        : -liHeight / 2 // Y轴最小移动距离(向上移动)
    
    ;(function () {
      // 初始化页面信息
      optionsEl.setAttribute(
        'style',
        `transition-duration: 0ms; transition-property: all;`
      )
      setTransform(beginPositionY, optionsEl)
    })()
    
    // 设置元素在Y轴的移动距离
    function setTransform(value, el) {
      el.style.transform = `translate3d(0px, ${value}px, 0px)`
    }
    
    // 获取元素在Y轴的移动距离
    function getTranslateY(el) {
      const translateStr = el.style.transform
      const valueStr = translateStr.match(/\(([^)]*)\)/)
      const valueArr = valueStr[1].split(',')
      return Number(valueArr[1].replace('px', ''))
    }
    
    // 给元素设置动画
    function setTransitionDuration(value, el) {
      el.style.transitionDuration = `${value}ms`
      el.style.transitionProperty = value === 0 ? 'none' : 'all'
    }
    
    // 重置动画
    function setTransitionNone(el) {
      setTimeout(() => {
        setTransitionDuration(0, el)
      }, 300)
    }
    
    // 设置 class
    function setClassName(index, elList) {
      elList[index].classList.toggle('selected')
    }
    
    // 阻止父元素的滑动
    columnsEl.addEventListener(
      'touchmove',
      function (e) {
        e.preventDefault()
      },
      false
    )
    
    // 父元素代理子元素点击事件
    columnsEl.addEventListener(
      'click',
      function (e) {
        console.log('click 事件触发')
    
        const target = e.target
        let index = 0
    
        // 方案一:遍历所有 li 得到 index
        // for (let i = 0; i < liCount; i++) {
        //   if (liAllEl[i] === target) {
        //     index = i
        //     break
        //   }
        // }
    
        // 方案二: 根据 data-index 获取当前 li 的index
        index = target.dataset.index
    
        if (liAllEl[index].hasAttribute('disabled')) {
          return false
        }
    
        setClassName(index, liAllEl)
      },
      false
    )
    
    // 开始滑动
    optionsEl.addEventListener(
      'touchstart',
      function (e) {
        console.log('touchstart 事件触发')
        startY = event.targetTouches[0].pageY
      },
      false
    )
    
    // 滑动中
    optionsEl.addEventListener(
      'touchmove',
      function (e) {
        endY = event.targetTouches[0].pageY
        let moveY = endY - startY
        if (moveY !== 0) {
          isTouch = true
        }
        console.log('touchmove 事件触发')
        currentY = beginPositionY + moveY
    
        // Y 轴位置大于最大位置
        if (currentY > maxTranslateY) {
          currentY = maxTranslateY
        } else if (currentY < minTranslateY) {
          currentY = minTranslateY
        }
        setTransform(currentY, optionsEl)
      },
      false
    )
    
    function touchend(e) {
      if (!isTouch) {
        return false
      }
      console.log('touchend 事件触发')
      setTransitionDuration(200, optionsEl)
    
      // 超过头部与底部位置
      if (currentY >= maxTranslateY) {
        currentY = initPositionY
      } else if (currentY <= minTranslateY) {
        currentY = currentY + liHeight / 2
      } else {
        let index
    
        // 根据不同的方向,回正所要选中的选项
        if (endY - startY > 0) {
          // 向下滚动
          index = Math.floor((initPositionY - currentY) / liHeight)
        } else {
          // 向上滚动
          index = Math.ceil((initPositionY - currentY) / liHeight)
        }
        index =
          index <= 0
            ? 0
            : index >= liCount - boxContainsLi
            ? liCount - boxContainsLi
            : index
    
        currentY = initPositionY - liHeight * index
        isTouch = false
      }
      setTransform(currentY, optionsEl)
      beginPositionY = currentY
      setTransitionNone(optionsEl)
    }
    // 滑动结束
    optionsEl.addEventListener('touchend', touchend, false)
    
    // 滑动取消
    optionsEl.addEventListener(
      'touchcancel',
      (evt) => {
        evt.preventDefault()
        console.log('touchcancel 事件触发')
        touchend(evt)
      },
      false
    )
    

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

文件操作-C语言实现图片、压缩包等文件的“复制粘贴“过程

大部分参考自&#xff1a; 文件操作-C语言实现图片的“复制粘贴“过程_一个图像一部分复制到另一个图像中c语言_philippe coutinho的博客-CSDN博客 #define _CRT_SECURE_NO_WARNINGS的作用参考&#xff1a; https://mp.csdn.net/mp_blog/creation/editor/new/129414996 首先我们…

线程池的优点

线程池的优点&#x1f50e;优点1(降低资源消耗)&#x1f50e;优点2(提高响应速度)&#x1f50e;优点3(可管理性)&#x1f50e;结尾&#x1f50e;优点1(降低资源消耗) 有了线程池后,创建线程不再是向系统申请,而是从线程池中拿 当线程不再使用后,再还给线程池 线程的创建,虽然相…

47了解公有云平台 GCP 的基本服务和使用方法,包括 Compute Engine、Cloud Storage

GCP Compute Engine Google Cloud Platform (GCP) 的 Compute Engine 是一个可扩展的云计算平台&#xff0c;可以让您快速启动虚拟机实例来运行您的应用程序。它提供了一种灵活的方式来管理您的计算资源&#xff0c;并支持多种操作系统、应用程序框架和开发工具。以下是一些基本…

Leetcode.939 最小面积矩形

题目链接 Leetcode.939 最小面积矩形 Rating &#xff1a; 1752 题目描述 给定在 xy平面上的一组点&#xff0c;确定由这些点组成的矩形的最小面积&#xff0c;其中矩形的边平行于 x 轴和 y 轴。 如果没有任何矩形&#xff0c;就返回 0。 示例 1&#xff1a; 输入&#xff1…

centos7安装rabbitmq服务

centos7安装rabbitmq服务 第一 软件包准备 1.erlang依赖包 2.rabbitmq安装包 第二 安装rabbitmq 1.安装依赖 rpm -ivh erlang-21.3-1.el7.x86_64.rpmyum install socat -y2.安装rabbitmq服务 rpm -ivh rabbitmq-server-3.8.8-1.el7.noarch.rpm3.启动rabbitmq服务 system…

一次线上MySQL vCPU飙升引发的思考

vCPU飙升 在一个漆黑的深夜&#xff0c;MySQL丛库的vCPU在做一个三点任务的时候突然飙升&#xff0c;从MySQL面板中可以查到是以下查询导致的。 表数据及相关索引说明&#xff1a; hotel_info_tbl: 数据量&#xff1a;100w&#xff0c;id 为 primary keydynamic_cache_task_…

二项式反演

二项式反演 在很多情况下&#xff0c;“恰好”往往是不好求的&#xff0c;因为恰好意味着"≤\leq≤"并且"≥\geq≥"&#xff0c;需要进行很多限制&#xff0c;破坏了情况之间的独立性。 二项式反演则通过一定手段&#xff0c;使得限制"≤\leq≤&quo…

谷粒商城笔记+踩坑(21)——提交订单。原子性验令牌+锁定库存

目录 1、环境准备 1.1、业务流程 1.2、Controller 层编写下单功能接口 1.3、订单提交的模型类 1.4、前端页面 confirm.html 提供数据 2、提交订单业务完整代码 3、原子性验令牌&#xff1a;令牌的对比和删除保证原子性 4、初始化新订单&#xff0c;包含订单、订单项等信…

C++ : C++基础 :从内存的角度看 char[]和char*

char*和char[]区别1&#xff1a;数据在内存中的存储2&#xff1a;char*和 char[]分析3&#xff1a;char* p2 和 char p1[]3.1 修改指针所指向的地址4: string转char*5: char * 转string5.1 to_string()用法1&#xff1a;数据在内存中的存储 栈&#xff1a;就是在那些由编译器在…

PYQT 自带的 Pyrcc 系统的使用,PyInstaller对PYQT程序进行打包,不能打包背景图片,图标等解决办法

问题 使用 PyInstaller 对程序进行打包&#xff0c;不能打包背景图片。打包后的软件可以正常运行&#xff0c;但涉及到图片相关的资源全部不显示。 问题分析 当使用Python PyInstaller对程序进行打包时&#xff0c;如果程序中涉及到背景图片&#xff0c;会出现无法打包背景图…

第十一章 指针

第十一章 指针 目录一&#xff0e; 指针变量二&#xff0e; 取地址运算符和间接寻址运算符三&#xff0e; 指针赋值一&#xff0e; 指针变量 概述   指针就是地址&#xff0c;而指针变量就是存储地址的变量。指针的大小都是相同的。32位机器一个地址是4个byte。64位机器一个…

【ChatGPT】这是一篇ChatGPT写的关于Python的文章

文章目录Python基础语法教学1、变量2、数据类型3、运算符4、条件语句5、循环语句更高级的概念1、函数2、模块3、面向对象编程ChatGPT的记录Python基础语法教学 Python是一种高级编程语言&#xff0c;它被广泛应用于计算机科学领域、数据分析和人工智能等各种领域。在学习Pytho…

聊聊MyBatis缓存机制(一)

前言 Mybatis是常见的Java数据库访问层框架&#xff0c;虽然我们在日常的开发中一般都是使用Mybatis Plus&#xff0c;但是从官网信息可以知道&#xff0c;其实Mybatis Plus只是让开发者在使用上更简单&#xff0c;并没有改动核心原理。在日常工作中&#xff0c;大多数开发者都…

HTML5 <!DOCTYPE> 标签

实例 <!DOCTYPE> 声明非常重要&#xff0c;它是一种标准通用标记语言的文档类型声明&#xff0c;通过该标签&#xff0c;浏览器能够了解HTML5文档正在使用的HTML规范&#xff0c;<!DOCTYPE> 声明是HTML5文档的起始点&#xff0c;也就是说它必须位于HTML5文档的第一…

《SpringBoot》第03章 自动配置机制(二) 根注解@SpringBootApplication

前言 之前介绍到了把启动类封装成BeanDefinition注入进IOC容器&#xff0c;那么这个启动类就会跟普通的bean一样在refresh()中被实例化&#xff0c;那么显而易见作为启动类这个实例化并不简单&#xff0c;肯定会存在一些特殊处理&#xff0c;那么就需要研究一下其注解SpringBo…

AI只会淘汰不进步的程序员

最近AI界的大新闻有点多&#xff0c;属于多到每天很努力都追不上&#xff0c;每天都忙着体验各种新产品或申请试用新产品。各种自媒体肯定也不会放过这个机会&#xff0c;AI取代程序员的文章是年年有&#xff0c;今天特别多。那么AI到底会不会取代程序员的工作呢&#xff1f;先…

[chapter4][5G-NR][传输方案]

前言&#xff1a; 多天线传输的基本过程传输方案 前面见过数据加扰&#xff0c;调制&#xff0c;层映射的一些基本原理&#xff0c;算法。 这里重点讲一下传输方案 目录&#xff1a; 1&#xff1a; 下行传输方案 2&#xff1a; 上行传输方案 3&#xff1a; 资源块映射 备注&…

.net开发安卓从入门到放弃 最后的挣扎(排查程序闪退问题记录-到目前为止仍在继续)

安卓apk闪退问题排查记录logcat程序包名先看日志&#xff08;以下日志是多次闪退记录的系统日志&#xff0c;挑拣几次有代表性的发上来&#xff09;最近一次闪退adb shell tophelp一个demo说明adb shell dumpsys meminfo <package_name>ps&#xff1a;写在前面&#xff0…

训练中文版chatgpt

文章目录1. 斯坦福的模型——小而低廉&#xff1a;Alpaca: A Strong Open-Source Instruction-Following Model2. Meta 模型&#xff1a;LLaMA&#xff1a;open and efficient foundation language models3.ChatGLM4.斯坦福开源机器人小羊驼Vicuna&#xff0c;130亿参数匹敌90%…

SSM+LayUi实现的学籍管理系统(分为管理员、教师、学生三个角色,实现了专业管理,班级管理,学生管理,老师管理,课程管理,开课管理以及用户管理等)

博客目录jspservletmysql实现的停车场管理系统实现功能截图系统功能使用技术完整源码jspservletmysql实现的停车场管理系统 本系统是一个servlet原生框架实现的停车场管理系统&#xff0c;总共分为两个角色&#xff0c;普通用户和管理员&#xff0c;实现了用户管理、停车信息管…