当前位置:首页 > React

react实现框选功能

2026-01-27 07:44:12React

实现框选功能的基本思路

框选功能通常涉及监听鼠标事件、绘制选框以及处理选框内的元素。React中可以通过结合原生DOM事件和状态管理来实现。

监听鼠标事件

在组件挂载时添加mousedownmousemovemouseup事件监听器,卸载时移除这些监听器。使用useEffect钩子来处理这些副作用。

react实现框选功能

useEffect(() => {
  const handleMouseDown = (e) => {
    // 记录起始坐标
  };

  const handleMouseMove = (e) => {
    // 计算选框尺寸并绘制
  };

  const handleMouseUp = (e) => {
    // 处理选框内的元素
  };

  document.addEventListener('mousedown', handleMouseDown);
  document.addEventListener('mousemove', handleMouseMove);
  document.addEventListener('mouseup', handleMouseUp);

  return () => {
    document.removeEventListener('mousedown', handleMouseDown);
    document.removeEventListener('mousemove', handleMouseMove);
    document.removeEventListener('mouseup', handleMouseUp);
  };
}, []);

绘制选框

使用useState存储选框的坐标和尺寸。在鼠标移动时更新这些值,并通过CSS绝对定位和transform来绘制选框。

react实现框选功能

const [selectionBox, setSelectionBox] = useState({
  startX: 0,
  startY: 0,
  width: 0,
  height: 0,
  isSelecting: false,
});

// 在handleMouseMove中更新选框
setSelectionBox({
  startX: Math.min(startX, currentX),
  startY: Math.min(startY, currentY),
  width: Math.abs(currentX - startX),
  height: Math.abs(currentY - startY),
  isSelecting: true,
});

处理选框内元素

使用Element.getBoundingClientRect()获取每个可选元素的边界框,检查是否与选框相交。可以通过intersects函数来判断两个矩形是否重叠。

const intersects = (rect1, rect2) => {
  return !(
    rect2.left > rect1.right ||
    rect2.right < rect1.left ||
    rect2.top > rect1.bottom ||
    rect2.bottom < rect1.top
  );
};

// 在handleMouseUp中筛选元素
const selectedElements = elements.filter(element => {
  const elementRect = element.getBoundingClientRect();
  const selectionRect = {
    left: selectionBox.startX,
    top: selectionBox.startY,
    right: selectionBox.startX + selectionBox.width,
    bottom: selectionBox.startY + selectionBox.height,
  };
  return intersects(elementRect, selectionRect);
});

完整组件示例

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

const SelectionBox = () => {
  const [selectionBox, setSelectionBox] = useState({
    startX: 0,
    startY: 0,
    width: 0,
    height: 0,
    isSelecting: false,
  });
  const [selectedItems, setSelectedItems] = useState([]);
  const itemsRef = useRef([]);

  useEffect(() => {
    const handleMouseDown = (e) => {
      setSelectionBox({
        startX: e.clientX,
        startY: e.clientY,
        width: 0,
        height: 0,
        isSelecting: true,
      });
    };

    const handleMouseMove = (e) => {
      if (!selectionBox.isSelecting) return;

      setSelectionBox(prev => ({
        ...prev,
        width: e.clientX - prev.startX,
        height: e.clientY - prev.startY,
      }));
    };

    const handleMouseUp = () => {
      if (!selectionBox.isSelecting) return;

      const selectionRect = {
        left: selectionBox.width > 0 
          ? selectionBox.startX 
          : selectionBox.startX + selectionBox.width,
        top: selectionBox.height > 0 
          ? selectionBox.startY 
          : selectionBox.startY + selectionBox.height,
        right: selectionBox.width > 0 
          ? selectionBox.startX + selectionBox.width 
          : selectionBox.startX,
        bottom: selectionBox.height > 0 
          ? selectionBox.startY + selectionBox.height 
          : selectionBox.startY,
      };

      const selected = itemsRef.current.filter(item => {
        const itemRect = item.getBoundingClientRect();
        return !(
          selectionRect.right < itemRect.left ||
          selectionRect.left > itemRect.right ||
          selectionRect.bottom < itemRect.top ||
          selectionRect.top > itemRect.bottom
        );
      });

      setSelectedItems(selected.map(el => el.id));
      setSelectionBox({
        startX: 0,
        startY: 0,
        width: 0,
        height: 0,
        isSelecting: false,
      });
    };

    document.addEventListener('mousedown', handleMouseDown);
    document.addEventListener('mousemove', handleMouseMove);
    document.addEventListener('mouseup', handleMouseUp);

    return () => {
      document.removeEventListener('mousedown', handleMouseDown);
      document.removeEventListener('mousemove', handleMouseMove);
      document.removeEventListener('mouseup', handleMouseUp);
    };
  }, [selectionBox.isSelecting]);

  return (
    <div style={{ position: 'relative', height: '100vh' }}>
      {selectionBox.isSelecting && (
        <div
          style={{
            position: 'absolute',
            left: selectionBox.width > 0 
              ? selectionBox.startX 
              : selectionBox.startX + selectionBox.width,
            top: selectionBox.height > 0 
              ? selectionBox.startY 
              : selectionBox.startY + selectionBox.height,
            width: Math.abs(selectionBox.width),
            height: Math.abs(selectionBox.height),
            backgroundColor: 'rgba(0, 0, 255, 0.2)',
            border: '1px solid blue',
          }}
        />
      )}

      {[1, 2, 3, 4, 5].map(id => (
        <div
          key={id}
          ref={el => itemsRef.current[id] = el}
          style={{
            position: 'absolute',
            left: `${id * 100}px`,
            top: `${id * 100}px`,
            width: '50px',
            height: '50px',
            backgroundColor: selectedItems.includes(id) 
              ? 'red' 
              : 'gray',
          }}
        />
      ))}
    </div>
  );
};

export default SelectionBox;

性能优化建议

对于大量可选项的场景,可以考虑以下优化:

  • 使用虚拟滚动只渲染可视区域内的元素
  • 对选框检测使用四叉树等空间分区技术
  • 节流鼠标移动事件处理

注意事项

  • 确保选框坐标计算考虑了页面滚动位置
  • 处理鼠标事件时注意阻止默认行为以避免冲突
  • 在严格模式下注意事件监听器的清理

标签: 功能react
分享给朋友:

相关文章

vue实现考试多选功能

vue实现考试多选功能

Vue实现考试多选功能 数据绑定与选项渲染 使用v-model绑定用户选择的答案数组,结合v-for循环渲染选项列表。示例代码: <template> <div>…

vue的艾特功能实现

vue的艾特功能实现

Vue 中的 @ 功能实现 Vue 中的 @ 符号是 v-on 指令的简写,用于绑定事件监听器。以下是具体实现方式: 基本语法 <button @click="handleClick"&…

react中monent如何获取日期

react中monent如何获取日期

使用 Moment.js 获取当前日期 在 React 中通过 Moment.js 获取当前日期,可以直接调用 moment() 函数。它会返回包含当前日期和时间的 Moment 对象。 impo…

react 如何引入jquery

react 如何引入jquery

引入 jQuery 到 React 项目 在 React 项目中引入 jQuery 可以通过多种方式实现,但需要注意 React 和 jQuery 操作 DOM 的方式可能冲突,因此建议仅在必要时使用…

react如何开发组件

react如何开发组件

React 组件开发基础 React 组件分为函数组件和类组件两种形式。函数组件是现代 React 开发的主流方式,结合 Hooks 可以实现完整功能。 函数组件示例: function Gree…

如何手写一个react

如何手写一个react

手写一个简单的 React 创建一个基础的 React 实现需要理解其核心概念:虚拟 DOM、组件、状态管理和渲染。以下是一个简化版的 React 实现。 创建虚拟 DOM 虚拟 DOM 是真实…