Перейти к содержимому

JavaScript Drag-and-Drop: Упрощённый подход с интерактивным примером

В этом руководстве мы рассмотрим функциональность перетаскивания (drag-and-drop) в JavaScript — мощную возможность, которая повышает интерактивность веб-страниц. Мы начнём с обсуждения основных концепций и алгоритмов, обеспечивающих перетаскивание, а затем перейдём к практическому примеру, где вы сможете увидеть эти идеи в действии. Этот пример включает иконку лампочки, которую можно перетащить в тёмную область, чтобы она загорелась.

Что такое перетаскивание?

Перетаскивание — это взаимодействие с пользовательским интерфейсом, которое позволяет пользователю захватить объект и переместить его в другое место на экране. Такое взаимодействие широко используется при управлении файлами на компьютере, расстановке предметов в играх или в онлайн-редакторах.

Основные концепции перетаскивания в JavaScript

Алгоритм перетаскивания

  1. Начало перетаскивания:
    • Процесс начинается, когда пользователь нажимает на элемент и удерживает кнопку мыши.
  2. Перемещение элемента:
    • По мере движения мыши элемент следует за курсором по экрану.
  3. Отпускание элемента:
    • Элемент отпускается, когда пользователь отпускает кнопку мыши, помещая его в новую позицию.

Понимание зон сброса (Droppables)

Зоны сброса (droppables) — это области, предназначенные для приёма перетаскиваемых элементов. Эти области обнаруживают, когда перетаскиваемый объект находится над ними, и могут запускать определённые действия в ответ.

INFO

Убедитесь, что ваш функционал перетаскивания удобен для сенсорных экранов. Пользователи мобильных устройств должны иметь возможность перетаскивать элементы жестами. Рассмотрите возможность реализации сенсорных событий (touchstart, touchmove, touchend) или использования лёгкой библиотеки для кросс-устройственной совместимости.

Интерактивный пример: Светлая и тёмная область

Давайте применим теорию на практике с помощью простого, но интерактивного примера. В качестве перетаскиваемого объекта мы будем использовать иконку лампочки. Когда эта иконка перемещается над тёмной областью, она загорается, имитируя включение света.

Настройка HTML и CSS

Сначала мы определяем базовую структуру и стиль. Мы добавляем тёмный блок и иконку лампочки.

Структура HTML


html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Interactive Lighting with Drag and Drop</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css" />
<style>
  #darkArea {
    width: 300px;
    height: 300px;
    background-color: #333;
    position: relative;
    margin-top: 20px;
  }
  #lightIcon {
    font-size: 48px;
    color: #ccc;
    cursor: pointer;
    position: absolute;
  }
</style>
</head>
<body>
<div id="main">
  <div id="darkArea"></div>
  <i id="lightIcon" class="fas fa-lightbulb"></i>
</div>

<script>
// JavaScript will be added here.
</script>
</body>
</html>

Реализация JavaScript

Теперь добавим функциональность, чтобы сделать лампочку перетаскиваемой и реагирующей на тёмную область.

Объяснение кода JavaScript


javascript
<script>
  // Get references to the light bulb icon and the dark area on the webpage
  var lightIcon = document.getElementById("lightIcon");
  var darkArea = document.getElementById("darkArea");

  // Variables to track whether the dragging is active and to store position data
  var active = false;
  var initialX, initialY, currentX, currentY, xOffset = 0, yOffset = 0;

  // Listen for the mouse down event on the light bulb icon
  lightIcon.addEventListener("mousedown", function(e) {
    // Record the starting position of the mouse and adjust by any existing offset
    initialX = e.clientX - xOffset;
    initialY = e.clientY - yOffset;
    // Set the active flag to true, indicating that dragging has started
    active = true;
  });

  // Listen for mouse movement across the entire document
  document.addEventListener("mousemove", function(e) {
    // If not dragging, don't do anything
    if (!active) return;
    // Calculate the new position of the mouse
    currentX = e.clientX - initialX;
    currentY = e.clientY - initialY;
    // Update the offset with the new position
    xOffset = currentX;
    yOffset = currentY;
    // Move the light bulb icon to the new position
    lightIcon.style.transform = "translate3d(" + currentX + "px, " + currentY + "px, 0)";
  });

  // Listen for the mouse up event across the entire document
  document.addEventListener("mouseup", function() {
    // Save the final position of the light bulb
    initialX = currentX;
    initialY = currentY;
    // Set the active flag to false, indicating dragging has ended
    active = false;
    // Check if the light bulb is inside the dark area
    if (isInside(darkArea, lightIcon)) {
      // Change the background color of the dark area to yellow
      darkArea.style.backgroundColor = "yellow";
      // Change the color of the light bulb to yellow
      lightIcon.style.color = "yellow";
    } else {
      // Revert the dark area's color to dark
      darkArea.style.backgroundColor = "#333";
      // Revert the light bulb's color to gray
      lightIcon.style.color = "#ccc";
    }
  });

  // Function to check if the light bulb is inside the dark area
  function isInside(container, element) {
    // Get the position of the container and the element
    var containerRect = container.getBoundingClientRect();
    var elementRect = element.getBoundingClientRect();
    // Return true if the element is within the container's boundaries
    return (
      elementRect.left >= containerRect.left &&
      elementRect.right <= containerRect.right &&
      elementRect.top >= containerRect.top &&
      elementRect.bottom <= containerRect.bottom
    );
  }
</script>

Этот скрипт делает иконку лампочки на экране перемещаемой с помощью мыши. Вы можете нажать на лампочку, перетащить её и увидеть, как она взаимодействует с тёмной областью на экране. Вот что делает каждая часть скрипта простыми словами:

  1. Подготовка: Сначала скрипт находит иконку лампочки и тёмную область на вашей веб-странице. Он также настраивает правила для отслеживания движения мыши.

  2. Начало перетаскивания:

    • Когда вы нажимаете кнопку мыши на лампочке, скрипт запоминает начальную точку. Это помогает ему понять, насколько далеко вы переместили лампочку.
  3. Перемещение лампочки:

    • Пока вы удерживаете кнопку мыши и двигаете курсором, скрипт заставляет лампочку следовать за мышью. Он постоянно обновляет позицию лампочки, чтобы она совпадала с положением курсора.
  4. Отпускание лампочки:

    • Когда вы отпускаете кнопку мыши, скрипт проверяет, находится ли лампочка над тёмной областью. Если да, тёмная область загорается (становится жёлтой), а цвет лампочки также меняется, показывая, что она включена. Если лампочка не над тёмной областью, всё возвращается в исходное состояние.
  5. Проверка позиции:

    • Существует специальный набор правил (функция isInside), который помогает скрипту определить, находится ли лампочка над тёмной областью. Он сравнивает позицию лампочки с границами тёмной области.

INFO

Используйте translate3d в CSS-трансформациях для перетаскиваемых элементов. Он задействует ускорение GPU, что обеспечивает более плавное движение и снижает нагрузку на процессор, что критически важно для приложений с высокой производительностью.

Полный пример

Теперь самое время увидеть всё в действии:


html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Interactive Lighting with Drag and Drop</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css" />
    <style>
      #darkArea {
        width: 300px;
        height: 300px;
        background-color: #333;
        position: relative;
        margin-top: 20px;
      }
      #lightIcon {
        font-size: 48px;
        color: #ccc;
        cursor: pointer;
        position: absolute;
      }
    </style>
  </head>
  <body>
    <div id="main">
     <p>Move the light into the dark area to light it up!</p>
      <div id="darkArea"></div>
      <i id="lightIcon" class="fas fa-lightbulb"></i>
    </div>

    <script>
      // Get references to the light bulb icon and the dark area on the webpage
      var lightIcon = document.getElementById("lightIcon");
      var darkArea = document.getElementById("darkArea");

      // Variables to track whether the dragging is active and to store position data
      var active = false;
      var initialX,
        initialY,
        currentX,
        currentY,
        xOffset = 0,
        yOffset = 0;

      // Listen for the mouse down event on the light bulb icon
      lightIcon.addEventListener("mousedown", function (e) {
        // Record the starting position of the mouse and adjust by any existing offset
        initialX = e.clientX - xOffset;
        initialY = e.clientY - yOffset;
        // Set the active flag to true, indicating that dragging has started
        active = true;
      });

      // Listen for mouse movement across the entire document
      document.addEventListener("mousemove", function (e) {
        // If not dragging, don't do anything
        if (!active) return;
        // Calculate the new position of the mouse
        currentX = e.clientX - initialX;
        currentY = e.clientY - initialY;
        // Update the offset with the new position
        xOffset = currentX;
        yOffset = currentY;
        // Move the light bulb icon to the new position
        lightIcon.style.transform = "translate3d(" + currentX + "px, " + currentY + "px, 0)";
      });

      // Listen for the mouse up event across the entire document
      document.addEventListener("mouseup", function () {
        // Save the final position of the light bulb
        initialX = currentX;
        initialY = currentY;
        // Set the active flag to false, indicating dragging has ended
        active = false;
        // Check if the light bulb is inside the dark area
        if (isInside(darkArea, lightIcon)) {
          // Change the background color of the dark area to yellow
          darkArea.style.backgroundColor = "yellow";
          lightIcon.style.color = "yellow";
        } else {
          // Revert the dark area's color to dark
          darkArea.style.backgroundColor = "#333";
          // Revert the light bulb's color to gray
          lightIcon.style.color = "#ccc";
        }
      });

      // Function to check if the light bulb is inside the dark area
      function isInside(container, element) {
        // Get the position of the container and the element
        var containerRect = container.getBoundingClientRect();
        var elementRect = element.getBoundingClientRect();
        // Return true if the element is within the container's boundaries
        return elementRect.left >= containerRect.left && elementRect.right <= containerRect.right && elementRect.top >= containerRect.top && elementRect.bottom <= containerRect.bottom;
      }
    </script>
  </body>
</html>

Основные используемые события мыши:

  1. mousedown: Это событие срабатывает, когда пользователь нажимает кнопку мыши над иконкой лампочки. Оно отмечает начало перетаскивания и записывает начальную позицию курсора.
  2. mousemove: Это событие срабатывает при движении мыши. Если перетаскивание активно (т.е. кнопка мыши всё ещё нажата), оно вычисляет новую позицию иконки на основе движения курсора и обновляет положение лампочки на экране.
  3. mouseup: Это событие происходит, когда пользователь отпускает кнопку мыши, отмечая конец перетаскивания. Оно проверяет, находится ли лампочка в пределах тёмной области, чтобы решить, менять ли её фоновый цвет.

Как мы узнали в статье События мыши, эти события являются фундаментальными для создания интерактивного функционала перетаскивания, позволяя динамически перемещать элементы на веб-странице и взаимодействовать с ними с помощью мыши.

Заключение

Действия перетаскивания могут сделать ваши сайты более удобными и привлекательными. Освоив простые идеи и попробовав их на реальных примерах, вы сможете эффективно использовать эти возможности. Это сделает ваши сайты более увлекательными и интерактивными.

Практика

Какие утверждения о функциональности перетаскивания в JavaScript являются верными?

Считаете ли это полезным?

Предпросмотр dual-run — сравните с маршрутами Symfony на продакшене.