Перемещаемые элементы, которые при перетаскивании раздвигают другие элементы

За исключением парочки почти мистических вещей, наподобие тех, что позволяют вручную изменять размер текстового поля, перемещаемые элементы в вебе - это территория JavaScript.

Например, нажать на элемент, удерживать кнопку мыши, перетащить элемент, удерживая его кнопкой мыши, а затем поместить его в нужном месте, отпустив кнопку мыши. Или их сенсорные аналоги. К счастью, это уже хорошо освоенная территория.

Но теперь пришло время экспериментов, наподобие JQuery UI предлагаемого Draggable (и других подобных методов), чтобы делать это еще проще.

В последнее время я заметил, что пытаясь совершить определенное действие (смотрите заголовок этой статьи), я не могу сделать это с помощью JQuery UI. Но я смог сделать это вот как.

Я пытался воссоздать эффект, как в боковой панели Keynote, где можно перетаскивать элементы, чтобы менять местами слайды.

Вот как выглядит этот эффект:

Вы можете получить что-то очень близкое к этому с помощью некоторых элементов базовой конфигурации JQuery UI. Мы используем здесь метод Сортировка.

Содержание

Он предназначен специально для сортировки списков, так как нам нужно. То есть он использует что-то похожее и на Перетаскивание, и на Отпускание одновременно.

JQuery

$(".all-slides").sortable({
  
  axis: "y",
  revert: true,
  scroll: false,
  placeholder: "sortable-placeholder",
  cursor: "move"

});

И базовый HTML наподобие этого:

HTML

<div class='all-slides'>

  <div class='slide'>Slide</div>
  <div class='slide'>Slide</div>
  <div class='slide'>Slide</div>

  <!-- etc -->

«Заполнитель» в конфигурации является элементом (это достигается с помощью введения имени класса), который вставляется между слайдами. Слайд, который мы перемещаем «бросается», если вы отпускаете кнопку мыши.

Вы можете задать его стиль по своему усмотрению с помощью CSS. Я сделал его похожим на прямоугольник с голубой полосой слева, как в Keynote:

HTML

Проблема заключается в том, чтобы «растолкать с дороги» другие слайды в том месте, где мы хотим поместить элемент. И на этом месте появился наш слайд.

Сперва я пытался решить эту проблему с помощью анимации @keyframes, увеличивая высоту заполнителя от 0 до высоты слайда, а затем вывести в этом месте элемент с помощью режима заполнения.

Это работает для установки заполнителя на новом месте, но JQuery UI просто вырывает заполнитель из DOM, когда он убирается со старого места, что не позволяет осуществлять перемещение плавно.

Таким образом, необходима дополнительная обработка. К счастью, ЭйДжей Кэнди нашел удобный способ сделать как раз то, что мне нужно.

Следите за моей мыслью, с этого момента все будет немного сложнее:

  • Запускаем цикл через все слайды;
  • Создаем дубликат каждого слайда;
  • Располагаем дубликат прямо поверх оригинала;
  • Скрываем оригинал;
  • Сохраняем ссылку к слайду, дубликат которого был размещен.

После этого запускаем метод сортировки для всех оригинальных слайдов.

И затем, когда вы начинаете перетаскивать слайд:

  • Скрываем все оригинальные слайды;
  • Выводим дубликаты слайдов;
  • При перетаскивании нужного слайда, скрытые оригинальные элементы переставляются невидимо;
  • После того как перестановка осуществилась, дубликаты слайдов выводятся на позициях соответствующих им оригинальных слайдов.

Когда перетаскивание завершилось, нужно:

  • Убедиться, что все дубликаты расставлены в правильном порядке;
  • Поменять видимость элементов и снова вывести оригинальные слайды, но уже в новом порядке.

Потребовалось достаточно много времени, чтобы продумать, как это все сделать, и получить именно тот код, который нужен.

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

JQuery

$(".slide").each(function(i) {
  var item = $(this);
  var item_clone = item.clone();
  item.data("clone", item_clone);
  var position = item.position();
  item_clone
  .css({
    left: position.left,
    top: position.top,
    visibility: "hidden"
  })
    .attr("data-pos", i+1);
  
  $("#cloned-slides").append(item_clone);
});

$(".all-slides").sortable({
  
  axis: "y",
  revert: true,
  scroll: false,
  placeholder: "sortable-placeholder",
  cursor: "move",

  start: function(e, ui) {
    ui.helper.addClass("exclude-me");
    $(".all-slides .slide:not(.exclude-me)")
      .css("visibility", "hidden");
    ui.helper.data("clone").hide();
    $(".cloned-slides .slide").css("visibility", "visible");
  },

  stop: function(e, ui) {
    $(".all-slides .slide.exclude-me").each(function() {
      var item = $(this);
      var clone = item.data("clone");
      var position = item.position();

      clone.css("left", position.left);
      clone.css("top", position.top);
      clone.show();

      item.removeClass("exclude-me");
    });
    
    $(".all-slides .slide").each(function() {
      var item = $(this);
      var clone = item.data("clone");
      
      clone.attr("data-pos", item.index());
    });

    $(".all-slides .slide").css("visibility", "visible");
    $(".cloned-slides .slide").css("visibility", "hidden");
  },

  change: function(e, ui) {
    $(".all-slides .slide:not(.exclude-me)").each(function() {
      var item = $(this);
      var clone = item.data("clone");
      clone.stop(true, false);
      var position = item.position();
      clone.animate({
        left: position.left,
        top: position.top
      }, 200);
    });
  }
  
});

Вот демо версия.

Бонусная функция: слайдам назначен класс, с помощью которого, когда элементы перемещаются, они мигают, точно как в Keynote.

Дэвид ДеСандро разрабатывал несколько проектов, связанных, с панелями с перемещаемыми элементами. О некоторых из них вы возможно слышали, например Masonry или Isotope.

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

Дэвид усовершенствовал мою демо-версию так, что она может работать и для двойной панели. А также смог оптимизировать код, который теперь стал меньше.

Просмотреть результат на Codepen.

РедакцияПеревод статьи «Draggable Elements That Push Others Out Of Way»