Как создать Dragable с ограничениями и перерасходом? - PullRequest
2 голосов
/ 30 марта 2019

Я пытаюсь создать перетаскиваемый объект внутри определенных ограничений (например, внутри контейнера размером 300 * 200 пикселей).Однако, в дополнение к этому, я хотел бы создать некоторый эффект резиновой ленты для перетаскивания перетаскиваемого объекта.До сих пор я не нашел способа подойти к этому в (чистом) JavaScript.

Желаемый результат: http://share.framerjs.com/cz3lly4vgoh9/

Мой текущий код: CodeSandbox URL

import React, { Component, Fragment } from "react";

const outerWidth = 400;
const outerHeight = 300;

export default class ImagePreview extends Component {
  draggableContainerRef = null;
  state = {
    dragging: false,
    posX: 0,
    posY: 0
  };

  componentWillMount = () => {
    document.addEventListener("mousedown", this.onMouseDown);
    document.addEventListener("mouseup", this.onMouseUp);
  };

  componentWillUnmount = () => {
    document.removeEventListener("mousedown", this.onMouseDown);
    document.removeEventListener("mouseup", this.onMouseUp);
  };

  calculateCenter = r => {
    const { clientWidth, clientHeight } = r;
    console.log({ clientHeight, clientWidth });

    //const centerX = innerWidth / 2 - clientWidth / 2;
    //const centerY = innerHeight / 2 - clientHeight / 2;
    const centerX = outerWidth / 2 - clientWidth / 2;
    const centerY = outerHeight / 2 - clientHeight / 2;

    console.log({ centerX, centerY });

    this.setState({
      posX: centerX,
      posY: centerY
    });
  };

  lastMousePos = {
    x: 0,
    y: 0
  };

  /**
   * Looks whether the mouse in a mouseEvent is on the desired target (.draggable)
   * @returns {boolean} True / False
   * @memberof ImagePreview
   */
  checkTarget = e => {
    if (
      e.target &&
      (e.target.classList.contains("draggable") || e.target.tagName === "SPAN")
    )
      return true;
    return false;
  };

  totalTranlationY = null;

  checkBounds = (pos, dimension, windowDimension) => {
    const posBoundBefore = 0;
    const posBoundAfter = windowDimension - dimension;

    if (pos < posBoundBefore) {
      return posBoundBefore;
    } else if (pos > posBoundAfter) {
      return posBoundAfter;
    }

    return pos;
  };

  /**
   * Mousedown event listener for .draggable
   * Initiates dragging process if zoomed in.
   * @memberof ImagePreview
   */
  onMouseDown = e => {
    if (this.checkTarget(e)) {
      this.lastMousePos = {
        x: e.clientX,
        y: e.clientY
      };

      document.addEventListener("mousemove", this.onMouseMove);
    }
  };

  /**
   * Mousemove event listener for .draggable
   * Moves
   * @memberof ImagePreview
   */
  onMouseMove = e => {
    const { clientX, clientY } = e;
    const { x: initialX, y: initialY } = this.lastMousePos;
    const { posX: lastStateX, posY: lastStateY } = this.state;

    let posX = this.checkBounds(
      lastStateX + (clientX - initialX),
      this.draggableContainerRef.clientWidth,
      outerWidth
    );
    let posY = this.checkBounds(
      lastStateY + (clientY - initialY),
      this.draggableContainerRef.clientHeight,
      outerHeight
    );

    this.lastMousePos = {
      x: clientX,
      y: clientY
    };

    this.setState({
      pose: "zoomedInDampened",
      dragging: true,
      posX,
      posY
    });
  };

  /**
   * Mouseup event listener for .draggable
   * Checks whether image has been dragged around. Otherwise it toggles the pose for zooming in/out and defaults position.
   * @memberof ImagePreview
   */
  onMouseUp = e => {
    document.removeEventListener("mousemove", this.onMouseMove);

    this.setState({ dragging: false });
  };

  ref = r => {
    this.draggableContainerRef = r;
    if (r !== null) this.calculateCenter(r);
  };

  render() {
    const { posX, posY } = this.state;
    return (
      <Fragment>
        <div
          className="draggableContainer"
          style={{
            fontFamily: "sans-serif",
            width: outerWidth,
            height: outerHeight,
            background: "#292929",
            position: "absolute",
            margin: "auto",
            left: 0,
            right: 0,
            top: 0,
            bottom: 0
          }}
        >
          <div
            ref={this.ref}
            className="draggable"
            style={{
              padding: "10px 15px",
              cursor: "grab",
              background: "gray",
              position: "absolute",
              userSelect: "none",
              transform: `translateX(${posX}px) translateY(${posY}px)`
            }}
          >
            <span
              draggable={false}
              style={{ color: "black", fontWeight: "bold" }}
            >
              Drag me!
            </span>
          </div>
        </div>
      </Fragment>
    );
  }
}
...