鼠标事件 | 项目实战

306 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第4天

大家好,我是朝朝。 最近在做一个需求,如下图所示。

最主要涉及几个事件

// 网页端有效
onmousedown 鼠标按钮被按下
onmousemove 鼠标被移动 只要鼠标在移动就会触发
onmousedup 鼠标按键被松开
// 手机端有效
touchstart 鼠标按钮被按下
touchmove 鼠标被移动
touchend 鼠标按键被松开

其他补充的知识点

  1. 执行顺序 onmousedown -> onmousedup -> onclick
  2. 绑定和移除 addEventListener removeEventListener
  3. 可以添加多个事件,不会被覆盖
document.getElementById('div').addEventListener('click', () => {
  console.log(1);
})
document.getElementById('div').addEventListener('click', () => {
  console.log(2);
})
// 点击div元素会输出1 2
  1. 事件移除
绑定事件引用外部函数
function fun () {
  console.log(1);
}
document.getElementById('div').addEventListener('click', fun);
// 使用移除函数的时候,参数事件是侦听器函数本身才能成功移除函数 - fun
document.getElementById('div').removeEventListener('click', fun);

代码实现

<div class="draw-box" id="drawArea" />
.draw-box {
  width: 1024px;
  height: 768px;
  background-color: aqua;
  margin: 10px auto;
  position: relative;
  display: flex;
  flex-wrap: wrap;
}
.box {
  width: 30px;
  height: 30px;
  background-color: rgb(240, 232, 232);
  border: 1px solid rgb(211, 205, 205);
}

js部分

image.png

  1. 准备好元素
let drawArea = document.getElementById('drawArea'); // 获取画布元素

// 1、准备好24*32个格子
let arr = Array(24);
for(let i=0; i<24; i++) {
  arr[i] = Array(32);
}

// 2、创建格子 可以使用grid布局
for (let x = 0; x < 24; x++) {
  for (let y = 0; y < 32; y++) {
    let child = document.createElement('div');
    child.className = 'box'
    arr[x][y] = child;
    drawArea.appendChild(child);
  }
}

// 3、创建矩形框 隐藏选区矩形框
let drawReact = document.createElement('div');
drawReact.id = 'drawReact';
drawReact.style.boxSizing = 'border-box'
drawReact.style.border = '1px dashed black';
drawReact.style.position = 'absolute';
drawReact.style.display = 'none';
drawArea.appendChild(drawReact);
  1. 绑定事件
  • onmousedown 记录鼠标点击的位置,显示选区矩形框
  • onmousemove 记录鼠标移动的时候位置变化,改变选区矩形框宽高
  • onmouseup 移除onmousemove和onmouseup,执行回调函数,隐藏选区矩形框
function initDrawReact (id, fn) {
  // 绑定鼠标事件--onmousedown 
  drawArea.onmousedown = function ($event) {
    // 初始化
    var drawReact = document.getElementById('drawReact'); // 获取矩形框元素
    var areaInfo = drawArea.getBoundingClientRect(); // 返回元素的大小及其相对于视口的位置
    var reactWidth, reactHeight, reactTop, reactLeft; // 定义矩形框的宽、高、top、left
    // xy坐标是以画布的左上角为原点,方向矢量以向下和向右为正方向。
    var beginPoint = {}; // 标记起点
    var endPoint = {}; // 标记终点

    drawReact.style.display = 'block'; // 进入画布按下鼠标显示默认矩形框
    // 鼠标按下的位置作为矩形框的起点,横纵坐标记为 x0, y0
    beginPoint = { x: $event.clientX - areaInfo.x, y: $event.clientY - areaInfo.y }
    // 起点的横坐标
    var x0 = $event.clientX - areaInfo.x;
    // 起点的纵坐标
    var y0 = $event.clientY - areaInfo.y;
    // 绑定鼠标事件--onmousemove 
    drawArea.onmousemove = function ($event) {
      // 终点的横坐标
      var x1 = $event.clientX - areaInfo.x;
      // 终点的纵坐标
      var y1 = $event.clientY - areaInfo.y;
      // 对终点相对于起点的位置进行分类讨论
      if (x1 >= x0 && y1 < y0) {
        // x 越界处理
        reactWidth = $event.clientX >= areaInfo.right ? areaInfo.width - x0 : x1 - x0;
        reactLeft = x0;
        // y 越界处理
        reactHeight = $event.clientY <= areaInfo.top ? y0 : y0 - y1;
        reactTop = $event.clientY <= areaInfo.top ? 0 : y1;
        // 终点
        endPoint = { x: x0 + reactWidth, y: y0 - reactHeight };
      } else if (x1 < x0 && y1 < y0) {
        // x 越界处理
        reactWidth = $event.clientX <= areaInfo.left ? x0 : x0 - x1;
        reactLeft = $event.clientX <= areaInfo.left ? 0 : x1;
        // y 越界处理
        reactHeight = $event.clientY <= areaInfo.top ? y0 : y0 - y1;
        reactTop = $event.clientY <= areaInfo.top ? 0 : y1;
        // 终点
        endPoint = { x: x0 - reactWidth, y: y0 - reactHeight };
      } else if (x1 < x0 && y1 >= y0) {
        // x 越界处理
        reactWidth = $event.clientX <= areaInfo.left ? x0 : x0 - x1;
        reactLeft = $event.clientX <= areaInfo.left ? 0 : x1;
        // y 越界处理
        reactHeight = $event.clientY >= areaInfo.bottom ? areaInfo.height - y0 : y1 - y0;
        reactTop = y0;
        // 终点
        endPoint = { x: x0 - reactWidth, y: y0 + reactHeight };
      } else if (x1 >= x0 && y1 >= y0) {
        // x 越界处理
        reactWidth = $event.clientX >= areaInfo.right ? areaInfo.width - x0 : x1 - x0;
        reactLeft = x0
        // y 越界处理
        reactHeight = $event.clientY >= areaInfo.bottom ? areaInfo.height - y0 : y1 - y0;
        reactTop = y0;
        // 终点
        endPoint = { x: x0 + reactWidth, y: y0 + reactHeight };
      }

      drawReact.style.width = reactWidth + 'px'; // 宽
      drawReact.style.height = reactHeight + 'px'; // 高
      drawReact.style.top = reactTop + 'px';
      drawReact.style.left = reactLeft + 'px';
    }

    // 绑定鼠标事件--onmousedown 
    drawArea.onmouseup = function ($event) {
      drawArea.onmousemove = null
      drawArea.onmouseup = null
      // 回调
      var options = { reactWidth, reactHeight, reactTop, reactLeft, beginPoint, endPoint }
      fn(options);
      drawReact.parentNode.removeChild(drawReact);
      drawReact = null;
      drawReact = document.createElement('div');
      drawReact.id = 'drawReact'
      drawReact.style.boxSizing = 'border-box'
      drawReact.style.border = '1px dashed black'
      drawReact.style.position = 'absolute'
      drawReact.style.display = 'none'
      drawArea.appendChild(drawReact);
    }
  };
}
  1. 执行事件
  • 获取选区矩形框之后,切换颜色
function run() {
  initDrawReact('drawArea', (ops) => {
    let start = [Math.floor(ops.beginPoint.x / 32 ), Math.floor(ops.beginPoint.y / 32 )];
    let end = [Math.floor(ops.endPoint.x/ 32), Math.floor(ops.endPoint.y / 32)];
    let startBox = arr[start[1]][start[0]];
    let color = startBox.style.backgroundColor === 'red' ? 'rgb(240, 232, 232)' : 'red'
    if (!ops.endPoint.x) {
      let { style } = startBox;
      style.backgroundColor = color;
      return;
    }
    let x1 = Math.min(start[0], end[0]);  
    let x2 = Math.max(start[0], end[0]);

    let y1 = Math.min(start[1], end[1]);
    let y2 = Math.max(start[1], end[1]);

    for(let n=x1; n <= x2; n++) {
      for(let m=y1; m <= y2; m++) {
        arr[m][n].style.backgroundColor = color;
      }
    }
  })
}
run();

vue中实现

获取dom的方式是this.$refs.xxx,只能使用addEventListener和removeEventListener方式添加和移除监听函数,第二个参数必须使用相同函数的函数名,否则就去不掉这个监听函数。

参考:

原生 js 实现鼠标拖拽绘制矩形区域

onmousedown 事件

touchstart

代码质量一般哈哈