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

相关推荐
UncleKyrie几秒前
🎨 市面上主流 Figma to Code MCP 对比
前端
南半球与北海道#14 分钟前
前端引入vue-super-flow流程图插件
前端·vue.js·流程图
然我20 分钟前
React 16.8:不止 Hooks 那么简单,这才是真正的划时代更新 🚀
前端·react.js·前端框架
小高00733 分钟前
📈前端图片压缩实战:体积直降 80%,LCP 提升 2 倍
前端·javascript·面试
OEC小胖胖37 分钟前
【React Hooks】封装的艺术:如何编写高质量的 React 自-定义 Hooks
前端·react.js·前端框架·web
BillKu1 小时前
vue3+element-plus 输入框el-input设置背景颜色和字体颜色,样式效果等同于不可编辑的效果
前端·javascript·vue.js
惊悚的毛毛虫1 小时前
掘金免广告?不想看理财交流圈?不想看exp+8?
前端
springfe01011 小时前
vue3组件 - 大文件上传
前端·vue.js
再学一点就睡1 小时前
Vite 工作原理(简易版)—— 从代码看核心逻辑
前端·vite
好好好明天会更好1 小时前
uniapp项目中小程序的生命周期
前端·vue.js