hipadev23 3 days ago

How does the dragging itself work? custom js?

1
tyleo 3 days ago

In the Cross-Platform Dynamic Light section I describe `data-*` attributes (https://www.tyleo.com/html-glass.html#cross-platform-dynamic...).

The dragging works with another bit of JavaScript--the only other bit on the page--which uses a `data-click-drag-area` to define an element which will contain draggable children and a `data-click-drag-item` attribute to indicate a child can be dragged.

The the parent must be a 'positioned element' (it must have `position` set to something in CSS) the children must have `position: absolute`.

I did this in TypeScript. I'll share the code below. You have to call `initDataClickDrag` from another script... if you want to include this script directly you can just remove the `export` keyword and call `initDataClickDrag()` at the bottom after it is defined:

  export const initDataClickDrag = () => {
    // Get all of the areas we can drag items in
    const dragAreas = document.querySelectorAll("[data-click-drag-area]");
    for (const dragArea of dragAreas) {
      // Only iterate `HTMLElement`s
      if (!(dragArea instanceof HTMLElement)) continue;

      // Get all of the items we can drag
      const dragItems = dragArea.querySelectorAll("[data-click-drag-item]");
      for (const dragItem of dragItems) {
        // Only iterate `HTMLElement`s
        if (!(dragItem instanceof HTMLElement)) continue;

        let isDragging = false;
        let lastCursorX: number | undefined = undefined;
        let lastCursorY: number | undefined = undefined;

        // Mouse down event to start dragging
        const downCallback = (obj: {
          readonly pageX: number;
          readonly pageY: number;
        }) => {
          isDragging = true;
          lastCursorX = obj.pageX;
          lastCursorY = obj.pageY;
        };

        dragItem.addEventListener("mousedown", (e) => {
          downCallback(e);
        });

        dragItem.addEventListener("touchstart", (e) => {
          const touches = e.touches;
          if (touches.length === 0) return;
          downCallback(touches[0]);
        });

        // Mouse move event to scroll while dragging
        const moveCallback = (obj: {
          readonly pageX: number;
          readonly pageY: number;
        }): boolean => {
          if (!isDragging) return false;

          if (lastCursorX === undefined) return false;
          if (lastCursorY === undefined) return false;

          const x = lastCursorX - obj.pageX;
          const y = lastCursorY - obj.pageY;

          const left = dragItem.offsetLeft - x;
          const top = dragItem.offsetTop - y;

          dragItem.style.left = `${left.toString()}px`;
          dragItem.style.top = `${top.toString()}px`;

          // Get dragArea dimensions
          const dragAreaRect = dragArea.getBoundingClientRect();

          // Get element dimensions
          const elementRect = dragItem.getBoundingClientRect();

          if (dragItem.offsetLeft < 0) dragItem.style.left = "0px";
          if (dragItem.offsetTop < 0) dragItem.style.top = "0px";

          if (left + elementRect.width > dragAreaRect.width) {
            // Right boundary
            const left = dragAreaRect.width - elementRect.width;
            dragItem.style.left = `${left.toString()}px`;
          }

          if (top + elementRect.height > dragAreaRect.height) {
            // Bottom boundary
            const top = dragAreaRect.height - elementRect.height;
            dragItem.style.top = `${top.toString()}px`;
          }

          lastCursorX = obj.pageX;
          lastCursorY = obj.pageY;

          return true;
        };

        document.addEventListener("mousemove", (e) => {
          moveCallback(e);
        });

        document.addEventListener(
          "touchmove",
          (e) => {
            const touches = e.touches;
            if (touches.length === 0) return;
            if (!moveCallback(touches[0])) return;
            e.preventDefault();
          },
          { passive: false },
        );

        // Mouse up event to stop dragging
        document.addEventListener("mouseup", () => {
          isDragging = false;
        });

        document.addEventListener("touchend", () => {
          isDragging = false;
        });
      }
    }
  };