春節終結束了,忙得我頭疼。終于有時間弄自己的東西了。今天來寫一個關于拖動的實例講解。先看效果:
這是一個簡單的組件設計,如果用原生的js設計就很簡單,但在React中有些事件必須要多考慮一些。這是一個系列的文章,專門針對實際應用開發過程中的技術難點逐個講解,相信大家能用得著。
再次說明,我的示例都是基于MUI框架的,如果你不講究樣式的話,也可以直接采用原生dom的樣式設計。關于項目的創建及MUI的應用請查看我以往的文章,都是詳細的教程講解。
要點解說
設計的思路是只把把要移動的組件進行包裹就可以對其進行拖動,注意是拖動
,(不是拖放,我后期會出一個拖放的技術文章,請大家另行期待)
第一步: 首先是觸發機制,當在目標組件上按下左鍵時開始拖動,所以要有個標記記錄拖動的狀態。
const [isDragging, setIsDragging] = useState(false);
當按下左鍵時設置為 true
, 放開時設置為false
// 鼠標按下左鍵時的事件
const handleMouseDown = (event) => {if (event.button !== 0) return; // 按下的不是左鍵則直接返回。setIsDragging(true);
};// 鼠標放開左鍵時的事件
const handleMouseUp = (event) => {if(event.button !== 0) return;setIsDragging(false);
};
第二步: 單擊左鍵時要記錄下鼠標的位置信息,我們定義一個state
來記錄這個值:
const offsetX = useRef(0);
const offsetY = useRef(0);
第三步: 單擊左鍵不放進行移動,要記錄下相對于position
的變化量。因為要把這個變化反應到UI上,所以要用useState
:
const [position, setPosition] = useState({ x: 0, y: 0 });
繼續下面的代碼:
// 鼠標按下左鍵時的事件
const handleMouseDown = (event) => {if (event.button !== 0) return;offsetX.current = event.clientX - position.x;offsetY.current = event.clientY - position.y;setIsDragging(true);
};// 鼠標移動事件
const handleMouseMove = (event) => {if (isDragging) {setPosition({x: event.clientX - offsetX.current,y: event.clientY - offsetY.current});}
};
或許你會想直接把這些事件綁定到要拖動的組件上就行了,但這里有個問題,有時我們拖著拖著由于速度過快,鼠標就移出了組件,這就達不到我們的設計效果了。所以呢,我們要把相應的事件綁定到document
上是最靠譜的。我們只要把觸發事件綁定到拖動組件上就可以了。
if (isDragging) {document.addEventListener('mousemove', handleMouseMove);document.addEventListener('mouseup', handleMouseUp);
} else {document.removeEventListener('mousemove', handleMouseMove);document.removeEventListener('mouseup', handleMouseUp);
}
document
上綁定的事件在組件卸載后還要移除,所以我們用到useEffect
,完整的代碼如下:
import React, { useEffect, useRef, useState } from 'react';export default function Draggable({children}) {const [isDragging, setIsDragging] = useState(false);const [position, setPosition] = useState({ x: 0, y: 0 });const offsetX = useRef(0);const offsetY = useRef(0);useEffect(() => {const handleMouseMove = (event) => {if (isDragging) {setPosition({x: event.clientX - offsetX.current,y: event.clientY - offsetY.current});}};const handleMouseUp = (event) => {if(event.button !== 0) return;setIsDragging(false);};if (isDragging) {document.addEventListener('mousemove', handleMouseMove);document.addEventListener('mouseup', handleMouseUp);} else {document.removeEventListener('mousemove', handleMouseMove);document.removeEventListener('mouseup', handleMouseUp);}return () => {document.removeEventListener('mousemove', handleMouseMove);document.removeEventListener('mouseup', handleMouseUp);};}, [isDragging]);const handleMouseDown = (event) => {event.preventDefault();event.stopPropagation();if (event.button !== 0) return;offsetX.current = event.clientX - position.x;offsetY.current = event.clientY - position.y;setIsDragging(true);};return (<divstyle={{position: 'relative',userSelect: 'none',cursor: isDragging ? 'grabbing' : 'grab',transform: `translate(${position.x}px, ${position.y}px)`}}onMouseDown={handleMouseDown}>{children}</div>);
}
這樣一個基本的拖動組件就設計完成了。快試試效果吧。
import React from "react";
import Draggable from "../framework-kakaer/SModel/_Dragable";
import Box from "@mui/material/Box";
import Stack from "@mui/material/Stack";
import Typegraphy from "@mui/material/Typography";function DraggableTest() {return (<Boxsx={{display: 'flex',flexDirection: 'column',alignItems: 'center',justifyContent: 'center',height: '100vh',}}><Stack spacing={2}><Typegraphy variant="h4">拖動組件設計測試</Typegraphy><Draggable><Box sx={{ width: 100, height: 100, bgcolor: 'red' }} /></Draggable><Draggable><Box sx={{ width: 100, height: 100, bgcolor: 'blue' }} /></Draggable></Stack></Box>)
}export default DraggableTest;