Documentation/Guides
Axes, limites et grilles
Verrouillez les glissements sur un axe, gardez-les dans une boîte, alignez-les sur une grille ou sur vos propres repères — pour un item, une liste ou toute une partie de la page.
Cette page a été traduite par IA et peut contenir des inexactitudes. Lire l'original en anglais
Un glissement peut être tenu sur un axe, gardé dans une boîte, aligné sur une grille, ou infléchi par une règle à vous. Ces règles sont des modificateurs : ils ajustent le décalage du glissement à chaque frame. Posez-les sur un élément déplaçable, sur une liste ou sur une partie de la page, et l'overlay, les cibles de dépôt et le dépôt les suivent tous.
Verrouiller un axe
Les clips d'une timeline glissent le long de leur piste. Désactivez le verrouillage, et ils peuvent aussi changer de piste.
Chargement de la démo…
<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' ne déplace qu'horizontalement, 'y' que verticalement. makeDraggable,
makeSortableList, makeSortableTree, makeSortableItem et makeDragArea l'acceptent tous :
makeSortableList(listRef, { items: tracks, axis: 'y' });
- Le verrouillage s'exécute après tous les modificateurs : il tient donc quoi qu'ils fassent. Un alignement ou une limite ne peut pas faire sortir l'item de sa ligne.
- La ligne est dans le contenu, pas à l'écran : elle se déplace avec la liste (ou le parent) où le glissement a commencé. Faites défiler la page en plein glissement et l'item reste sur sa ligne.
- L'
axispropre à un item l'emporte sur celui de sa liste ou de son arbre, et celui d'un élément déplaçable (ou de sa liste) l'emporte sur celui de sa zone de glissement. - Il est lu quand un glissement commence : un getter peut donc le basculer, comme ci-dessus.
- Un arbre verrouillé sur
'y's'imbrique toujours : la profondeur vient alors du survol du milieu d'une ligne.
axis aux modificateurs restrictToVerticalAxis et restrictToHorizontalAxis. Ils font
la même chose, mais en tant que modificateurs ordinaires, ils s'exécutent au milieu de la chaîne,
et un modificateur placé après eux (une grille qui aligne les deux axes) peut de nouveau faire
sortir l'item de l'axe.Modificateurs
Un modificateur ajuste le décalage du glissement à chaque frame. Son résultat déplace l'overlay
et constitue le point où les cibles sont cherchées : une liste se trie donc selon la position
verrouillée, limitée ou alignée, et la position du dépôt est l'endroit où la boîte a fini.
| Modificateur | Ce qu'il fait |
|---|---|
restrictToParent | Garde l'overlay dans le conteneur où le glissement a commencé : la liste triable, ou l'élément parent d'un élément déplaçable simple. |
restrictToElement(target) | Garde l'overlay dans un élément : une ref de template, un getter ou l'élément lui-même. |
restrictToWindowEdges | Garde l'overlay dans la fenêtre. |
restrictToFirstScrollableAncestor | Garde l'overlay dans le premier ancêtre défilant de l'élément glissé. |
snapToGrid(size) | Aligne l'overlay sur une grille (voir plus bas). |
snapCenterToCursor | Centre l'overlay sous le pointeur au lieu de garder le décalage de saisie. |
restrictToVerticalAxis | Ne se déplace que verticalement. Préférez axis: 'y'. |
restrictToHorizontalAxis | Ne se déplace qu'horizontalement. Préférez axis: 'x'. |
modifiers est une option du provider, des zones de glissement, des listes et des arbres, ainsi
que des éléments déplaçables et des items triables. Toutes, sauf celle du provider, acceptent
aussi une ref ou un getter, lus quand le glissement commence.
Le garder à l'intérieur
La vignette de votre propre caméra dans un appel vidéo reste sur l'écran. Changez de limite et faites-la glisser loin au-dehors :
Chargement de la démo…
<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 []
});
restrictToParentla retient sur l'écran, son élément parent. Pour un item triable, le « parent » est sa liste, où que la liste se trouve dans le DOM.restrictToWindowEdgesla laisse quitter l'écran, mais pas la fenêtre. Déposée hors de l'écran, elle n'est sur aucune cible de dépôt : elle revient donc à sa place.- Sans limite, elle va n'importe où.
Une limite garde l'overlay à l'intérieur, à la taille qu'il a : avec overlayFit: 'slot',
une boîte qui grandit au-dessus d'une liste d'items plus larges est gardée à l'intérieur à sa
nouvelle taille.
Aligner sur une grille
Les nœuds d'un diagramme de flux s'alignent sur la grille du canevas. Changez la taille des cellules et le glissement en douceur :
Chargement de la démo…
<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) aligne les deux axes sur des cellules de 24 px. Un objet permet d'en régler
davantage :
| Option | Signification |
|---|---|
x, y | La taille de la cellule en pixels, par axe. Omettez-en un pour n'aligner que l'autre : { y: 15 } aligne un événement sur les quarts d'heure et le laisse se déplacer librement d'un jour à l'autre. |
origin | 'container' (par défaut) : le coin supérieur gauche de l'overlay se pose sur des lignes de grille tracées depuis le coin supérieur gauche du conteneur — la liste triable, ou le parent d'un élément déplaçable simple. 'delta' : c'est le décalage lui-même qui est arrondi, si bien qu'un item parti hors de la grille y reste et se déplace par cellules entières depuis l'endroit où il était. Choisissez-le pour des items placés n'importe où (un canevas libre) qui doivent garder leur propre alignement. |
smooth | true ou { duration, easing } : l'overlay glisse en douceur d'une cellule à l'autre (80 ms par défaut). Les cibles utilisent tout de suite la position alignée. |
Avec origin: 'container', tracez la grille depuis la border box du conteneur : le canevas
ci-dessus a son cadre en box-shadow, pas en border, si bien que ses lignes commencent tout au
bord.
Votre propre modificateur
Les boîtes d'une diapositive sautent sur les bords et les centres des autres quand elles s'en approchent, et des repères roses montrent les lignes sur lesquelles elles se posent :
Chargement de la démo…
<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];
Un modificateur reçoit transform, le décalage depuis le début du glissement tel que l'ont
laissé les modificateurs précédents, et en renvoie un nouveau. Il reçoit aussi ce dont il peut
avoir besoin pour le calculer :
| Argument | Ce que c'est |
|---|---|
transform | Le décalage jusqu'ici. |
overlayRect | La boîte de l'overlay déplacée de transform, à la taille qu'elle a maintenant. |
activeRect | L'élément glissé au début du glissement. |
containerRect | Le conteneur où le glissement a commencé (la liste triable, ou l'élément parent), là où il est maintenant : il suit le défilement. |
windowRect | La fenêtre. |
origin | Le pointeur au début du glissement. |
active, over | Le glissement et la cible en dessous. |
Les modificateurs s'exécutent à chaque frame : gardez-les rapides et sans effets de bord. Les
repères ci-dessus sont dessinés à partir du delta du glissement dans l'onDragMove d'un
moniteur, qui est le décalage après tous les modificateurs. Les types se trouvent dans
Modificateurs.
L'ordre
Les modificateurs venant de plusieurs endroits s'exécutent l'un après l'autre, chacun à partir du résultat du précédent :
- les
modifiersdu provider ; - les
modifiersde la zone de glissement où le glissement a commencé ; - les
modifierspropres à l'item glissé ou, s'il n'en a pas, ceux de sa liste ou de son arbre ; - le
restrictToAreade la zone de glissement ; - le verrouillage d'
axis.
Dans un même tableau, ils s'exécutent dans l'ordre : placez donc les alignements et les aimants
avant les limites. Une limite en dernier garde la boîte à l'intérieur quoi qu'aient fait les
autres. La diapositive ci-dessus exécute [magnet, restrictToParent] ; dans l'autre sens,
l'aimant pourrait tirer une boîte retenue au bord de la diapositive de quelques pixels au-delà.
Les modifiers d'un item remplacent ceux de sa liste ; ils ne s'y ajoutent pas. Les limites
de la zone viennent après les alignements des étapes 1 à 3 : un item aligné reste donc à
l'intérieur, et le verrouillage d'axe vient tout à la fin.
Les listes limitées gardent leurs extrémités
Un panneau de calques : les lignes ne se déplacent que vers le haut et le bas, et ne quittent jamais le panneau. Faites glisser la ligne du haut tout en bas :
Chargement de la démo…
<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] });
Une limite retient l'overlay au bord de la boîte pendant que le pointeur continue. Dès que le pointeur a quitté l'overlay le long de l'axe de la liste, la liste se trie plutôt selon le pointeur : la première et la dernière place restent ainsi accessibles, quelle que soit la façon dont vous tenez l'item. Les grilles continuent de se trier selon l'overlay : retenu au bord, il couvre déjà la cellule du bord.
Des règles pour une partie de la page
Deux zones, deux jeux de règles. Les stickers de Layout s'alignent sur sa grille et ne peuvent pas en sortir ; les stickers de Sketch vont n'importe où, y compris dans Layout. Un glissement garde les règles de la zone où il a commencé, et la zone dont les règles s'appliquent s'éclaire.
Chargement de la démo…
<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 fixe des règles pour les glissements qui commencent à l'intérieur de son
élément — éléments déplaçables simples comme items triables, qui n'ont alors besoin d'aucune
règle à eux. Gardez un seul DnDProvider pour toute l'application, et donnez à un tableau, un
canevas ou un fil sa propre zone.
| Option | Ce qu'elle fait |
|---|---|
axis | Verrouille les glissements sur un axe. L'axis propre à un élément déplaçable, ou celui de sa liste, l'emporte. |
modifiers | S'exécutent après ceux du provider et avant ceux de l'élément déplaçable. |
restrictToArea | true : les glissements restent dans l'élément de la zone. |
autoScroll | Le défilement automatique du conteneur défilant de la zone (l'élément, ou son ancêtre défilant le plus proche). false le désactive à cet endroit. |
activation | L'activation par type de pointeur, comme un appui tactile plus long dans un fil qui défile. Celle de l'élément déplaçable l'emporte ; celle du provider vient en dernier. |
disabled | Aucun glissement ne démarre dans la zone, ni dans les zones qu'elle contient. Ses éléments déplaçables indiquent aria-disabled. |
Chaque option accepte une valeur, une ref ou un getter, lus quand un appui ou un glissement
commence. La zone renvoie isDragging (un glissement qui a commencé ici est en cours, comme le
montre le cadre éclairé ci-dessus) et isAutoScrolling (le défilement automatique fait défiler
son conteneur). Référence complète : makeDragArea.
Comment les zones se combinent
- Un glissement suit la zone où il a commencé, même après en être sorti. Passer au-dessus
d'une zone ne change rien, sauf son
autoScroll: celui-ci s'applique à chaque glissement qui fait défiler son conteneur. - Les zones imbriquées ne fusionnent pas : la plus proche l'emporte.
disabledest l'exception, et il est hérité : une zone désactivée désactive aussi les zones qu'elle contient. - La détection de collision, les touches du clavier et les annonces restent à l'échelle du provider.
DnDProvider autour du tableau ? Les providers sont indépendants : un
glissement ne passe jamais de l'un à l'autre, et les items du tableau ne pourraient donc jamais en
sortir. N'imbriquez un provider que quand une partie de la page a besoin de ses propres réglages
à l'échelle du provider et que ses items y restent. Voir Le provider.Le laisser là où il est déposé
La timeline, l'appel, l'diagramme de flux, la diapositive et les zones ci-dessus gardent tous
chaque item là où il a été déposé. Chaque événement de glissement transporte position : le coin
supérieur gauche de la boîte glissée à l'intérieur de la cible, depuis le bord de son padding et
défilement compris — le left et le top qui placent un item positionné en absolu exactement à
cet endroit. Elle est calculée après les modificateurs : un nœud aligné atterrit donc sur la
grille.
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);
},
});
Plus de détails sur le glissement libre et les cibles de dépôt dans Glisser-déposer.