阻止 mousemove 或 touchmove 与 click 事件同时触发

2023-10-30

最近做了自己的开源项目 Msw-Tools,参考了 VConsole 工具中按钮的拖拽功能,计划给 MSW 按钮也增加类似的拖拽效果,并兼容PC端和手机端,但是遇到一个问题:一个按钮绑定了多个事件,怎样才能阻止 mousemove 或 touchmove 与 click 事件同时触发

MSW Tools

一、背景

如上图所示,实现 MSW 按钮拖拽要用到 mousedown、mousemove、mouseup 事件,对应的移动端要用到 touchstart、touchmove、touchend 事件,但是按钮上已经绑定了 click 点击事件,所以就要想办法阻止 mouse 鼠标事件或 touch 触摸事件 与 click 事件同时触发。不然每次拖拽按钮后都会触发 click 事件,这显然是不友好的。

二、问题解析

事件的执行顺序依次是:mousedown > mousemove > mouseup > click,因此,要想 mouseup 事件执行完后,不执行 click 事件,可能不太好直接处理,但是可以间接的实现。设置一个 移动状态的开关,并加上 延迟处理 就可以达到"阻止 click 事件"的目的。

三、代码实现

因为 Msw-Tools 工具是使用 Svelte 框架开发的,所以这里展示 Svelte 部分代码。

<!-- msw.svelte -->

<div class="msw-container">
  <div on:click|stopPropagation={showModal}
       bind:this={btnDOM}
       class="msw-show">MSW</div>
</div>

<script>
  import { onMount } from "svelte";
  
  // 区分当前是PC端,还是移动端,来设置 mouse 事件 或 touch 事件
  function getModels() {
    let userAgentInfo = navigator.userAgent;
    let mobileAgents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"];
    return mobileAgents.reduce((prev, ua)=>{
      return userAgentInfo.includes(ua) || prev
    }, false)
  };
  const MSW_BTN_POSITION = '__MSW_BTN_POSITION__'

  let show = false;
  let btnDOM = null;
  let isDrop = false;
  let isMoving = false;
  let offset = {
    x: 0,
    y: 0,
  };
  let offsetDown = {};
  let dropTimer = null;
  let isMobile = getModels();
  let btnW = 0;
  let btnH = 0;
  let clientW = 0;
  let clientH = 0;
  let eventType = isMobile ? 'touchstart' : 'mousedown';

  // DOM 挂载后执行
  onMount(async () => {
    // 初始化,获取按钮、视口宽高、计算边界值
    initClientData();
    return () => {
      // component 卸载后,解除事件绑定
      btnDOM.removeEventListener(eventType, btnMousedown)
    }
  });

  function initClientData() {
    // 按钮位置保存在本地,可以记住位置,避免每次去拖拽
    let local = localStorage.getItem(MSW_BTN_POSITION)
    if (local) {
      offset = JSON.parse(local)
      btnMove()
    }
    let w = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth
    clientW = isMobile ? w : document.body.clientWidth
    clientH = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight
    btnW = btnDOM.offsetWidth
    btnH = btnDOM.offsetHeight
    // 给按钮绑定 mousedown 或 touchstart 事件
    btnDOM.addEventListener(eventType, btnMousedown)
  }
  
  function eventHandle (type) {
    if (isMobile) {
      document[`${type}EventListener`]('touchmove', mousemove);
      document[`${type}EventListener`]('touchend', mouseup);
    } else {
      document[`${type}EventListener`]('mousemove', mousemove);
      document[`${type}EventListener`]('mouseup', mouseup);
    }
  }

  function showModal () {
    if (!isMoving) {
      show = true;
    }
  }

  function btnMousedown(e) {
    e = e || window.event
    isDrop = true
    offsetDown = {
      ...getOffset(e)
    };
    eventHandle('add')
  }

  function mousemove(e) {
    e = e || window.event
    if (isDrop) {
      let data = getOffset(e);
      // 判断是否移动了
      isMoving = !(offsetDown.x === data.x && offsetDown.y === data.y)
      let x = data.x - btnW / 2;
      let y = data.y - btnH / 2;
      if (x > 5 && x < (clientW-btnW - 5)) {
        offset.x = x;
      }
      if (y > 5 &&  y < (clientH-btnH - 5)) {
        offset.y = y;
      }
      if (isMoving) {
        btnMove()
      }

      clearTimeout(dropTimer);
      dropTimer = setTimeout(()=>{
        isMoving = false;
        clearTimeout(dropTimer);
        dropTimer = null;
      }, 300);
    }
  }

  function mouseup() {
    if (isDrop) {
      window.localStorage.setItem(MSW_BTN_POSITION, JSON.stringify(offset))
      eventHandle('remove')
    }
    isDrop = false
    // console.log('mouseup')
  }

  function btnMove (){
    btnDOM.style.cssText = `
    left: ${offset.x}px;
    top: ${offset.y}px;
    right: auto;
    bottom: auto;
    `
  }

  function getOffset(e) {
    return isMobile ? {
      x: e.targetTouches[0].clientX,
      y: e.targetTouches[0].clientY,
    } : {
      x: e.clientX,
      y: e.clientY,
    }
  }
</script>

<style lang="scss" type="text/scss">
  @import "index";
</style>

欢迎访问:天问博客

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

阻止 mousemove 或 touchmove 与 click 事件同时触发 的相关文章

  • 如何向上转型以限制对象属性

    在 JavaScript 中 如何从子类向上转换到超类以自动删除超类中不存在的对象属性 示例 假设有以下 2 个类 class ClassA constructor public a string public b string class
  • 带换行符的 React/Momentjs 日期格式

    我有一个日期字符串 我想对其进行格式化 其中数字位于月份下 Jul 6 我尝试了几种不同的方法来添加新行
  • 为什么我的反应路由器链接将我带到页面中间?

    我的网站上有很多链接 但只有一个可以执行此操作 它没有将我带到导航栏的顶部 而是转到内容的中间 知道为什么会这样吗 这是一个页面 其中有一个 map经历一些JSON 这是渲染div页面下方的文本元素 如果重要的话 这是一些可能相关的代码 路
  • axios 请求中未发送正文数据

    我试图通过 axios 请求将数据发送到我的后端脚本 但正文看起来是空的 这是前端发送的请求 axios request method GET url http localhost 4444 next api headers Authori
  • Eslint 从另一个文件确定全局变量

    我试图以这样的方式设置 ESLint 使其在对实际目标文件进行 linting 之前解析全局声明文件 这样我就不必将所有确实是全局的函数和变量声明为全局 而是让解析器弄清楚 In 一些 模块 js function do something
  • 如何在 DOM 中的每个元素中调用函数,即使它们是动态创建的

    我想对 DOM 上的特定元素调用函数 例如 red css backgroundColor pink 它适用于 DOM 中已经存在的任何元素 但我也希望在动态添加到 DOM 的元素中调用此方法 我尝试过类似的事情 red on functi
  • 使用 jQuery Select2 清除下拉菜单

    我正在尝试使用奇妙的方式以编程方式清除下拉菜单Select2 http ivaynberg github com select2 图书馆 使用 Select2 远程 ajax 调用动态填充下拉列表query option HTML
  • 在随机位置启动 HTML5

    我有一个大约 2 小时长的音轨 我想在我的网站上使用它 我希望它在页面加载时在随机位置开始播放曲目 使用 HTML5 可以吗 我知道您可以使用 element currentTime 函数来获取当前位置 但是如何在完全下载之前获取曲目的总时
  • 在 R 传单中添加不透明度滑块

    如何在 R leaflet 应用程序中添加滑块来控制特定图层的不透明度 对于这个应用程序 我不想使用闪亮 这里建议 在 R 传单应用程序中添加滑块 https stackoverflow com questions 37682619 add
  • 访问 nuxt 配置文件中的存储

    我想添加通过 Nuxt 静态生成的动态路由 我定义了一个客户端 服务器端存储asyncData方法 我想将这个存储值 一个数组 映射到我的nuxt config js文件使其成为 动态 静态 路线图nuxt generate命令 但如何访问
  • JS 保留以零结尾的小数[重复]

    这个问题在这里已经有答案了 在JavaScript中 是否可以 锁定 十进制数 以保留以零结尾的 浮点数 例如 我有 2 个不同的数字 如下所示 伪代码 let a 1 0 let b 1 00 a b true should be fal
  • React JS 服务器端问题 - 找不到窗口

    你好 我正在尝试在我的reactJS项目中使用react rte 我有服务器端渲染 每次我想使用这个包时 我都会得到 return msie 6 9 b test window navigator userAgent toLowerCase
  • 如何在 Web 服务器上设置 gzip 压缩?

    我有一个嵌入式网络服务器 总共有 2 兆空间 通常 您使用 gzip 文件对客户端有利 但这会节省我们在服务器上的空间 我读到你可以只 gzip js 文件并将其保存在服务器上 我在 IIS 上测试过 但没有任何运气 为了使这项工作成功 我
  • 当我多次调用 requestAnimationFrame 时会发生什么

    我的意思是一次调用多个具有相同功能的 requestAnimationFrame function Draw DoSomething function AFunc prepare something requestAnimationFram
  • ES6继承:使用`super`访问父类的属性

    JavaScript 的super关键字 当我在 Chrome Babel TypeScript 上运行代码时 得到了不同的结果 我的问题是哪个结果是正确的 规范的哪一部分定义了这种行为 下面的代码 class Point getX con
  • 如何动态调整jqgrid到当前窗口大小?

    如何动态调整jqgrid到当前窗口大小 基于javascript jQuery 最好的例子在这里 TinyMCE 去 http www tinymce com tryit full php http www tinymce com tryi
  • 如何重复 ajax 请求,直到满足 RxJS Observable 的条件?

    我正在尝试重复请求 直到响应包含使用 RxJS 的数据 此时我想调用成功 或失败 处理程序 但我在使用 RxJS 时遇到了麻烦 这是我目前的方法 redux observable action observable mergeMap gt
  • Jwt 签名和前端登录身份验证

    我有这个特殊的 jwt sign 函数 Backend const token jwt sign id user id process env TOKEN SECRET expiresIn 1m res header auth token
  • ng-include 和 ng-view 不同时加载

    下面是我的应用程序的结构 很简单 页眉和页脚是非常小的文件 而主页上的 ng view 要大得多 当我进入该页面时 我注意到了这一点 首先加载两个 ng include 然后 ng view 出现 页脚被推到底部 页脚闪烁大约 0 1 秒
  • 如何在运行脚本之前提交活动单元格中所做的更改? (Google 表格/Google Apps 脚本)

    我正在使用 Google Apps 脚本在 Google 表格中创建提交表单 该表单位于一页上 提交内容被移至第二个隐藏页面 当用户填写表单后 他们按下提交页面上的按钮以激活脚本 我遇到的问题是 当用户填写最后一个单元格然后单击按钮时 输入

随机推荐

  • powershell 学习笔记

    命令模式 字符串不需要加引号 除变量和圆括号中的内容外的所有内容均可看作字符串 copy users txt accounts txt copy src dest write host 2 2 表达式模式 以最高级别语言分析方法来进行分析
  • 高通平台MSM8916LCM模块移植(一)-bootloader部分

    目录 LK中LCM启动流程 oem panel select mdss dsi initialize read panel id 此次移植打算分成两个模块来说 bootloader部分和kernel部分 在实际的移植调试过程中也是这么分成了
  • C语言期末复习编程练习之百钱百鸡问题

    古代数学家张丘建在 算经 一书中提出的数学问题 鸡翁一值钱五 鸡母一值钱三 鸡雏三值钱一 百钱买百鸡 问鸡翁 鸡母 鸡雏各几何 创作此系列的原因也是为了复习期末考试 各位见谅哦 include
  • 近7000万美元被盗:Curve被攻击事件分析

    7 月 31 日 以太坊编程语言Vyper发布公告称 Vyper 0 2 15 0 2 16 和 0 3 0 版本的递归锁失效 随后Curve Finance等相关协议出现了漏洞攻击事件 漏洞的根源都是某些版本的 Vyper 中出现故障的递
  • python发送邮件zmail库

    第三方库 zmail 和 yagmail 可实现邮件发送 在实际使用对比zmail比yagmail更简洁 使用zmail 无需登录OA邮箱 便可完成邮件的发送及附件的自动加载 import zmail def send zmail send
  • JupyterHub on Kubernetes部署

    理论是灰色的 实践之树长青 恩格斯 近日在做毕设项目 涉及到在K8s和swarm基础上部署JupyterHub 经过两天时间的学习和部署 N次的失败尝试 最终在服务器上成功部署了JupyterHub 实验依赖 阿里云服务器2核4G ubun
  • SpringBoot项目的创建和jar、war方式的部署

    一 创建项目 项目开发用的是IDEA java版本为1 8 Tomcat版本为9 0 8 新建项目 选择Spring Initializr 下一步 填写group和artifact信息 下一步 选择Web中的Web 下一步 选择存放路径 例
  • git部署出现的问题

    git部署出现的问题 error remote origin already exists remote rejected master master hook declined 一 出错信息 fatal remote origin alr
  • spring boot 配置log4j2

    刚入职新公司 接到的第一个需求就是把项目的log4j 1 x 升级到2 x 之前没有做过日志配置 都是直接拿来用的 这是第一次自己配置日志文件 所以记录下相关知识点 1 排除1 0的jar包 首先排查项目中log的版本 把1 0相关的版本都
  • AI-day02-2(Python小白逆袭大神)

    安装paddlehub pip install paddlehub 1 6 0 i https pypi tuna tsinghua edu cn simple Looking in indexes https pypi tuna tsin
  • AndroidStudio如何使用@hide api

    前提 你的应用必须是System App 在project的build gradle里面添加 gradle projectsEvaluated 所有的 project 都配置完成后的回调 此时 所有的project都已经配置完毕 准备开始生
  • 关于 DRM 中 DUMB 和 PRIME 名字的由来

    前言 在上一篇 DRM驱动程序开发 VKMS 文章里 我们学习了如何编写一个最简单的 KMS 驱动 而本篇 我将以叙述的形式为大家讲解 DRM GEM 的相关概念 代码留到下一篇进行讲解 我知道 大多数的 DRM 初学人员 在刚接触到 GE
  • 怎么编写接口测试用例

    怎么编写接口测试用例 接口测试用例如何编写 看到许多这样的问题 大家都知道编写接口测试用例是接口测试的重要组成部分 它决定了测试的质量和可靠性 因此 程序员必须编写高质量的接口测试用例 以确保接口在生产环境中能够正常运行 编写接口测试用例的
  • C语言基础入门48篇_14_逻辑运算符(逻辑与(&&)、逻辑或(

    C语言中的逻辑运算符有 及 他们分别被称为逻辑与 逻辑或 逻辑非 前两者是二元运算符 逻辑非是一元运算符 1 逻辑与运算符 逻辑与运算符的基本语法是 表达式1 表达式2 其求值的结果规则是 1 当两个表达式均为非0时 求值结果为1 2 其他
  • vue2在css中使用js变量

    本篇将实现vue2在css中使用js变量 下图是el tab组件 由上面的tab头和下面的内容区构成 当内容区过长的时候 外层固定高度的盒子会出现滚动条 设置了overflow auto tab头部会向上滚动而消失 滚动前 滚动后 现在的需
  • pyqt5安装

    一定要先pip install sip 再pip install pyqt5 不然可能会安装失败 然后测试一下是否成功 输入 import sys from PyQt5 QtWidgets import QWidget QApplicati
  • Redis 发布 订阅

    1 简介 Redis 发布订阅 pub sub 是一种消息通信模式 发送者 pub 发送消息 订阅者 sub 接收消息 Redis 客户端可以订阅任意数量的频道 客户端订阅频道 当给频道发布消息后 消息就会发送给订阅的客户端 2 实现 A
  • Linux下gcc编译器的编译过程

    一 什么是GCC GCC是以GPL许可证所发行的自由软件 也是GNU计划的关键部分 GCC的初衷是为GNU操作系统专门编写一款编译器 现已被大多数类Unix操作系统 如Linux BSD MacOS X等 采纳为标准的编译器 甚至在微软的W
  • 10吨地埋式农村生活废水处理设备厂家电话

    10吨地埋式农村生活废水处理设备厂家电话 工艺流程 厌氧生化处理 好氧生物接触氧化 二沉沉淀 二氧化氯接触消毒 达标排放 工艺流程 采用生物膜法 缺氧 好氧 A 0 处理工艺 A O即缺氧好氧生物接触氧化法是一种成熟的生物处理工艺 具有容积
  • 阻止 mousemove 或 touchmove 与 click 事件同时触发

    最近做了自己的开源项目 Msw Tools 参考了 VConsole 工具中按钮的拖拽功能 计划给 MSW 按钮也增加类似的拖拽效果 并兼容PC端和手机端 但是遇到一个问题 一个按钮绑定了多个事件 怎样才能阻止 mousemove 或 to