Trascinatori personalizzati

Un cursore è un oggetto di controllo che coordina il trascinamento degli elementi trascinabili in risposta alle interazioni dell'utente.

Esistono pochissime circostanze in cui è consigliabile implementare un cursore personalizzato, perché non ci sono molti aspetti che è possibile personalizzare per quanto riguarda il coordinamento di un trascinamento. Il plug-in scroll-options implementa un cursore personalizzato perché voleva aggiungere lo scorrimento sul bordo dell'area di lavoro, il che cambia il modo in cui le coordinate dei pixel vengono convertite in coordinate dell'area di lavoro.

Responsabilità

Il trascinatore ha diverse responsabilità durante l'esecuzione dei trascinamenti:

  • Chiamata dei metodi di trascinamento sull'elemento trascinabile.
  • Calcolo della posizione in cui deve spostarsi l'elemento trascinabile nelle coordinate dello spazio di lavoro.
  • Chiamata dei metodi target di trascinamento su eventuali target di trascinamento su cui è stato eseguito il passaggio del mouse.

Implementazione

Per creare un cursore personalizzato, devi implementare l'interfaccia IDragger.

class MyDragger implements IDragger {
  // Takes in the draggable being dragged and the workspace the drag
  // is occurring in.
  constructor(draggable, workspace);
}

Puoi anche creare una sottoclasse di Blockly.dragging.Dragger integrata, che gestisce già le responsabilità di base.

Inizia i trascinamenti

Il metodo onDragStart inizializza un trascinamento. Deve memorizzare tutti i dati necessari per eseguire il trascinamento. Dovrebbe anche chiamare startDrag sull'elemento draggable in fase di trascinamento.

onDragStart(e) {
  this.startLoc = this.draggable.getRelativeToSurfaceXY();

  this.draggable.startDrag(e);
}

Trascina

Il metodo onDrag esegue un trascinamento. Dovrebbe calcolare la nuova posizione dell'area di lavoro per l'elemento spostabile in base a totalDelta, che viene fornito in coordinate in pixel.

Dovrebbe anche aggiornare tutti i target di trascinamento su cui viene eseguito il passaggio del mouse.

  • wouldDelete deve essere sempre chiamato prima di chiamare altri hook sul target di trascinamento.
  • onDragExit deve sempre essere chiamato sul vecchio target di trascinamento prima di chiamare onDragEnter sul nuovo target di trascinamento.
  • onDragOver deve essere chiamato dopo onDragEnter la prima volta che il target di trascinamento viene visualizzato e a ogni chiamata aggiuntiva a onDrag in cui il target di trascinamento è ancora visualizzato.
onDrag(e, totalDelta) {
  // Update the draggable location.
  const delta = this.pixelsToWorkspaceUnits(totalDelta);
  const newLoc = Coordinate.sum(this.startLoc, delta);
  this.draggable.drag(newLoc, e);

  // Call wouldDeleteDraggable.
  if (isDeletable(this.draggable)) {
    this.draggable.setDeleteStyle(
      // Checks that the drag target is an `IDeleteArea` and calls `wouldDelete`
      // on it.
      this.wouldDeleteDraggable(e, this.draggable),
    );
  }

  const newDragTarget = this.workspace.getDragTarget(e);
  if (this.dragTarget !== newDragTarget) {
    // Call `onDragExit` then `onDragEnter`.
    this.dragTarget?.onDragExit(this.draggable);
    newDragTarget?.onDragEnter(this.draggable);
  }
  // Always call `onDragOver`
  newDragTarget?.onDragOver(this.draggable);
  this.dragTarget = newDragTarget;
}

Interrompere i trascinamenti

Il metodo onEndDrag termina un trascinamento. Deve comunicare all'elemento trascinabile che il trascinamento è terminato e a qualsiasi target di trascinamento visualizzato che l'elemento è stato rilasciato. Deve anche eliminare l'elemento spostabile se il target di trascinamento è un'area di eliminazione.

  • onDrop deve sempre essere chiamato prima di altri metodi.
  • revertDrag deve essere chiamato se il target di trascinamento impedisce i trascinamenti.
  • endDrag deve essere chiamato dopo l'annullamento del trascinamento, ma prima dell'eliminazione.
  • dispose deve essere chiamato se il target di trascinamento è un'area di eliminazione.
onDragEnd(e) {
  // Call `onDrop` first.
  const dragTarget = this.workspace.getDragTarget(e);
  if (dragTarget) {
    this.dragTarget?.onDrop(this.draggable);
  }

  // Then revert the drag (if applicable).
  if (this.shouldReturnToStart(e, this.draggable)) {
    this.draggable.revertDrag();
  }

  // Then end the drag.
  this.draggable.endDrag(e);

  // Then delete the draggable (if applicable).
  if (
    isDeletable(this.draggable) &&
    this.wouldDeleteDraggable(e, this.draggable)
  ) {
    this.draggable.dispose();
  }
}

Registrazione

La classe del cursore deve essere registrata in modo che possa essere creata quando vengono rilevati i trascinamenti.

// Note that the type is BLOCK_DRAGGER even though draggers drag more than
// blocks. The name is for backwards compatibility.
Blockly.registry.register(registry.Type.BLOCK_DRAGGER, 'MY_DRAGGER', MyDragger);

Utilizzo

Dopo aver implementato il cursore personalizzato, puoi utilizzarlo passandolo alle opzioni di configurazione.

const myWorkspace = Blockly.inject('blocklyDiv', {
  plugins: {
    // Note that we pass this to blockDragger even though draggers drag more
    // than blocks. The name is for backwards compatibility.
    blockDragger: MyDragger,
  },
});