UNPKG

@douyinfe/semi-ui

Version:

A modern, comprehensive, flexible design system and UI library. Connect DesignOps & DevOps. Quickly build beautiful React apps. Maintained by Douyin-fe team.

237 lines (199 loc) 7.51 kB
--- localeCode: zh-CN order: 31 category: Plus title: DragMove 拖拽移动 icon: doc-dragmove dir: column brief: 可通过拖拽改变位置 --- ## 使用场景 用于设置元素可被拖动改变位置,支持限制拖拽范围,支持自定义触发拖动的元素。 ## 代码演示 ### 如何引入 DragMove 从 v2.71.0 开始支持 ```jsx import { DragMove } from '@douyinfe/semi-ui'; ``` ### 基本用法 被 `DragMove` 包裹的元素将能够通过拖拽改变位置。 ***注意*** 1. DragMove 会将可拖拽的元素设置为 absolute 定位 2. DragMove 需要将 DOM 事件监听器应用到 children 中,如果子元素是自定义的组件,你需要确保它能将属性传递至底层的 DOM 元素。支持以下类型的 children: 1. Class Component,不强制绑定ref,但需要确保 props 可被透传至真实的 DOM 节点上 2. 使用 forwardRef 包裹后的函数式组件,将 props 与 ref 透传到 children 内真实的 DOM 节点上 3. 真实 DOM 节点, 如 span,div,p... ```jsx live=true import React, { useRef, useEffect } from 'react'; import { DragMove } from '@douyinfe/semi-ui'; function Demo() { return ( <DragMove> <div style={{ backgroundColor: 'var(--semi-color-primary)',width: 80, height: 80, display: 'flex', alignItems: 'center',justifyContent: 'center', borderRadius: 10, fontWeight: 500, position: 'absolute', color: 'rgba(var(--semi-white), 1)' }} >Drag me</div> </DragMove> ); } ``` ### 限制拖动范围 传入 `constrainer`, 该函数返回限制可拖拽范围的元素。 ***注意:constrainer 设置的元素需要为 relative 定位*** ```jsx live=true import React, { useRef, useEffect } from 'react'; import { DragMove } from '@douyinfe/semi-ui'; function Demo() { const containerRef = React.useRef(); return ( <div style={{ backgroundColor: 'rgba(var(--semi-grey-2), 1)', width: 300, height: 300, padding: 5, position: 'relative', color: 'rgba(var(--semi-white), 1)', fontWeight: 500, }} ref={containerRef} > <span>Constrainer</span> <DragMove constrainer={() => containerRef.current} > <div style={{ backgroundColor: 'var(--semi-color-primary)', width: 80, height: 80, borderRadius: 10, display: 'flex', alignItems: 'center', justifyContent: 'center', position: 'absolute', top: 80, left: 80, }} >Drag me</div> </DragMove> </div> ) } ``` ### 自定义触发拖动的元素 可通过 `handler` 自定义触发拖动的元素。如果不设置, 则点击任意位置均可拖动;如果设置,则仅点击 handler 部分可拖动。 ```jsx live=true import React, { useRef, useEffect } from'react'; import { IconTransparentStroked } from '@douyinfe/semi-icons'; import { DragMove } from '@douyinfe/semi-ui'; function Demo(){ const handlerRef = React.useRef(); const containerRef = React.useRef(); return ( <div style={{ backgroundColor: 'rgba(var(--semi-grey-2), 1)', width: 300, height: 300, padding: 5, position: 'relative', color: 'rgba(var(--semi-white), 1)', fontWeight: 500, }} ref={containerRef} > <span>Constrainer</span> <DragMove handler={() => handlerRef.current} constrainer={() => containerRef.current} > <div style={{ backgroundColor: 'var(--semi-color-primary)', width: 80, height: 80, borderRadius: 10, position: 'absolute', top: 50, left: 50, display: 'flex', alignItems: 'center', justifyContent: 'center', }} > <div style={{ width: 'fit-content', height: 'fit-content' }} ref={handlerRef} ><IconTransparentStroked size={'large'}/></div> </div> </DragMove> </div> ) } ``` ### 自定义拖动后的位置处理 可通过 `customMove` 自定义拖动后的位置处理,该参数设置后,DragMove 组件内部将仅通过参数返回计算后的位置,不做设置,用户按需自行设置新位置。 ```jsx live=true import React, { useRef, useEffect } from'react'; import { DragMove } from '@douyinfe/semi-ui'; function CustomMove() { const containerRef = React.useRef(); const elementRef = React.useRef(); const startPoint = React.useRef(); const customMove = useCallback((element, top, left) => { if (left + 100 > containerRef.current.offsetWidth) { element.style.right = `${containerRef.current.offsetWidth - left - element.offsetWidth}px` element.style.left = 'auto'; } else { element.style.left = left + 'px'; } element.style.top = top + 'px'; }, []) const onMouseDown = useCallback((e) => { startPoint.current = { x: e.clientX, y: e.clientY, } }, []); const onMouseUp = useCallback((e) => { if (startPoint.current) { const { x, y } = startPoint.current; if (Math.abs(e.clientX - x) < 5 && Math.abs(e.clientY - y) < 5) { if (elementRef.current.style.width === '60px') { elementRef.current.style.width = '100px'; } else { elementRef.current.style.width = '60px'; } } } startPoint.current = null; }, []); return ( <> <span>蓝色色块点击可改变宽度,改变前后蓝色色块均不会超出范围限制 </span> <br /><br /> <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, }} 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, left: 50, borderRadius: 10, padding: 5 }} onMouseDown={onMouseDown} onMouseUp={onMouseUp} ref={elementRef} >Drag me</div> </DragMove> </div> </> ) } ``` ### API | 属性 | 说明 | 类型 | 默认值 | | --- | --- | --- | ----- | | allowInputDrag | 点击原生 input/textarea 时是否允许拖动 | boolean | false | | allowMove | 点击/触摸时是否允许拖动的判断函数 | (event: TouchEvent \| MouseEvent, element: HTMLElement) => boolean | - | | constrainer | 返回限制可拖拽的范围的元素 | () => HTMLElement \| 'parent' | - | | customMove | 自定义拖动后的位置处理| (element: HTMLElement, top: number, left: number) => void | -| | handler | 返回触发拖动的元素 | () => HTMLElement | - | | onMouseDown | 鼠标按下时的回调 | (e: MouseEvent) => void | - | | onMouseMove | 鼠标移动时的回调 | (e: MouseEvent) => void | - | | onMouseUp | 鼠标抬起时的回调 | (e: MouseEvent) => void | - | | onTouchCancel | 触摸取消时的回调 | (e: TouchEvent) => void | - | | onTouchEnd | 触摸结束时的回调 | (e: TouchEvent) => void | - | | onTouchMove | 触摸移动时的回调 | (e: TouchEvent) => void | - | | onTouchStart | 触摸开始时的回调 | (e: TouchEvent) => void | - |