5
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

JavaScriptでHTML要素をドラッグして動かす

Last updated at Posted at 2020-01-01

※赤い部分をドラッグして動かせます。

See the Pen MovableElement by Yoshiharu (@yoshiharu2580) on CodePen.

パソコン、スマホの両方とも対応しています。

interface ElementPosition {
  top: number;
  left: number;
}

interface PointerPosition {
  x: number | null;
  y: number | null;
}

class MovableElement {
  // Elementオブジェクト
  private element?: HTMLElement;
  // 要素の初期位置
  private elementInitialPosition?: ElementPosition;
  // 要素の動き始めの位置
  private elementStartPosition?: ElementPosition;
  // ポインタの動き始めの位置
  private pointerStartPosition: PointerPosition = {
    x: null,
    y: null
  };

  constructor(id: string) {
    this.element = document.getElementById(id);
    this.elementInitialPosition = this.getCurrentPosition();

    this.element.addEventListener("mousedown", (event: MouseEvent) => {
      this.elementStartPosition = this.getCurrentPosition();

      this.pointerStartPosition.x = event.clientX;
      this.pointerStartPosition.y = event.clientY;
    });
    this.element.addEventListener("touchstart", (event: TouchEvent) => {
      this.elementStartPosition = this.getCurrentPosition();

      this.pointerStartPosition.x = event.touches[0].clientX;
      this.pointerStartPosition.y = event.touches[0].clientY;
    });

    window.addEventListener("mousemove", (event: MouseEvent) => {
      if (
        this.pointerStartPosition.x == null ||
        this.pointerStartPosition.y == null
      )
        return;

      const pointerMovedDistanceX = event.clientX - this.pointerStartPosition.x;
      const pointerMovedDistanceY = event.clientY - this.pointerStartPosition.y;
      this.moveDistance(pointerMovedDistanceX, pointerMovedDistanceY);
    });
    window.addEventListener(
      "touchmove",
      (event: TouchEvent) => {
        if (
          this.pointerStartPosition.x == null ||
          this.pointerStartPosition.y == null
        )
          return;

        // touchmove中はスクロールをキャンセルする
        event.preventDefault();

        const pointerMovedDistanceX =
          event.touches[0].clientX - this.pointerStartPosition.x;
        const pointerMovedDistanceY =
          event.touches[0].clientY - this.pointerStartPosition.y;
        this.moveDistance(pointerMovedDistanceX, pointerMovedDistanceY);
      },
      // touchmove中はスクロールをキャンセルする
      { passive: false }
    );

    window.addEventListener("mouseup", () => {
      this.resetPointerStartPosition();
    });
    window.addEventListener("touchend", () => {
      this.resetPointerStartPosition();
    });
  }

  // 要素の位置を取得する
  getCurrentPosition(): ElementPosition {
    const { top, left } = this.element.getBoundingClientRect();
    return { top, left };
  }

  // 要素を現在位置からの距離だけ移動させる
  moveDistance(distanceX: number, distanceY: number) {
    // 補正値
    const correctionX =
      this.elementStartPosition.left - this.elementInitialPosition.left;
    const correctionY =
      this.elementStartPosition.top - this.elementInitialPosition.top;

    // 目標値
    const targetX = correctionX + distanceX;
    const targetY = correctionY + distanceY;

    this.element.style.transform = `translate(${targetX}px, ${targetY}px)`;
  }

  // 要素の動き始めの位置をリセット
  resetPointerStartPosition() {
    if (
      this.pointerStartPosition.x == null ||
      this.pointerStartPosition.y == null
    )
      return;

    this.pointerStartPosition.x = null;
    this.pointerStartPosition.y = null;
  }
}

new MovableElement('element');

ここから色々応用できそう。

5
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?