vue3框选

一、前言

项目中经常会遇到文件列表相关的需求,其中框选文件是比较常见的需求,因此提取了一个小组件,如大家有更好的实现方式,可以评论区交流一下。

demo链接:code.juejin.cn/api/raw/731...

二、如何实现

1.容器

第一步,创建多个块,使用数组记录并动态设置active以高亮边框。

js 复制代码
<div 
    class="box" 
    :class="{
        'active': activeList.includes(index)
    }" 
    v-for="(item, index) in 50"
>{{ index }}</div>

第二步,使用getBoundingClientRect计算盒子信息,用于后续位置判定。

js 复制代码
const calcBox = (arr, dom, baseScrollTop) => {
    const length = dom.children.length;
    for(let i = 0; i < length; i++) {
        let child = dom.children[i];
        let domData = child.getBoundingClientRect();
        let obj = {
            index: i,
            dom: child,
            position: [
                domData.x,
                domData.y + baseScrollTop,
                domData.width,
                domData.height,
            ]
        }
        arr.push(obj);
    }
}

2.计算选择框是否包含块

根据选择框的left,top,width,height,以及前期收集的盒子数据数组判断是否处于框内。

从x轴方向分析,有三种情况需要考虑,y轴同理。

根据框的四维和盒子的思维做判断

js 复制代码
const calcSelect = (left, top, width, height, boxList) => {
    const arr = new Set()
    for (let i = 0; i < boxList.length; i++) {
        const p = boxList[i].position
        if (
            (left <= p[0] && left + width > p[0]) ||
            (left > p[0] && left < p[0] + p[2] && width > 0)
        ) {
            if (
                (top <= p[1] && top + height > p[1]) ||
                (top > p[1] && top < p[1] + p[3] && height > 0)
            ) {
                arr.add(boxList[i].index)
            } else {
                arr.delete(boxList[i].index)
            }
        } else {
            arr.delete(boxList[i].index)
        }
    }
    return Array.from(arr)
}

3.鼠标事件中记录需要的值

使用mouseevent中的clientX和clientY记录鼠标位置,同时需要考虑页面原本的滚动高度。

移动中需考虑整体方向是向左还是向右,会影响到选择框的思维绘制。

js 复制代码
const mousedown = (e) => {
    let baseL = e.clientX,
        baseT = e.clientY,
        baseScrollTop = document.children[0].scrollTop, // html根节点,需根据实际场景替换
        boxList = []
    calcBox(boxList, dragSelectDom.value, baseScrollTop);
    activeList.value = []
    document.onmousemove = (ev) => {
        showDrag.value = true
        const xOffset = ev.clientX - baseL
        const yOffset = ev.clientY - baseT
        const width = Math.abs(xOffset)
        const height = Math.abs(yOffset)
        const left = xOffset < 0 ? ev.clientX : baseL
        const top = yOffset < 0 ? ev.clientY : baseT
        const scrollTop = document.children[0].scrollTop
        updateRect(left, top + scrollTop, width, height); // 这个函数用于修改选择框css属性
        activeList.value = []
        activeList.value = calcSelect(left, top + scrollTop, width, height, boxList)
    }
    document.onmouseup = () => {
        showDrag.value = false
        document.onmousemove = null
        document.onmouseup = null
    }
}

最后将mousedown事件挂载到根节点上即可

三、后续事项

1.首先是判断算法优化,目前是每次移动都需要判断全部块是否有相交,比较耗性能,块越多越卡。

2.还需考虑拖拽触底时候的滚动,可以用requestAnimationFrame模拟滚动,在相应的判断中要增加滚动差的判断。

3.横向滚动

4.针对实际应用,会有无限滚动加载的应用场景,需及时刷新容器数据。

相关推荐
SleepyZone5 分钟前
Cline 源码浅析 - 从输入到输出
前端·ai编程·cline
Struggler2819 分钟前
pinia-基于monorepo的项目结构管理
前端
Struggler28113 分钟前
SSE的使用
前端
用户58061393930020 分钟前
前端文件下载实现深度解析:Blob与ObjectURL的完美协作
前端
Lin866623 分钟前
Vue 3 + TypeScript 组件类型推断失败问题完整解决方案
前端
coding随想23 分钟前
从零开始:前端开发者的SEO优化入门与实战
前端
前端工作日常25 分钟前
我理解的JSBridge
前端
Au_ust26 分钟前
前端模块化
前端
顺丰同城前端技术团队26 分钟前
还不会用 Charles?最后一遍了啊!
前端
BUG收容所所长27 分钟前
二分查找的「左右为难」:如何优雅地找到数组中元素的首尾位置
前端·javascript·算法