拖拽移动(Semi Design)
一、使用场景
用于设置元素可被拖动改变位置,支持限制拖拽范围,支持自定义触发拖动的元素。
二、使用方式
可通过 customMove
自定义拖动后的位置处理,该参数设置后,DragMove 组件内部将仅通过参数返回计算后的位置,不做设置,用户按需自行设置新位置。
三、代码实现逻辑
-
定义组件:
-
创建一个名为
CustomMove
的函数组件。
-
-
使用
useRef
创建引用:-
containerRef
:用于引用容器元素,以便获取容器的尺寸信息。 elementRef
:用于引用可拖动的元素,以便直接操作DOM元素。startPoint
:用于存储鼠标按下时的起始点坐标,以便判断是否为点击事件。
-
-
使用
useCallback
定义事件处理函数:-
customMove
:自定义拖动逻辑,确保元素不会超出容器边界。 -
onMouseDown
:处理鼠标按下事件,记录起始点坐标。 -
onMouseUp
:处理鼠标释放事件,判断是否为点击事件并切换元素宽度。
-
-
提示信息:
-
在组件顶部渲染一个提示信息,说明蓝色色块的功能。
-
-
容器元素:
-
渲染一个
div
作为容器,设置其样式和引用。
-
-
可拖动元素:
-
使用
DragMove
组件包裹一个div
,设置其样式和事件处理函数,并绑定引用。
-
-
容器样式:
-
设置容器的背景色、宽度、高度、定位方式等样式。
-
-
可拖动元素样式:
-
设置元素的背景色、宽度、高度、定位方式、圆角等样式。
-
-
鼠标按下事件:
-
记录鼠标按下时的坐标。
-
-
鼠标释放事件:
-
判断鼠标移动距离,如果小于5像素则认为是点击事件,切换元素宽度。
-
-
导出
CustomMove
组件:-
使用
export default
导出组件,以便在其他地方使用。
-
四、代码展示
import React, { useCallback, useRef } from 'react'; // Import useCallback and useRef from react
import { DragMove } from '@douyinfe/semi-ui'; // Only import DragMove from semi-ui
/**
* 自定义拖动组件 CustomMove
* 该组件实现了一个可拖动的元素,并且可以通过点击来切换元素的宽度。
* 拖动时,元素不会超出其容器的边界。
*/
function CustomMove() {
// 创建容器元素的引用,用于获取容器的尺寸信息
const containerRef = useRef();
// 创建可拖动元素的引用,用于直接操作DOM元素
const elementRef = useRef();
// 存储鼠标按下时的起始点坐标,用于判断是否为点击事件
const startPoint = useRef();
/**
* 自定义拖动逻辑
* @param {HTMLElement} element - 拖动的元素
* @param {number} top - 元素相对于容器顶部的距离
* @param {number} left - 元素相对于容器左边的距离
*/
const customMove = useCallback((element, top, left) => {
// 如果元素超出容器右侧边界,则调整为右侧对齐
if (left + element.offsetWidth > containerRef.current.offsetWidth) {
element.style.right = `${containerRef.current.offsetWidth - left - element.offsetWidth}px`;
element.style.left = 'auto'; // 将 left 设置为 auto,以便 right 生效
} else {
element.style.left = `${left}px`; // 否则,正常设置 left
}
// 设置元素的 top 样式
element.style.top = `${top}px`;
}, []);
/**
* 鼠标按下事件处理函数
* @param {MouseEvent} e - 鼠标事件对象
*/
const onMouseDown = useCallback((e) => {
// 记录鼠标按下时的坐标
startPoint.current = {
x: e.clientX,
y: e.clientY,
};
}, []);
/**
* 鼠标释放事件处理函数
* @param {MouseEvent} e - 鼠标事件对象
*/
const onMouseUp = useCallback((e) => {
if (startPoint.current) {
const { x, y } = startPoint.current;
// 如果鼠标移动距离小于5像素,认为是点击事件
if (Math.abs(e.clientX - x) < 5 && Math.abs(e.clientY - y) < 5) {
// 切换元素宽度
if (elementRef.current.style.width === '60px') {
elementRef.current.style.width = '100px'; // 如果当前宽度是60px,则设置为100px
} else {
elementRef.current.style.width = '60px'; // 否则,设置为60px
}
}
}
// 清空起始点坐标
startPoint.current = null;
}, []);
return (
<>
{/* 提示信息 */}
<span style={
{ margin: '0 0 0 170px' }}>蓝色色块点击可改变宽度,改变前后蓝色色块均不会超出范围限制</span>
<br /><br />
{/* 容器 div */}
<div
style={
{
backgroundColor: 'rgba(var(--semi-grey-2), 1)', // 容器背景色
width: 300, // 容器宽度
height: 300, // 容器高度
position: 'relative', // 相对定位,用于子元素的绝对定位
padding: 10, // 内边距
color: 'rgba(var(--semi-white), 1)', // 文字颜色
fontWeight: 500, // 文字粗细
margin: '0 0 0 170px'
}}
ref={containerRef} // 绑定容器引用
>
<span>Constrainer</span> {/* 容器内的文字 */}
{/* 可拖动元素 */}
<DragMove
constrainer={() => containerRef.current} // 设置约束容器
customMove={customMove} // 自定义拖动逻辑
>
<div
style={
{
backgroundColor: 'var(--semi-color-primary)', // 元素背景色
width: 60, // 元素初始宽度
height: 50, // 元素高度
display: 'flex',
alignItems: 'center', // 垂直居中
justifyContent: 'center', // 水平居中
position: 'absolute', // 绝对定位
top: 50, // 初始 top 位置
left: 50, // 初始 left 位置
borderRadius: 10, // 圆角
padding: 5 // 内边距
}}
onMouseDown={onMouseDown} // 绑定鼠标按下事件
onMouseUp={onMouseUp} // 绑定鼠标释放事件
ref={elementRef} // 绑定元素引用
>
拖动 {/* 元素内的文字 */}
</div>
</DragMove>
</div>
</>
);
}
export default CustomMove;