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.针对实际应用,会有无限滚动加载的应用场景,需及时刷新容器数据。

相关推荐
前端大卫14 小时前
Vue3 + Element-Plus 自定义虚拟表格滚动实现方案【附源码】
前端
却尘14 小时前
Next.js 请求最佳实践 - vercel 2026一月发布指南
前端·react.js·next.js
ccnocare14 小时前
浅浅看一下设计模式
前端
Lee川14 小时前
🎬 从标签到屏幕:揭秘现代网页构建与适配之道
前端·面试
Ticnix15 小时前
ECharts初始化、销毁、resize 适配组件封装(含完整封装代码)
前端·echarts
纯爱掌门人15 小时前
终焉轮回里,藏着 AI 与人类的答案
前端·人工智能·aigc
twl15 小时前
OpenClaw 深度技术解析
前端
崔庆才丨静觅15 小时前
比官方便宜一半以上!Grok API 申请及使用
前端
星光不问赶路人15 小时前
vue3使用jsx语法详解
前端·vue.js
天蓝色的鱼鱼15 小时前
shadcn/ui,给你一个真正可控的UI组件库
前端