Plus 组件

拖拽移动

可通过拖拽改变位置

使用场景

用于设置元素可被拖动改变位置,支持限制拖拽范围,支持自定义触发拖动的元素。

代码演示

如何引入

<script lang="ts">
    import { dragMove } from '@fibrae/semi-ui-svelte';
</script>

基本用法

Svelte 中的 DragMove 是 dragMove attachment。在元素上写 {@attach dragMove(options)},该元素即可通过拖拽改变位置。

注意

  • DragMove 默认会将可拖拽的元素设置为 absolute 定位;需要保留元素原有布局位置(例如居中的 Modal)时,可设置 positionStrategy="relative"
  • dragMove 是 attachment 而不是组件:它让所写的那个元素可拖动,每个元素需要各自写一个。它会内联写入 position、top、left、cursor 和 touch-action,所以元素上请用 style: 指令,不要用会随状态重写的 style 属性,否则会覆盖这些值。选项在 attachment 运行时读取;传入新的选项对象会让它重新运行,并结束正在进行的拖动
Drag me
代码
<script lang="ts">
    import { dragMove } from '@fibrae/semi-ui-svelte';
</script>

<!-- dragMove positions the element absolutely, so the wrapper keeps room for it in the preview -->
<div style="height: 120px">
    <div {@attach dragMove()} style:align-items="center" style:background-color="var(--semi-color-primary)" style:border-radius="10px" style:color="var(--semi-white)" style:display="flex" style:font-weight="500" style:height="80px" style:justify-content="center" style:position="absolute" style:width="80px">
        Drag me
    </div>
</div>

限制拖动范围

传入 constrainer,可以是 'parent',也可以是返回限制可拖拽范围的元素的函数。

注意:constrainer 设置的元素需要为 relative 定位

Constrainer
Drag me
代码
<script lang="ts">
    import { dragMove } from '@fibrae/semi-ui-svelte';

    let container = $state<HTMLDivElement>();
</script>

<div bind:this={container} style="background-color: var(--semi-grey-2); width: 300px; height: 300px; padding: 5px; position: relative; color: var(--semi-white); font-weight: 500">
    <span style="color: var(--semi-color-text-0)">Constrainer</span>
    <div
        {@attach dragMove({ constrainer: () => container ?? null })}
        style:align-items="center"
        style:background-color="var(--semi-color-primary)"
        style:border-radius="10px"
        style:display="flex"
        style:height="80px"
        style:justify-content="center"
        style:left="80px"
        style:position="absolute"
        style:top="80px"
        style:width="80px"
    >
        Drag me
    </div>
</div>

自定义触发拖动的元素

可通过 handler 自定义触发拖动的元素。如果不设置, 则点击任意位置均可拖动;如果设置,则仅点击 handler 部分可拖动。

Constrainer
代码
<script lang="ts">
    import { IconTransparentStroked } from '@fibrae/semi-icons-svelte';
    import { dragMove } from '@fibrae/semi-ui-svelte';

    let container = $state<HTMLDivElement>();
    let handlerEl = $state<HTMLDivElement>();
</script>

<div bind:this={container} style="background-color: var(--semi-grey-2); width: 300px; height: 300px; padding: 5px; position: relative; color: var(--semi-white); font-weight: 500">
    <span style="color: var(--semi-color-text-0)">Constrainer</span>
    <div
        {@attach dragMove({ constrainer: () => container ?? null, handler: () => handlerEl ?? null })}
        style:align-items="center"
        style:background-color="var(--semi-color-primary)"
        style:border-radius="10px"
        style:display="flex"
        style:height="80px"
        style:justify-content="center"
        style:left="50px"
        style:position="absolute"
        style:top="50px"
        style:width="80px"
    >
        <div bind:this={handlerEl} style="width: fit-content; height: fit-content">
            <IconTransparentStroked size="large" />
        </div>
    </div>
</div>

自定义拖动后的位置处理

可通过 customMove 自定义拖动后的位置处理,该参数设置后,DragMove 组件内部将仅通过参数返回计算后的位置,不做设置,用户按需自行设置新位置。

蓝色色块点击可改变宽度,改变前后蓝色色块均不会超出范围限制

Constrainer
代码
<script lang="ts">
    import { dragMove } from '@fibrae/semi-ui-svelte';

    let container = $state<HTMLDivElement>();
    let startPoint: { x: number; y: number } | null = null;

    const customMove = (element: HTMLElement, top: number, left: number) => {
        if (container && left + 100 > container.offsetWidth) {
            element.style.right = `${container.offsetWidth - left - element.offsetWidth}px`;
            element.style.left = 'auto';
        } else {
            element.style.left = left + 'px';
        }
        element.style.top = top + 'px';
    };

    const toggleWidth = (element: HTMLElement) => {
        element.style.width = element.style.width === '60px' ? '100px' : '60px';
    };

    const onmousedown = (e: MouseEvent) => {
        startPoint = { x: e.clientX, y: e.clientY };
    };

    const onmouseup = (e: MouseEvent) => {
        if (startPoint && Math.abs(e.clientX - startPoint.x) < 5 && Math.abs(e.clientY - startPoint.y) < 5) {
            toggleWidth(e.currentTarget as HTMLElement);
        }
        startPoint = null;
    };

    const onkeydown = (e: KeyboardEvent) => {
        if (e.key === 'Enter') {
            toggleWidth(e.currentTarget as HTMLElement);
        }
    };
</script>

<span>蓝色色块点击可改变宽度,改变前后蓝色色块均不会超出范围限制 </span>
<br />
<br />
<div bind:this={container} style="background-color: var(--semi-grey-2); width: 300px; height: 300px; position: relative; padding: 10px; color: var(--semi-white); font-weight: 500">
    <span style="color: var(--semi-color-text-0)">Constrainer</span>
    <button
        {@attach dragMove({ constrainer: () => container ?? null, customMove })}
        style:align-items="center"
        style:background-color="var(--semi-color-primary)"
        style:border="none"
        style:border-radius="10px"
        style:box-sizing="content-box"
        style:color="inherit"
        style:display="flex"
        style:font="inherit"
        style:height="50px"
        style:justify-content="center"
        style:left="50px"
        style:padding="5px"
        style:position="absolute"
        style:top="50px"
        style:width="60px"
        type="button"
        {onkeydown}
        {onmousedown}
        {onmouseup}
    >
        Drag me
    </button>
</div>

API

dragMove(options?: DragMoveOptions) 返回 Attachment<HTMLElement>。DragMoveOptions 从 @fibrae/semi-ui-svelte 导出。

属性说明类型默认值
allowInputDrag在原生 input、textarea 上按下时是否允许拖动。booleanfalse
allowMove按下时判断是否允许拖动的函数,返回 false 则不拖动。(e: PointerEvent, element: HTMLElement) => boolean-
constrainer限制拖动范围的元素:'parent' 或返回该元素的函数,该元素需要为 relative 定位。'parent' | (() => HTMLElement | null)-
customMove自定义拖动后的位置处理;设置后只回传计算出的位置,不再改写元素的 top 与 left。(element: HTMLElement, top: number, left: number) => void-
handler触发拖动的元素或返回该元素的函数,缺省为被拖动元素本身。HTMLElement | (() => HTMLElement | null)-
onMouseDown鼠标或触控笔按下时调用。(e: PointerEvent) => void-
onMouseMove鼠标或触控笔移动时调用。(e: PointerEvent) => void-
onMouseUp鼠标或触控笔抬起时调用。(e: PointerEvent) => void-
onTouchCancel指针被系统取消(pointercancel)时调用。(e: PointerEvent) => void-
onTouchEnd触摸结束时调用。(e: PointerEvent) => void-
onTouchMove触摸移动时调用。(e: PointerEvent) => void-
onTouchStart触摸开始时调用。(e: PointerEvent) => void-
positionStrategy定位策略,relative 保留元素原有的布局位置。'absolute' | 'relative''absolute'