基于Vue3+TypeScript实现鼠标框选功能
作者:广岛原子
这篇文章主要介绍了基于Vue3+TypeScript实现鼠标框选功能,文中通过代码示例给大家讲解的非常纤细,对大家的学习或工作有一定的帮助,需要的朋友可以参考下
实现功能:
- 选择、取消选择单个元素、同时选择多个元素。
- 鼠标悬浮变色。
- 框选、滚动框选区域。
- 同时选择多个区域。
最终实现效果
1. HTML代码
<div class="right-bottom"> <<img src="div" alt="" width="70%" /> class="page" ref="pageRef" @scroll="handleScroll" @mouseup="handleMouseUp" > <table class="table" @mousemove="handleMouseMove"> <tr v-for="(row, rowIndex) in tableData" :key="rowIndex"> <td id="td" v-for="(col, colIndex) in row" :key="colIndex" :class="{ 'table-item-active': selectedCells.has(rowIndex + '-' + colIndex), }" :style="{ backgroundColor: selectedCells.has(rowIndex + '-' + colIndex) ? 'skyblue' : col.color, }" @mousedown.prevent="startSelect(rowIndex, colIndex)" > <div class="table-item" :class="{ 'item-hover': hoverPosition == `${rowIndex}-${colIndex}`, }" @click="handleItemClick(rowIndex, colIndex)" @mouseenter="handleMouseEnter(rowIndex, colIndex)" @mouseleave="handleMouseLeave(rowIndex, colIndex)" > {{ col.hole }} </div> </td> </tr> </table> </div> </div>
2. 定义全局变量
// 已选择的单元格。因为可能出现重复框选单元格的情况,所以这里最好使用Set类型。 const selectedCells = reactive(new Set([])); // 矩阵起始坐标位置。框选矩阵在页面上的位置,使用时请根据实际情况计算 let tableStartX = 0; let tableStartY = 0; // 单元格宽高 let cellWidth = 63; let cellHeight = 63; // 是否按住ctrl let isClickCtrl = false; // 当前悬浮单元格坐标 let hoverPosition = ref('');
3. 实现单个元素选择、取消选择、按ctrl同时选择多个元素
/** * @description: 点击单元格事件 * @param row 当前单元格X坐标 * @param column 当前单元格Y坐标 */ const handleItemClick = (row: number, column: number) => { // 按下ctrl document.onkeydown = (e: KeyboardEvent) => { if (e.key === 'Control') isClickCtrl = true; }; // 松开ctrl document.onkeyup = (e: KeyboardEvent) => { if (e.key === 'Control') isClickCtrl = false; }; // 如果按住ctrl键 if (isClickCtrl) { // 点击了已经选中的元素,取消选中; 否则选中 if (selectedCells.has(`${row}-${column}`)) { selectedCells.delete(`${row}-${column}`); }else { selectedCells.add(`${row}-${column}`); } } // 如果没有点击ctrl键,则先清空所有选中的元素,然后选中当前元素 else { selectedCells.clear(); selectedCells.add(`${row}-${column}`); } };
4. 鼠标悬浮变色
/** * @description: 鼠标悬浮单元格变色 * @param row 当前单元格X坐标 * @param column 当前单元格Y坐标 */ // 鼠标进入单元格,使用防抖降低代码重复执行次数,提高性能 const handleMouseEnter = _.debounce((row: number, column: number) => { hoverPosition = `${row}-${column}`; }, 100); // 鼠标离开单元格 const handleMouseLeave = _.debounce(() => { hoverPosition = ''; }, 50);
5. 框选、滚动框选元素功能实现
5.1 获取框选区域内的所有元素
实现步骤:
- 拿到起始单元格坐标以及结束单元格坐标。
- 根据起始结束坐标计算出框选区域内的所有单元格。
实现原理:例1. 如起始坐标为 startCell = [0, 0],结束坐标 endCell = [2, 2]
则当前区域的元素包括:
0,0 | 0,1 | 0,2 |
---|---|---|
1,0 | 1,1 | 1,2 |
2,0 | 2,1 | 2,2 |
例2. 如起始坐标为 startCell = [3, 4],结束坐标 endCell = [1, 2]
则当前区域的元素包括:
1,2 | 1,3 | 1,4 |
---|---|---|
2,2 | 2,3 | 2,4 |
3,2 | 3,3 | 3,4 |
以此类推。。。
结论: 根据以上实例可得,我们只需要拿到起止位置X坐标和Y坐标,然后用双重for循环即可得到当前框选区域内的所有元素
获取框选区域内的所有元素方法:
/** * @description: 根据起始坐标和结束坐标,设置选中区域 * @param startX 起始坐标x * @param startY 起始坐标y * @param endX 结束坐标x * @param endY 结束坐标y */ const selectRange = ( startX: number, startY: number, endX: number, endY: number ) => { for ( let rowIndex = Math.min(startX, endX); rowIndex <= Math.max(startX, endX); rowIndex++ ) { for ( let cellIndex = Math.min(startY, endY); cellIndex <= Math.max(startY, endY); cellIndex++ ) { selectedCells.add(`${rowIndex}-${cellIndex}`); } } };
5.2 计算框选结束坐标
实现原理:首先分别计算出鼠标距离矩阵原点left距离和top距离,然后再用这个距离除单个单元格的宽高就能得到结束坐标。
如果矩阵框选有滚动操作,只要再加上滚动距离即可。
5.3 鼠标移动事件
// 起始单元格坐标 let startCell: [number, number] | null = null; // 鼠标点击事件,设置起始坐标 function startSelect(rowIndex: number, cellIndex: number) { startCell = [rowIndex, cellIndex]; } // 鼠标抬起事件,将起始坐标设置为null const handleMouseUp = () => { startCell = null; }; // 鼠标移动事件 const handleMouseMove = (e: MouseEvent) => { debounceFun1(e); }; const debounceFun1 = _.debounce((e: MouseEvent) => { // 如果没有起始坐标,则不执行 if (!startCell) return; // 按下ctrl键 document.onkeydown = (e: KeyboardEvent) => { if (e.key === 'Control') isClickCtrl = true; }; // 松开ctrl键 document.onkeyup = (e: KeyboardEvent) => { if (e.key === 'Control') isClickCtrl = false; }; endXY.x = e.clientX; endXY.y = e.clientY; // 获取鼠标移动的当前单元格坐标,并设置为框选结束坐标 let endCell: [number, number] | null = null; endCell = [ Math.floor((e.clientY + scrollXY.y - tableStartY) / cellHeight), Math.floor((e.clientX + scrollXY.x - tableStartX) / cellWidth), ]; // 如果鼠标位置发生变化,则清空已选择的单元格,并重新设置选中区域 if (!isClickCtrl) { selectedCells.clear() } // 根据起始坐标和结束坐标,设置选中区域 selectRange(...startCell, ...endCell); }, 50);
5.4 滚轮滚动事件
const scrollXY = reactive({ x: 0, y: 0 }); const handleScroll = (e: any) => { scrollXY.x = e.target.scrollLeft; scrollXY.y = e.target.scrollTop; if (!startCell) return; const endCell: [number, number] = [ Math.floor((endXY.y + e.target.scrollTop - tableStartY) / cellHeight), Math.floor((endXY.x + e.target.scrollLeft - tableStartX) / cellWidth), ]; selectRange(...startCell, ...endCell); };
6. 样式相关代码
.item-hover { background-color: #bae0ff !important; } th, td { border: 1px solid black; border-collapse: collapse; } .right-bottom { margin-left: 200px; height: 800px; width: 800px; .page { width: 100%; height: 100%; overflow: scroll; margin: 0 auto; .table { overflow-x: auto; overflow-y: auto; .table-item { position: relative; font-size: 13px; display: flex; justify-content: center; align-items: center; width: 60px; height: 60px; .coordinates { position: absolute; left: 0; top: 0; font-size: 10px; } } .table-item-active { background-color: skyblue; } } } .page ::selection { background-color: transparent; } }
7. 总结
实际开发中,矩阵原点坐标可能会因为各种情况发生改变(这里默认是[0,0]),需要根据实际情况计算,否则框选区域会出现错位的情况。最后希望这个组件能帮助到有需要的人,欢迎大家提出建议!
以上就是基于Vue3+TypeScript实现鼠标框选功能的详细内容,更多关于Vue3 TypeScript鼠标框选的资料请关注脚本之家其它相关文章!