Documentação/Guias
Eixos, limites e grades
Trave arrastes em um eixo, mantenha-os dentro de uma caixa, encaixe-os em uma grade ou em guias suas — para um item, uma lista ou uma parte inteira da página.
Esta página foi traduzida por IA e pode conter imprecisões. Ler o original em inglês
Um arraste pode ser preso a um eixo, mantido dentro de uma caixa, encaixado em uma grade ou desviado por uma regra sua. As regras são modificadores: eles ajustam o deslocamento do arraste a cada quadro. Defina-os em um draggable, em uma lista ou em uma parte da página, e o overlay, os alvos de drop e o drop seguem todos eles.
Travar um eixo
Os clipes de uma linha do tempo deslizam pela sua faixa. Desligue a trava e eles também podem trocar de faixa.
Carregando a demo…
<script setup lang="ts">
import { DnDProvider } from '@vue-dnd-kit/core';
import Timeline from './Timeline.vue';
</script>
<template>
<DnDProvider>
<Timeline />
</DnDProvider>
</template>const { isDragging } = makeDraggable(el, {
id: () => props.clip.id,
axis: () => (props.locked ? 'x' : undefined),
});
axis: 'x' move só na horizontal, 'y' só na vertical. makeDraggable, makeSortableList,
makeSortableTree, makeSortableItem e makeDragArea aceitam essa opção:
makeSortableList(listRef, { items: tracks, axis: 'y' });
- A trava roda depois de todos os modificadores, então ela se mantém, não importa o que eles façam: um encaixe ou um limite não consegue tirar o item da sua linha.
- A linha está no conteúdo, não na tela: ela se move com a lista (ou o elemento pai) onde o arraste começou. Role a página no meio do arraste e o item continua na mesma linha.
- O
axisdo próprio item vence o da lista ou da árvore, e o de um draggable (ou da lista dele) vence o da área de arraste. - Ele é lido quando um arraste começa, então um getter pode alterná-lo, como acima.
- Uma árvore travada em
'y'ainda aninha: a profundidade passa a vir de parar sobre o meio de uma linha.
axis aos modificadores restrictToVerticalAxis e restrictToHorizontalAxis. Eles fazem
a mesma coisa, mas, como modificadores comuns, rodam no meio da cadeia, e um modificador depois
deles (uma grade que encaixa os dois eixos) pode tirar o item do eixo de novo.Modificadores
Um modificador ajusta o deslocamento do arraste a cada quadro. O resultado dele move o overlay
e é o ponto onde os alvos são procurados, então uma lista ordena pela posição travada,
limitada ou encaixada, e a position do drop é onde a caixa terminou.
| Modificador | O que faz |
|---|---|
restrictToParent | Mantém o overlay dentro do contêiner em que o arraste começou: a lista ordenável, ou o elemento pai de um draggable simples. |
restrictToElement(target) | Mantém o overlay dentro de um elemento: um template ref, um getter ou o próprio elemento. |
restrictToWindowEdges | Mantém o overlay dentro da janela. |
restrictToFirstScrollableAncestor | Mantém o overlay dentro do primeiro ancestral rolável do elemento arrastado. |
snapToGrid(size) | Encaixa o overlay em uma grade (abaixo). |
snapCenterToCursor | Centraliza o overlay sob o ponteiro em vez de manter o deslocamento de onde ele foi pego. |
restrictToVerticalAxis | Move só na vertical. Prefira axis: 'y'. |
restrictToHorizontalAxis | Move só na horizontal. Prefira axis: 'x'. |
modifiers é uma opção do provider, das áreas de arraste, das listas e árvores, e dos draggables
e itens ordenáveis. Todas, menos a do provider, também aceitam um ref ou um getter, lido quando o
arraste começa.
Manter dentro
A própria imagem em uma videochamada fica na tela. Troque o limite e arraste-a para bem longe:
Carregando a demo…
<script setup lang="ts">
import { DnDProvider } from '@vue-dnd-kit/core';
import CallScreen from './CallScreen.vue';
</script>
<template>
<DnDProvider>
<CallScreen />
</DnDProvider>
</template>const { isDragging } = makeDraggable(el, {
id: 'self-view',
modifiers: () => props.modifiers, // [restrictToParent], [restrictToWindowEdges] or []
});
restrictToParenta mantém na tela, o elemento pai dela. Para um item ordenável, o "pai" é a lista dele, onde quer que a lista esteja no DOM.restrictToWindowEdgesdeixa que ela saia da tela, mas não da janela. Solta fora da tela, ela não está sobre um alvo de drop, então volta ao lugar.- Sem limite, ela vai para qualquer lugar.
Um limite mantém o overlay dentro, no tamanho que ele tem: com overlayFit: 'slot', uma caixa
que cresce sobre uma lista de itens mais largos é mantida dentro no novo tamanho.
Encaixar em uma grade
Os nós de um fluxograma se encaixam na grade da tela de desenho. Troque o tamanho da célula e o deslizamento:
Carregando a demo…
<script setup lang="ts">
import { DnDProvider } from '@vue-dnd-kit/core';
import Flowchart from './Flowchart.vue';
</script>
<template>
<DnDProvider>
<Flowchart />
</DnDProvider>
</template>const { isDragging } = makeDraggable(el, {
id: () => props.node.id,
modifiers: () => [snapToGrid({ x: props.grid, y: props.grid, smooth: props.smooth })],
});
snapToGrid(24) encaixa os dois eixos em células de 24 px. Um objeto define mais:
| Opção | Significado |
|---|---|
x, y | O tamanho da célula em pixels, por eixo. Deixe um de fora para encaixar só o outro: { y: 15 } encaixa um evento em quartos de hora e o deixa se mover livremente entre os dias. |
origin | 'container' (padrão): o canto superior esquerdo do overlay pousa em linhas de grade traçadas a partir do canto superior esquerdo do contêiner — a lista ordenável, ou o pai de um draggable simples. 'delta': o próprio deslocamento é arredondado, então um item que começou fora da grade continua fora dela e se move por células inteiras a partir de onde estava. Escolha isso para itens colocados em qualquer lugar (um canvas livre) que devem manter o próprio alinhamento. |
smooth | true ou { duration, easing }: o overlay desliza entre as células (80 ms por padrão). Os alvos usam a posição encaixada na hora. |
Com origin: 'container', desenhe a grade a partir da border box do contêiner: a tela de desenho
acima tem a moldura como box-shadow, não como border, então as linhas dela começam bem na
borda.
Um modificador seu
As caixas de um slide pulam para as bordas e os centros das outras quando chegam perto, e guias rosa mostram as linhas em que elas ficam:
Carregando a demo…
<script setup lang="ts">
import { DnDProvider } from '@vue-dnd-kit/core';
import Slide from './Slide.vue';
</script>
<template>
<DnDProvider>
<Slide />
</DnDProvider>
</template>import type { Modifier } from '@vue-dnd-kit/core';
/** Near a line, the box jumps onto it. */
const magnet: Modifier = ({ transform, overlayRect, containerRect, active }) => {
if (!containerRect) return transform;
const id = String(active.id);
const left = overlayRect.left - containerRect.left;
const top = overlayRect.top - containerRect.top;
return {
x: transform.x + pull(left, overlayRect.width, lines(id, 'x')),
y: transform.y + pull(top, overlayRect.height, lines(id, 'y')),
};
};
// In order: the magnet pulls, then the slide's edges hold.
const modifiers = [magnet, restrictToParent];
Um modificador recebe transform, o deslocamento desde o início do arraste como os modificadores
anteriores o deixaram, e retorna um novo. Ele também recebe o que pode precisar para calculá-lo:
| Argumento | O que é |
|---|---|
transform | O deslocamento até agora. |
overlayRect | A caixa do overlay movida por transform, no tamanho que ela tem agora. |
activeRect | O elemento arrastado quando o arraste começou. |
containerRect | O contêiner em que o arraste começou (a lista ordenável, ou o elemento pai), onde ele está agora: acompanha a rolagem. |
windowRect | A janela. |
origin | O ponteiro quando o arraste começou. |
active, over | O arraste e o alvo sob ele. |
Os modificadores rodam a cada quadro: mantenha-os rápidos e sem efeitos colaterais. As guias
acima são desenhadas a partir do delta do arraste no onDragMove de um monitor, que é o
deslocamento depois de todos os modificadores. Tipos em Modificadores.
A ordem
Modificadores de vários lugares rodam um depois do outro, cada um a partir do resultado do anterior:
- os
modifiersdo provider; - os
modifiersda área de arraste em que o arraste começou; - os
modifiersdo próprio item arrastado ou, quando ele não tem, os da lista ou da árvore dele; - o
restrictToAreada área de arraste; - a trava de
axis.
Dentro de um array, eles rodam em ordem, então coloque encaixes e ímãs antes dos limites: um
limite por último mantém a caixa dentro, não importa o que os outros fizeram. O slide acima roda
[magnet, restrictToParent]; na ordem inversa, o ímã poderia puxar uma caixa presa na borda do
slide alguns pixels para além dela.
Os modifiers de um item substituem os da lista; eles não se somam a eles. Os limites da área
vêm depois do encaixe das etapas 1–3, então um item encaixado ainda fica dentro, e a trava de eixo
vem por último de todos.
Listas limitadas mantêm as pontas
Um painel de camadas: as linhas só se movem para cima e para baixo, e nunca saem do painel. Arraste a linha de cima até o fim:
Carregando a demo…
<script setup lang="ts">
import { DnDProvider } from '@vue-dnd-kit/core';
import Layers from './Layers.vue';
</script>
<template>
<DnDProvider>
<Layers />
</DnDProvider>
</template>const { items } = makeSortableList(list, { items: layers, axis: 'y', modifiers: [restrictToParent] });
Um limite segura o overlay na borda da caixa enquanto o ponteiro continua. Assim que o ponteiro sai do overlay ao longo do eixo da lista, a lista passa a ordenar pelo ponteiro, então o primeiro e o último lugar continuam ao alcance, não importa como você segura o item. Grades continuam ordenando pelo overlay: segurado na borda, ele já cobre a célula da borda.
Regras para uma parte da página
Duas áreas, dois conjuntos de regras. Os adesivos em Layout se encaixam na grade dela e não podem sair; os adesivos em Sketch vão para qualquer lugar, para Layout também. Um arraste mantém as regras da área em que começou, e a área cujas regras estão valendo se acende.
Carregando a demo…
<script setup lang="ts">
import { ref } from 'vue';
import { DnDProvider, type Coordinates, type Key } from '@vue-dnd-kit/core';
import DragArea from './DragArea.vue';
const stickers = ref([
{ id: 'logo', text: 'Logo', color: '#42b883', area: 'sketch', x: 20, y: 24 },
{ id: 'photo', text: 'Photo', color: '#3b82f6', area: 'sketch', x: 110, y: 96 },
{ id: 'quote', text: 'Quote', color: '#ec4899', area: 'sketch', x: 36, y: 150 },
{ id: 'title', text: 'Title', color: '#f59e0b', area: 'layout', x: 24, y: 24 },
{ id: 'button', text: 'Button', color: '#a855f7', area: 'layout', x: 120, y: 144 },
]);
const place = (area: string, id: Key, at: Coordinates) => {
const sticker = stickers.value.find((item) => item.id === id);
if (!sticker) return;
sticker.area = area;
sticker.x = Math.round(at.x);
sticker.y = Math.round(at.y);
};
</script>
<template>
<DnDProvider>
<div class="studio">
<DragArea
id="sketch"
title="Sketch"
rules="no rules"
:stickers="stickers.filter((sticker) => sticker.area === 'sketch')"
@place="place"
/>
<DragArea
id="layout"
title="Layout"
rules="snapToGrid(24) · restrictToArea"
strict
:stickers="stickers.filter((sticker) => sticker.area === 'layout')"
@place="place"
/>
</div>
</DnDProvider>
</template>
<style scoped>
.studio {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
gap: 12px;
width: 100%;
max-width: 560px;
margin: 0 auto;
}
</style><script setup lang="ts">
const area = useTemplateRef<HTMLElement>('area');
const { isDragging } = makeDragArea(area, {
modifiers: () => (props.strict ? [snapToGrid(24)] : []),
restrictToArea: () => props.strict,
});
</script>
<template>
<div ref="area" class="area" :class="{ busy: isDragging }">
<Sticker v-for="sticker in stickers" :key="sticker.id" :sticker="sticker" />
</div>
</template>
makeDragArea define regras para os arrastes que começam dentro do seu elemento — draggables
simples e itens ordenáveis igualmente, que então não precisam de regras próprias. Mantenha um
DnDProvider para o app inteiro, e dê a um quadro, a uma tela de desenho ou a um feed uma área
própria.
| Opção | O que faz |
|---|---|
axis | Trava os arrastes em um eixo. O axis do próprio draggable, ou o da lista dele, vence. |
modifiers | Rodam depois dos do provider e antes dos do próprio draggable. |
restrictToArea | true: os arrastes ficam dentro do elemento da área. |
autoScroll | Rolagem automática do contêiner de rolagem da área (o elemento, ou o ancestral rolável mais próximo dele). false a desliga ali. |
activation | Ativação por tipo de ponteiro, como um toque mais longo em um feed que rola. A do próprio draggable vence; a do provider vem por último. |
disabled | Nenhum arraste começa na área, nem nas áreas dentro dela. Os draggables dela informam aria-disabled. |
Toda opção aceita um valor, um ref ou um getter, lido quando uma pressão ou um arraste começa. A
área retorna isDragging (um arraste que começou aqui está em andamento, como mostra a moldura
acesa acima) e isAutoScrolling (a rolagem automática está movendo o contêiner dela). Referência
completa: makeDragArea.
Como as áreas se combinam
- Um arraste segue a área em que começou, mesmo depois de sair dela. Passar por cima de uma
área não muda nada, exceto o
autoScrolldela: esse vale para todo arraste que rola o contêiner dela. - Áreas aninhadas não se mesclam: a mais próxima vence.
disabledé a exceção, e é herdado: uma área desligada desliga também as áreas dentro dela. - A detecção de colisão, as teclas do teclado e os anúncios continuam valendo para o provider inteiro.
DnDProvider em volta do quadro? Os providers são independentes: um
arraste nunca passa de um para outro, então os itens do quadro nunca poderiam sair dele. Aninhe um
provider só quando uma parte da página precisa de configurações próprias de provider e os itens
dela ficam dentro dela. Veja O provider.Deixar onde foi solto
A linha do tempo, a chamada, o fluxograma, o slide e as áreas acima mantêm cada item onde ele foi
solto. Todo evento de arraste traz position: o canto superior esquerdo da caixa arrastada dentro
do alvo, a partir da borda do padding dele e com a rolagem dele — o left e o top que colocam
um item com posicionamento absoluto bem ali. Ela vem depois dos modificadores, então um nó
encaixado pousa na grade.
makeDroppable(canvas, { id: 'canvas' });
useDnDMonitor({
onDragEnd({ active, over, position }) {
const node = nodes.value.find((item) => item.id === active.id);
if (!node || over?.id !== 'canvas' || !position) return;
node.x = Math.round(position.x);
node.y = Math.round(position.y);
},
});
Mais sobre arraste livre e alvos de drop em Arrastar e soltar.