Изучение того, как перемещать элемент HTML с помощью JS, динамически добавляя переходы CSS

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

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

Мы даже можем легко поменять временную функцию на удобство включения, выключения или чего угодно!

Предсказывать каждое ваше движение

Звучит круто, но есть одна проблема ... Как вы учитываете каждый ход?

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

Мы переосмысливаем то, как мы используем CSS. Избавьтесь от предубеждений о том, что CSS - это статическая таблица стилей, определяющая предсказуемые цвета и размеры страницы. Пусть JavaScript использует марионетку, как Weekend at Bernie’s.

Настройка

В прошлый раз я лениво добавил встроенный стиль в little-box, чтобы немного сэкономить. Но на этот раз это не сработает. Начнем с очень простого HTML.

<div id="little-box"></div>

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

#little-box
{
  background-color: black;
  position: absolute;
  width: 10px;
  height: 10px;
  top: 0px;
  left: 0px;
}

Создайте таблицу стилей движения

Теперь приступим к созданию функции move(). Первый шаг - убедиться, что у JavaScript есть таблица стилей, которую он может изменять для своих переходов CSS в элементе, в котором вы его используете.

function move(element) {
   var elStyleSheet = document.getElementById(element.id + "-movement");
   if (!elStyleSheet) {
      elStyleSheet = document.createElement("style");
      elStyleSheet.id = element.id + "-movement";
      document.head.appendChild(elStyleSheet);
   }
}

Чтобы быстро приступить к тестированию, я начал с этого… this.

var littleBox = document.getElementById("little-box");
littleBox.onclick = function(){ move(this); };

Я проверил свою DOM, и мне ввели таблицу стилей, хорошо. Но если бы я забыл добавить идентификатор, была бы создана таблица стилей с идентификатором «-movement». Это было бы довольно бесполезно, потому что вы не смогли бы отличить таблицы стилей одного элемента без идентификатора друг от друга.

Должен иметь удостоверение личности

Чтобы исправить эту неидентифицируемую проблему с таблицей стилей, я обязал функцию иметь идентификатор.

function move(element) {
   var elId = element.id;
   if (!elId) { throw("Cannot move an element without an ID!"); }
   /* ... */
}

Давай двигаться!

Мы заложили основу, но давайте приступим к делу прямо сейчас. Давайте начнем с того, что он переместит элемент вниз и оттуда двинется.

function move(element, distance=20) {
   /* ... */
   // (Step 1) Get the current top value
   var elStyle = window.getComputedStyle(element);
   var topValue = elStyle.getPropertyValue("top").replace("px", "");
   // (Step 2) Determine the destination of top value
   var destination = (Number(topValue) + distance) + "px";
   // (Step 3) Add a new class to the stylesheet
   elStyleSheet.innerHTML += `
      #` + elId + `.moved
      {
         transition: all 1s linear;
         -webkit-transition: all 1s linear;
         -moz-transition: all 1s linear;
         -o-transition: all 1s linear;
         top: ` + destination + `;
         left: ` + elStyle.getPropertyValue("left") + `;
      }
   `;
   // (Step 4) Add the class, to fire the CSS transition
   element.classList.add("moved");
}

Если вы не знакомы с обратными кавычками в JavaScript, они позволяют писать многострочные строки. Это позволило мне легко добавить новый CSS.

Это работает! Он движется вниз. Функция classList.add() даже предотвращает добавление класса несколько раз, поэтому список классов элемента довольно чистый. Однако таблица стилей быстро становится загроможденной. Нет необходимости вести журнал всех движений, так что давайте очистим его по ходу дела.

function move(element, distance=20) {
   /* ... */
   // Out with the old!
   elStyleSheet.innerHTML = elStyleSheet.innerHTML.replace(/\s+#(.|\n)*?\}\s+\n/, "");
   // In with the new!
   elStyleSheet.innerHTML += `
      #` + elId + `.moved
      {
         transition: all 1s linear;
         -webkit-transition: all 1s linear;
         -moz-transition: all 1s linear;
         -o-transition: all 1s linear;
         top: ` + destination + `;
         left: ` + elStyle.getPropertyValue("left") + `;
      }
   `;
   /* ... */
}

Получение comf (y | ortable) с помощью RegEx

Если это регулярное выражение вас до бесконечности смущает, посетите regex101.com для получения подробных объяснений. Вы помещаете регулярное выражение в верхнюю часть и настраиваете флаги, выбираете JavaScript в качестве разновидности и смотрите объяснение справа.

Добавление продолжительности

Чтобы добавить продолжительность, все, что вам нужно сделать, это изменить атрибут стиля перехода в CSS. Это позволит вам писать в s для секунд или ms для миллисекунд.

function move(element, distance=20, duration="0.1s") {
   /* ... */
   elStyleSheet.innerHTML += `
      #` + elId + `.moved
      {
         transition: all ` + duration + ` linear;
         -webkit-transition: all ` + duration + ` linear;
         -moz-transition: all ` + duration + ` linear;
         -o-transition: all ` + duration + ` linear;
         top: ` + destination + `;
         left: ` + elStyle.getPropertyValue("left") + `;
      }
   `;
   /* ... */
}

Вы также можете добавить параметр функции времени, если хотите использовать что-то другое, кроме linear (например, ease-in или ease-out)!

Добавление других направлений

Теперь, чтобы заставить move() сделать больше, чем просто спуститься вниз. Добавляем параметр direction. И в основном делаем именно то, что мы делали в предыдущем уроке, с несколькими небольшими (хотя и сбивающими с толку) изменениями. Но не волнуйтесь! Вам просто нужно один раз довести его до совершенства, потому что это функция, установите ее и забудьте о ней!

function move(element, direction, distance=10, duration="0.1s") {
   /* ... */
   // Determine if you're altering the top or left attribute
   var topOrLeft = (direction=="left" || direction=="right") ? "left" : "top";
   // Determine if you're adding or subtracting
   if (direction=="up" || direction=="left") { distance *= -1; }
   
   // Determine the changed value's destination
   var elStyle = window.getComputedStyle(element);
   var value = elStyle.getPropertyValue(topOrLeft).replace("px", "");
   var destination = (Number(value) + distance) + "px";
   // Determine the new location's coordinates
   var newLoc = (topOrLeft=="left") ?
    [destination, elStyle.getPropertyValue("top")] : 
    [elStyle.getPropertyValue("left"), destination];
   /* ... */
   // Pop the new location's coordinates into a new class definition
   elStyleSheet.innerHTML += `
      #` + elId + `.moved
      {
         transition: all ` + duration + ` linear;
         -webkit-transition: all ` + duration + ` linear;
         -moz-transition: all ` + duration + ` linear;
         -o-transition: all ` + duration + ` linear;
         top: ` + newLoc[1] + `;
         left: ` + newLoc[0] + `;
      }
   `;
   /* ... */
}

Все это немного сбивает с толку. К сожалению, такова природа зверя. Но это работает! Следует отметить одну вещь о том, как я это закодировал: я использую систему координат (x, y), которую вы бы видели в математическом классе, но CSS предпочитает ставить верх перед левым. Вот почему верхний является вторым элементом списка (err, 1st), а left - первым (umm, 0th).

Конечный результат

Я помещаю полную версию кода внизу здесь для нетерпеливых людей, которые прокручивают вниз, чтобы скопировать и вставить!

function move(element, direction, distance=10, duration="0.1s") {
   var elId = element.id;
   if (!elId) { throw("Cannot move an element without an ID!"); }
   var elStyleSheet = document.getElementById(elId + "-movement");
   if (!elStyleSheet) {
      elStyleSheet = document.createElement("style");
      elStyleSheet.id = elId + "-movement";
      document.head.appendChild(elStyleSheet);
   }
   var topOrLeft = (direction=="left" || direction=="right") ? "left" : "top";
   if (direction=="up" || direction=="left") { distance *= -1; }
   
   var elStyle = window.getComputedStyle(element);
   var value = elStyle.getPropertyValue(topOrLeft).replace("px", "");
   var destination = (Number(value) + distance) + "px";
   var oldLoc = [elStyle.getPropertyValue("left"), elStyle.getPropertyValue("top")];
   var newLoc = (topOrLeft=="left") ? [destination, oldLoc[1]] : [oldLoc[0], destination];
   elStyleSheet.innerHTML = elStyleSheet.innerHTML.replace(/\s+\#(.|\n)*?\}\s+\n/, "");
   elStyleSheet.innerHTML += `
      #` + elId + `.moved
      {
         transition: all ` + duration + ` linear;
         -webkit-transition: all ` + duration + ` linear;
         -moz-transition: all ` + duration + ` linear;
         -o-transition: all ` + duration + ` linear;
         top: ` + newLoc[1] + `;
         left: ` + newLoc[0] + `;
      }
   `;
   element.classList.add("moved");
}

Если вы нашли это полезным, оставьте для него. Если вы этого не сделали, или вы обнаружили ошибку, или у вас есть предложение, пожалуйста, оставьте мне комментарий, и я смогу его исправить!

Следуйте за мной, чтобы узнать больше!