首页 > 其他分享 >记录--实现一个鼠标框选的功能,要怎么实现和设计 api?

记录--实现一个鼠标框选的功能,要怎么实现和设计 api?

时间:2024-02-03 17:45:11浏览次数:29  
标签:滚动 自定义 -- value react api 框选

这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

前言

前两年在一家做电商的公司做了一个需求:鼠标框选商品卡片,开始拖拽的时候合成一个然后改变位置,页面上有几千个所以还要结合虚拟列表。当时不知道怎么做,就在 github 上到处找现成的库,最后找到了 react-selectable-fast,并结合 react-virtualizedreact-sortable-hoc 完成了需求。虽然该库已经很久不维护了,但大致上能满足我的需求了,尽管它是以 dom 的方式,很不 react,但秉承着能用就行的原则。意料之中,开发过程中遇到了 bug,最后只能 fork 一份修改源码后自己发了个 npm 包来使用。

项目介绍

前几个月在空闲时间突然来了兴致,自己找点事做,就想自己开发一个框选的库吧,万一也有人有这个需求不知道怎么办呢?写完后发到了 antd 的社区共建群里,有的人觉得不错也 star 了。先献上项目地址 react-selectable-box,文档完整,使用 dumi 编写。api 友好,支持自定义一些功能。

api 设计

一个组件在设计时,首先思考的应该是 api 如何去设计,最好符合大家平常的习惯,并具有一定的自定义和拓展能力。再加上了解 react-selectable-fast 这个库的缺点和痛点,对我的设计就更加有帮助了。大家在看下面的文章之前也可以思考一下,如果是你,你会怎么设计?这里只选取几个 api 来进行介绍。

主组件 Selectable

选中的值

defaultValuevalue,类型为 any[],每个方块一般都有一个唯一 id 来标识,2024-1-31 更新后 后支持任意类型,因为考虑到很多情况你可能需要一个对象或数组来标识,文章后面提供了 compareFn 来自定义比较值相等。

禁用

disabled,大部分有值的组件应该都会有此属性,能直接禁用框选功能。

模式

mode,类型为 "add" | "remove" | "reverse"。模式,表明当前框选是增加、减少还是取反。这个 api 感觉是设计的最好的,用户会框选来选择目标,肯定也会需要删除已经框选的目标,可能是按住 shift 来删除等等之类的操作。用户可以自己编写自定义逻辑来修改 mode 的值来控制不同的行为,反观 react-selectable-fast,则是提供了 deselectOnEscallowAltClickallowCtrlClickallowMetaClickallowShiftClick 等多个 api。

开始框选的条件

selectStartRange,类型 "all" | "inside" | "outside",鼠标从方块内部还是外部可以开始框选,或都可以。

可以进行框选的容器

dragContainer,类型 () => HTMLElement,例如你只希望某个卡片内才可以进行框选,不希望整个页面都可以进行框选,这个 api 就会起到作用了。

滚动的容器

scrollContainer,类型 () => HTMLElement,如果你的这些方块是在某个容器中并且可滚动,就需要传入这个属性,就可以在滚动的容器中进行框选操作了。

框选矩形的 style 与 className

boxStyleboxClassName,使用者可以自定义颜色等一些样式。

自定义 value 比较函数

compareFn,类型 (a: any, b: any) => boolean,默认使用 === 进行比较(因为 value 支持任意类型,比如你使用了对象或数组类型,所以你可能需要自定义比较)

框选开始事件

onStart,框选开始时,使用者可能需要做一些事情。

框选结束事件

onEnd,类型为 (selectingValue: (string | number)[], { added: (string | number)[], removed: (string | number)[] }) => voidselectingValue 为本次框选的值,added 为本次增加的值,removed 为本次删除的值。例如你想在每次框选后覆盖之前的操作,直接设置 selectingValue 成 value 即可。如果你想每次框选都是累加,加上 added 的值即可,这里就不再说明了。

方块可选 - useSelectable

怎么让方块可以被选择呢?并且一一绑定上对应的值?react-selectable-fast 则是提供 clickableClassName api,传入可以被选择的目标的 class,这种方式太不 react 了。此时我的脑海里想到了 dnd-kit,我认为是 react 最好用的拖拽库,它是怎么让每个方块可以被拖拽的呢?优秀的东西应该借鉴,于是就有了 useSelectable

const { 
setNodeRef, // 设置可框选元素
isSelected, // 是否已经选中
isAdding, // 当前是否正在添加
isRemoving, // 当前是否正在删除
isSelecting, // 当前是否被框选
isDragging // 是否正在进行框选操作
} = useSelectable({
  value, // 每个元素的唯一值,支持任意类型
  disabled, // 这个元素是否禁用
  rule, // "collision" | "inclusion" | Function,碰撞规则,碰到就算选中还是全部包裹住才算选中,也可以自定义
});

如何使用?

const Item = ({ value }: { value: number }) => {
  const { setNodeRef, isSelected, isAdding } = useSelectable({
    value,
  });

  return (
    <div
      ref={setNodeRef}
      style={{
        width: 50,
        height: 50,
        borderRadius: 4,
        border: isAdding ? '1px solid #1677ff' : undefined,
        background: isSelected ? '#1677ff' : '#ccc',
      }}
    />
  );
};

实现

这里只简单讲一下思路,有兴趣的同学可以直接前往源码进行阅读。

主组件 Selectable 相当于一个 context,一些状态在这里进行保存,并掌管每个 useSelectable,将其需要的值通过 context 传递过去。

在设置的可被框选的容器内监听鼠标 mousedown 事件,记录其坐标,根据 mousemove 画出框选矩形,再根据 setNodeRef 收集的元素和框选矩形根据碰撞检测函数计算出是否被框选了,并将值更新到 Selectable 中去,最后在 mouseup 时触发 onEnd,将值处理完之后并丢出去。

演示

这里演示一下文章开头所说的框选拖拽功能,配合 dnd-kit 实现,代码在文档的 example 中。

遇到的坑

这里分享一下遇到的坑的其中之一:框选的过程中会选中文字,很影响体验,怎么让这些文字不能被框选呢?

方案1: 用 user-select: none 来控制文本不可被选中,但是这是在用户侧来做,比较麻烦。并且发现在 chrome 下设置此属性后,拖拽框选到浏览器边缘或容器边缘后不会自动滚动,其它浏览器则正常

方案2: 在 mousedown 时设置 e.preventDefault(),这样选中时文字就不会被选中,但是拖拽框选到浏览器边缘或容器边缘后不会自动滚动,只能自己实现了滚动逻辑。后面又发现在移动端的 touchstart 设置时,会发现页面上的点击事件都失效了,查资料发现没法解决,只能另辟蹊径。

方案3: 在 mousemovetouchmove 时设置 e.preventDefault() 也是可以的,但也需要自己实现滚动逻辑。

最终也是采取了方案3。

后续目标

目前只能进行矩形的碰撞检测,不支持圆形(2024.1.26 更新支持自定义已经可以实现)及一些不规则图形(2024.1.26 更新提供自定义碰撞检测(dom 下太难,canvas 比较好做碰撞检测),剩下的就是使用者的事了!)。这是一个难点,如果有想法的可以在评论区提出或者 pr 也可。

2024-1-24 更新

添加 cancel 方法,试一试。可以调用 ref.current?.cancel() 方法取消操作。这样可以自定义按下某个键来取消当前操作。有想需不需要添加一个属性传入 keyCode 数组内置取消,但是感觉会使 api 太多而臃肿,也欢迎留下你的想法。

2024-1-26 更新一

添加 items api 以优化虚拟滚动滚动时框选范围增加或减小时,已经卸载的 Item 的判断框选。(可选)试一试

优化前:滚动到下面时,加大框选面积,上面已经被卸载的不会被选中

 

2024-1-26 更新二

支持自定义碰撞规则检测,试一试自定义圆形碰撞检测

 

2024-1-31 更新

value 支持任意类型 any,不再只是 string | number 类型,因为很多情况需要是一个对象或数组来当唯一标识,并提供了 compareFn 来支持自定义值的比较,默认使用 ===,如果你的 value 是对象或数组,需要此属性来比较值。

本文转载于:

https://juejin.cn/post/7326979670485123110

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

 

标签:滚动,自定义,--,value,react,api,框选
From: https://www.cnblogs.com/smileZAZ/p/18004995

相关文章

  • linux 查看端口并关闭端口命令
    1.查看服务器端口情况:lsof-i:端口号(lsof-i需要root用户的权限)相关命令:lsof-i:8080--查看8080端口占用lsofabc.txt--显示开启文件abc.txt的进程lsof-cabc--显示abc进程现在打开的文件lsof-c-p1234--列出进程号为1234的进程所打开的文件lsof-ggid--显示归......
  • 读论文-基于图神经网络的会话推荐方法综述
    前言今天读的一篇文章是于2023年6月25日在《数据分析与知识发现》发表的一篇期刊论文,文章聚焦图神经网络技术,对会话推荐方法进行述评,为后续研究提供借鉴。以“SessionbasedRecommendation”、“GraphNeuralNetwork”、“会话推荐”、“图神经网络”为检索词,在“WebofScience......
  • <template #default="{ node,data }">
    <template><el-dialogv-model="dialogVisible":title="title":width="width":close-on-click-modal="false":close-on-press-escape="false"@close="dialogClose"......
  • c语言小练习——字符串长度、拷贝、拼接、比较
    /* 使用c语言知识实现下面程序: 1,实现strlen函数的功能 2,实现strcpy函数的功能 3,实现strcat函数的功能 4,实现strcmp函数的功能 不允许使用已有的str函数*/1#define_CRT_SECURE_NO_WARNINGS2#include<stdio.h>3#include<string.h>4#include<stdbool.h>5#......
  • [THUSCH2017] 巧克力
    [THUSCH2017]巧克力题目描述「人生就像一盒巧克力,你永远不知道吃到的下一块是什么味道。」明明收到了一大块巧克力,里面有若干小块,排成\(n\)行\(m\)列。每一小块都有自己特别的图案,它们有的是海星,有的是贝壳,有的是海螺……其中还有一些因为挤压,已经分辨不出是什么图案了。......
  • OneFlow计算框架的OneAgent是不是一个子虚乌有的东西?
    自己是搞强化学习的,今天看了些OneFlow计算框架的一些资料,发现OneFlow官方一直有宣传自己的强化学习框架——OneAgent,但是十分诡异的是从了OneFlow的官方宣传可以看到这个词,但是在所有的网上都是看不到这个词的,不论是GitHub还是pypi还是conda上都没有这个东西,那么问题来了,这个宣传......
  • 地铁最优线路算法的求解(三)-深度优先搜索java实现
    多的不说,showmethecode,先上一段java代码1/*2*深度优先算法(DFS)算法生成所有可能路径3*startId:出发站4*endId:到达站5*graph:辅助邻接矩阵,若99站与35站相邻,6*则graph[35][99]=1,graph[99][35]=17*8*......
  • 干草堆
    先倒序处理(为什么下文会说),然后就变成了划分尽量多的段,使得每段的和单调不减很容易设置出一个状态\(f[i][j]\)表示前\(i\)堆草,最后一段是\([j,i]\)的最大高度,方程也很容易推导,但是时空复杂度显然炸掉那么此时我们就应该思考,要么就是利用数组值来搞一些事情,要么就是发现某些引理(一......
  • valgrind的memcheck功能使用手册
    valgrind是一个构建动态分析程序的工具集框架,它有一套功能强大的工具集合,包括debug、profiling等,其中最重要和常用的是内存泄漏检测工具memcheck下载:wgethttps://sourceware.org/pub/valgrind/valgrind-3.22.0.tar.bz2--no-check-certificate解压后编译安装:tarxfvalgrind......
  • 操作系统
    操作系统第一章计算机系统概述系统调用与库函数第二章进程管理进程组织用PCB来记录进程的相关数据,状态等进程状态进程控制实现进程状态转换的方式1.如果处于运行态,在运行的过程中发出了对某个进程的请求,就会进入阻塞态,去完成那个需要的进程,再回来完成这个2.......