个人博客网站欢迎交流:萤火之森:https://blog.xkongkeji.com

近段时间做一个编辑器,就需要一个鼠标拖拽选区的功能,方便批量选中元素,进行相应操作,所有就有了这篇文章。

效果展示


建立选区组件

1、要想选中元素,肯定要先建立选区

  • 根据两个坐标点确定选区位置,并绘制出选区
  • 根据两个坐标删除选区的宽与高
let size = computed(() => {let width =props.endPoint.x === 0? 0: Math.abs(props.startPoint.x - props.endPoint.x);let height =props.endPoint.y === 0? 0: Math.abs(props.startPoint.y - props.endPoint.y);return {width,height,};
  • 确定起始坐标点

    • 不管从何处点击都需要找到两个坐标点所绘制的矩形的左上角的坐标点。
    • 左上角的坐标点,很明显是所有坐标的最小值,也就是X,Y取最小的值的点(如下)
    • 还需要考虑终点还没有产生时的情况,也就是排除终点为初始值也就是0的情况
 let Point = computed(() => {let x =props.endPoint.x === 0? props.startPoint.x: Math.min(props.startPoint.x, props.endPoint.x);let y =props.endPoint.y === 0? props.startPoint.y: Math.min(props.startPoint.y, props.endPoint.y);return {x,y,};});zaige
  • 这样就可绘制出一个选区

2、根据选区,筛选出选中的元素

  • 筛选选区的元素主要是根据nodeType选中出编辑区所有可选择的元素节点。
  • 怎么才算可选元素,这就得看自己需要,我在元素节点上标注了canChecked,通过这个属性排除非可选元素
 * 获取该元素下可以被选中的元素集合* @param parentElement 父元素* @param keyCode 可选元素标识* @returns*/
function getChildrens(parentElement: HTMLElement, keyCode: string) {const ary = [];const childs = parentElement.childNodes;for (let i = 0; i < childs.length; i++) {if (childs[i].nodeType === 1) {if ((childs[i] as HTMLElement).getAttribute(keyCode) !== null) {ary.push(childs[i]);}}}return ary as Array<HTMLElement>;
}

3、判断节点是否在选区内

  • 这个主要根据getBoundingClientRect方法返回的信息

  • 当选区的top与left小于判断元素,bottom与大于判断元素,即认为该元素在选区内。
/*** 判断元素是否在选区内* @param selectBoxElement 选区元素* @param canCheckedElements  可选中元素列表*/
function judgeContainElement(selectBoxElement: HTMLElement,canCheckedElements: Array<HTMLElement>
) {const ContainElement: Array<HTMLElement> = [];const { left, right, bottom, top } = selectBoxElement.getBoundingClientRect();canCheckedElements.forEach((item) => {const child = item.getBoundingClientRect();if (child.left > left &&child.top > top &&child.bottom < bottom &&child.right < right) {ContainElement.push(item);}});return ContainElement;
}

4、至此我们已经可以获得到选区内选中元素了,然后就可对选中元素做需要的操作了,也就是如效果图所示/

完整代码附上

1、组件基础结构文件

<template><divid="select-area"class="select-area":style="[{ width: size.width + 'px' },{ height: size.height + 'px' },{ top: Point.y + 'px' },{ left: Point.x + 'px' },]"></div>
</template><script lang="ts">
import { computed, defineComponent, PropType } from 'vue';
interface Point {x: number;y: number;
}
export default defineComponent({name: 'SelectArea',props: {startPoint: {type: Object as PropType<Point>,required: true,},endPoint: {type: Object as PropType<Point>,required: true,},},setup(props) {let Point = computed(() => {let x =props.endPoint.x === 0? props.startPoint.x: Math.min(props.startPoint.x, props.endPoint.x);let y =props.endPoint.y === 0? props.startPoint.y: Math.min(props.startPoint.y, props.endPoint.y);return {x,y,};});let size = computed(() => {let width =props.endPoint.x === 0? 0: Math.abs(props.startPoint.x - props.endPoint.x);let height =props.endPoint.y === 0? 0: Math.abs(props.startPoint.y - props.endPoint.y);return {width,height,};});return {Point,size,};},
});
</script>
<style lang="less" scoped>
.select-area {position: fixed;background-color: rgba(255, 192, 203, 0.1);border: 1px solid red;z-index: 9;
}
</style>

2、导出组件文件

import { createVNode, render } from 'vue';
import SelectAreaConstructor from './SelectArea.vue';
let instence: HTMLElement | undefined;
let instenceIsExit = false;
const SelectArea = function(options: any) {if (instenceIsExit) {document.body.removeChild(instence as HTMLElement);instenceIsExit = false;}const vm = createVNode(SelectAreaConstructor, options);const container = document.createElement('div');render(vm, container);instence = container.firstElementChild as HTMLElement;document.body.appendChild(instence);instenceIsExit = true;return instence;
};const close = () => {if (instenceIsExit) {// eslint-disable-next-line @typescript-eslint/no-unused-varsinstenceIsExit = false;document.body.removeChild(instence as HTMLElement);instence = undefined;}
};
export { SelectArea, close };

3、应用文件setup部分

 setup() {let selectProps = reactive({startPoint: {x: 0,y: 0,},endPoint: {x: 0,y: 0,},});let mouseKey = ref(false); // 是否监听鼠标移动(移出编辑区范围,不再监听鼠标移动事件)let mouseComplete = ref(false); // 鼠标移动事件是否完成(鼠标按下到抬起的流程)const headleMouseDown = (e: MouseEvent) => {close();selectProps.startPoint.x = e.clientX;selectProps.startPoint.y = e.clientY;SelectArea(selectProps);mouseKey.value = true;mouseComplete.value = false;};const headleMouseMove = (e: MouseEvent) => {if (mouseKey.value && !mouseComplete.value) {selectProps.endPoint.x = e.clientX;selectProps.endPoint.y = e.clientY;const div = document.querySelector('#select-area');const parent = document.querySelector('.edit-area');const containDiv = selectElement(parent as HTMLElement,div as HTMLElement,'canChecked');containDiv.canCheckedElements.forEach((item) => {item.style.border = 'none';});containDiv.containElements.forEach((item) => {item.style.border = '1px solid red';item.style.cursor = 'move';});}};const headleDrag = (e: MouseEvent) => {// 防止与拖动事件冲突e.preventDefault();};const headleMouseUp = () => {mouseKey.value = false;mouseComplete.value = true;selectProps.startPoint.x = 0;selectProps.startPoint.y = 0;selectProps.endPoint.x = 0;selectProps.endPoint.y = 0;close();};window.addEventListener('mousedown', headleMouseDown);window.addEventListener('mousemove', headleMouseMove);window.addEventListener('mouseup', headleMouseUp);onUnmounted(() => {window.removeEventListener('mousedown', headleMouseDown);window.removeEventListener('mousemove', headleMouseMove);window.removeEventListener('mouseup', headleMouseUp);});const saveWeekly = () => {console.log('click');};return {headleMouseDown,headleMouseMove,headleMouseUp,headleDrag,saveWeekly,};},

4、辅助工具函数文件

/*** 获取该元素下可以被选中的元素集合* @param parentElement 父元素* @param selectBoxElement 选择框元素* @param keyCode 可选元素标识* @returns*/
function selectElement(parentElement: HTMLElement,selectBoxElement: HTMLElement,keyCode: string
) {if (keyCode) {}const canCheckedElements = getChildrens(parentElement, keyCode);const containElements = judgeContainElement(selectBoxElement,canCheckedElements);return {containElements,canCheckedElements,};
}
export { selectElement };/**** 获取该元素下可以被选中的元素集合* @param parentElement 父元素* @param keyCode 可选元素标识* @returns*/
function getChildrens(parentElement: HTMLElement, keyCode: string) {const ary = [];const childs = parentElement.childNodes;for (let i = 0; i < childs.length; i++) {if (childs[i].nodeType === 1) {if ((childs[i] as HTMLElement).getAttribute(keyCode) !== null) {ary.push(childs[i]);}}}return ary as Array<HTMLElement>;
}
function judgeContainElement(selectBoxElement: HTMLElement,canCheckedElements: Array<HTMLElement>
) {const ContainElement: Array<HTMLElement> = [];const { left, right, bottom, top } = selectBoxElement.getBoundingClientRect();canCheckedElements.forEach((item) => {const child = item.getBoundingClientRect();if (child.left > left &&child.top > top &&child.bottom < bottom &&child.right < right) {ContainElement.push(item);}});return ContainElement;
}

鼠标拖拽建立选区,选中元素相关推荐

  1. 鼠标拖拽---自定义布局(电影院)

    鼠标拖拽点击-电影院自定义布局 标签 鼠标拖拽.自定义布局 效果 自定义布局-电影院 目的 疫情期间,电影院都是隔空而坐,想用html.css.js.jq做个简单的自定义布局. 介绍 自定义布局中有银 ...

  2. 弹出框的鼠标拖拽事件

    几个与定位有关的概念: js: clientX/clientY属性:获得事件发生时鼠标指针在视口中的水平和垂直坐标. screenX/screenY属性:获取鼠标事件发生时鼠标光标相对于整个电脑屏幕的 ...

  3. 自动化测试之鼠标悬浮操作、双击、鼠标拖拽

    鼠标操作.封装在ActionChains中 -move_to_element -double_click 双击 -context_click right_click 右键 -drag_and_drop ...

  4. java鼠标左键按下后拖动实现多选_鼠标拖拽多选功能

    鼠标拖拽多选功能 *{ box-sizing:border-box; } ul{ width:500px; height:auto; margin:0; padding:20px; font-size ...

  5. html5的鼠标拖拽

    鼠标拖拽 <!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8 ...

  6. javascript鼠标拖拽的那些事情

    <html> <head> <title>javascript鼠标拖拽的那些事情</title> <meta http-equiv="C ...

  7. html点击控制盒子左右移动,JS实现鼠标拖拽盒子移动及右键点击盒子消失效果示例...

    JS实现鼠标拖拽盒子移动及右键点击盒子消失效果示例 发布时间:2020-10-04 12:47:25 来源:脚本之家 阅读:121 作者:s_psycho 本文实例讲述了JS实现鼠标拖拽盒子移动及右键 ...

  8. 计算机图形学 实现鼠标拖拽图元

    计算机图形学 实现鼠标拖拽图元 问题描述 当计算机图形学中需要实现拖拽图元时,关键在于如何判断鼠标是否在拖拽,鼠标被拖拽时的移动 确定鼠标是在图元内部按下还是按下后移动到图元内 确定鼠标的移动,点击图 ...

  9. 拖拽之路(五):自定义QListWidget实现美观的拖拽样式(拖拽不影响选中 + doAutoScroll)

    环境配置 :MinGW + QT 5.12 效果图: 这种自定义拖拽样式的灵感来自于Chrome浏览器的书签栏.文章中所使用的自定义QListWidget来自于:自定义QListWidget实现ite ...

最新文章

  1. Maven2的配置文件settings.xml
  2. iOS之深入解析预乘透明度Premultiplied Alpha
  3. php的autoload机制
  4. 需要额外端口信息_NR逻辑天线端口介绍
  5. python多进程与多线程实验
  6. poi导出excel 并实现合并单元格
  7. 几步看懂晶闸管的结构和工作原理
  8. golang(4)使用beego + ace admin 开发后台系统 CRUD
  9. 【复习笔记】电分-第三章-电力系统潮流分析与计算
  10. 盘点机PDA搭配蓝牙便携打印机,条码标签打印,超市仓库条码管理,条码标签纸
  11. Coall-jtag烧写器功能与特色
  12. 《构建之法》CH5~6读书笔记 PB16110698 第九周(~5.15)
  13. php seo技巧,十个对排名最有效的SEO​技巧
  14. 护眼灯频闪是什么意思?如何消除led灯频闪
  15. codeforces1670F Jee, You See?(DP/位运算/前缀和/组合数)
  16. Machine Learning Basics(2)
  17. 2.CUDA 编程手册中文版---编程模型
  18. 认知决定你的格局和财富差距
  19. Git+Pandoc配置Word文档版本控制
  20. 地图的出图设置和经纬网的调整(基础篇)

热门文章

  1. mysql的mis安装各种版本
  2. linux进度条脚本,linux shell进度条实现方法
  3. 有的游戏不能叫游戏,只能叫具有交互功能的动画效果
  4. JS获取fileupload文件路径
  5. Unity Fungus插件的对话系统简单使用
  6. docker geovis_GitHub - ThinkBlue1991/cronjob-operator
  7. 轻量级网速、CPU及内存利用率监控软件
  8. 修改C盘系统文件 Hosts 两种方法
  9. 【集成电路】深度解密:集成电路系统级封装(SiP)技术和应用 !
  10. html img 圆头像_html圆形头像简易实现