React鼠标选择框

使用React模拟windows的鼠标选择框功能

git地址

大致思路是先判断鼠标左键是否按下,如果按下就记录下来,当鼠标移动时判断左键是否按下来让选择框显示出来,根据鼠标移动的位置来设置选择框的大小.

附上代码

import React, {useRef, useState} from 'react';

import "./index.css"

export default function App() {
  //  深克隆
  function deepClone(origin, target = {}) {
    for (let prop in origin) {
      if (origin.hasOwnProperty(prop)) {
        if (origin[prop] !== null && typeof (origin[prop]) === "object") {
          Array.isArray(origin[prop]) ? target[prop] = [] : target[prop] = {};
          deepClone(origin[prop], target[prop]);
        } else {
          target[prop] = origin[prop];
        }
      }
    }
    return target;
  }

  //  获取容器项目
  function getBoxItem() {
    let BoxItem = [];
    for (let i = 0; i < 8; i++) {
      for (let j = 0; j < 12; j++) {
        BoxItem.push({
          col: j,
          row: i,
          selected: false,
        });
      }
    }
    return BoxItem;
  }

  const ITEM_WIDTH = 68;
  const ITEM_LEFT = 71;
  const [BoxItem, setBoxItem] = useState(getBoxItem());
  const BoxItemContainer = useRef();  //  Box容器
  const [showSelectBox, setShowSelectBox] = useState(false);  // 是否开启框选功能
  const [startX, setStartX] = useState(0);  //  鼠标按下起始位置横坐标
  const [startY, setStartY] = useState(0);  //  鼠标按下起始位置纵坐标
  const [selectBoxWidth, setSelectBoxWidth] = useState(0);  //  选择框宽度
  const [selectBoxHeight, setSelectBoxHeight] = useState(0);  //  选择框高度
  const [selectBoxLeft, setSelectBoxLeft] = useState(0);  //  选择框原点横坐标
  const [selectBoxTop, setSelectBoxTop] = useState(0);  //  选择框原点纵坐标
  const [leftMouseDown, setLeftMouseDown] = useState(false);  // 鼠标左键是否按下
  let cloneBoxItem = [];

  //  鼠标左键按下监听器
  function mouseDownListener(e) {
    if (e.buttons !== 1) {      //  判断鼠标左键按下
      return;
    }
    e.preventDefault();
    setLeftMouseDown(true);
    setStartX(e.clientX);  //	记录鼠标按下时的位置
    setStartY(e.clientY);
  }

  //  鼠标移动监听器
  function mouseMoveListener(e) {
    if (!leftMouseDown) {
      return;
    }
    e.preventDefault();
    let {left, top} = BoxItemContainer.current.getBoundingClientRect(); //  容器上边(左边)到视窗上边(左边)的距离
    let [nowX, nowY] = [e.clientX - left, e.clientY - top]; //  鼠标当前的位置
    setSelectBoxLeft(Math.min(nowX, startX));
    setSelectBoxTop(Math.min(nowY, startY));
    setSelectBoxWidth(Math.abs(nowX - startX));
    setSelectBoxHeight(Math.abs(nowY - startY));
    setShowSelectBox(true);
    if (selectBoxWidth > 10 && selectBoxHeight > 10) {
      cloneBoxItem = deepClone(BoxItem, cloneBoxItem);
      cloneBoxItem.forEach(item => {
        item.selected = (ITEM_WIDTH + item.col * ITEM_LEFT) > selectBoxLeft
          && (ITEM_WIDTH + item.row * ITEM_LEFT) > selectBoxTop
          && item.col * ITEM_LEFT < selectBoxLeft + selectBoxWidth
          && item.row * ITEM_LEFT < selectBoxTop + selectBoxHeight
      });
      setBoxItem(cloneBoxItem);
    }
  }

  //  鼠标左键抬起监听器
  function mouseUpListener() {
    if (!leftMouseDown) {
      return;
    }
    setShowSelectBox(false);
    setLeftMouseDown(false);
  }

  //  鼠标移出BoxItem面板监听器
  function mouseLeaveListener() {
    if (!leftMouseDown) {
      return;
    }
    setShowSelectBox(false);
    setLeftMouseDown(false);
  }

  return (
    <div onMouseLeave={mouseLeaveListener} className="app">
      <div className="box"
           ref={BoxItemContainer}
           onMouseDown={e => mouseDownListener(e)}
           onMouseMove={e => mouseMoveListener(e)}
           onMouseUp={mouseUpListener}
      >
        {
          BoxItem.map(well => (
            <div key={well.row * 12 + well.col}
                 style={{borderStyle: well.selected ? "dashed" : "solid"}}
            >
            </div>
          ))
        }
        {
          showSelectBox
            ? <div className="SelectBox" style={{
              left: `${selectBoxLeft}px`,
              top: `${selectBoxTop}px`,
              width: `${selectBoxWidth}px`,
              height: `${selectBoxHeight}px`
            }}/>
            : null
        }
      </div>
    </div>
  );
}

首先是判断鼠标左键按下

  //  鼠标左键按下监听器
  function mouseDownListener(e) {
    if (e.buttons !== 1) {      //  判断鼠标左键按下
      return;
    }
    e.preventDefault();
    setLeftMouseDown(true);
    setStartX(e.clientX);
    setStartY(e.clientY);
  }

左键按下时记录下来,便于后面鼠标移动时判断是否开启选择框,然后把原点设置为容器的左上角.

鼠标移动功能

  //  鼠标移动监听器
  function mouseMoveListener(e) {
    if (!leftMouseDown) {
      return;
    }
    e.preventDefault();
    let {left, top} = BoxItemContainer.current.getBoundingClientRect(); //  容器上边(左边)到视窗上边(左边)的距离
    let [nowX, nowY] = [e.clientX - left, e.clientY - top]; //  鼠标当前的位置
    setSelectBoxLeft(Math.min(nowX, startX));
    setSelectBoxTop(Math.min(nowY, startY));
    setSelectBoxWidth(Math.abs(nowX - startX));
    setSelectBoxHeight(Math.abs(nowY - startY));
    setShowSelectBox(true);
    if (selectBoxWidth > 10 && selectBoxHeight > 10) {
      cloneBoxItem = deepClone(BoxItem, cloneBoxItem);
      cloneBoxItem.forEach(item => {
        item.selected = (ITEM_WIDTH + item.col * ITEM_LEFT) > selectBoxLeft
          && (ITEM_WIDTH + item.row * ITEM_LEFT) > selectBoxTop
          && item.col * ITEM_LEFT < selectBoxLeft + selectBoxWidth
          && item.row * ITEM_LEFT < selectBoxTop + selectBoxHeight
      });
      setBoxItem(cloneBoxItem);
    }
  }

在这里插入图片描述
移动时获取鼠标的实时位置,设置选择框的大小和位置,这里选择框距左和距上的距离要取鼠标按下时的位置和实时位置的最小值.

在判断元素是否被选中时,这里用了一个取巧的方法,因为96个元素大小相同,所以我们可以计算出每个元素距容器左上角的具体位置,来判断元素是否被选择框覆盖,如果覆盖就设置元素边框虚线

 //  鼠标左键抬起监听器
  function mouseUpListener() {
    if (!leftMouseDown) {
      return;
    }
    setShowSelectBox(false);
    setLeftMouseDown(false);
  }

  //  鼠标移出BoxItem面板监听器
  function mouseLeaveListener() {
    if (!leftMouseDown) {
      return;
    }
    setShowSelectBox(false);
    setLeftMouseDown(false);
  }

鼠标抬起的功能很简单,就是把鼠标按下的标记和选择框开启的标记设置为false

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值