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

相关推荐
HWL567924 分钟前
Express项目解决跨域问题
前端·后端·中间件·node.js·express
刺客-Andy37 分钟前
React 第三十九节 React Router 中的 unstable_usePrompt Hook的详细用法及案例
前端·javascript·react.js
Go_going_43 分钟前
【js基础笔记] - 包含es6 类的使用
前端·javascript·笔记
浩~~1 小时前
HTML5 浮动(Float)详解
前端·html·html5
AI大模型顾潇2 小时前
[特殊字符] 本地大模型编程实战(29):用大语言模型LLM查询图数据库NEO4J(2)
前端·数据库·人工智能·语言模型·自然语言处理·prompt·neo4j
九月TTS3 小时前
TTS-Web-Vue系列:Vue3实现内嵌iframe文档显示功能
前端·javascript·vue.js
爱编程的小学究3 小时前
【node】如何把包发布到npm上
前端·npm·node.js
weixin_473894773 小时前
前端服务器部署分类总结
前端·网络·性能优化
LuckyLay3 小时前
React百日学习计划-Grok3
前端·学习·react.js
澄江静如练_3 小时前
小程序 存存上下滑动的页面
前端·javascript·vue.js