Скрипт улетающего в корзину товара без jQuery

Во многих Интернет-магазинах можно встретить эффект, когда при нажатии кнопки «Добавить» фотография товара летит в направлении изображения корзины. Обычно его реализуют на jQuery с помощью метода transfer, но можно обойтись и чистым JavaScript. Это делается за несколько шагов:
  1. Определяем координаты фотографии товара и корзины на экране
  2. Создадим новый DOM-элемент с копией фотографии товара и задаём ему начальные координаты
  3. Рассчитаем расстояние, на которое нужно переместить фотографию
  4. Добавляем соответствующие transform и transition, после чего добавляем копию в документ.


Будем считать, что у нас в переменной picture лежит ссылка на DOM-элемент с изображением товара, в переменной cart — DOM-элемент с корзиной. Тогда каждый из шагов можно реализовать так:

Определение координат элементов

В современных броузерах для того, чтобы определить коодинаты DOM-элемента на экране, есть специальный метод: getBoundingClientRect(). Вызываем его и получаем координаты:
let picture_pos = picture.getBoundingClientRect(); let cart_pos = cart.getBoundingClientRect();
Теперь picture_pos и cart_pos — это объекты, у которых в свойствах left,top лежат координаты верхнего левого угла изображения относительно области просмотра, height, width — высота и ширина DOM-элемента.

Создание копии DOM-элемента и задание позиции

Копия DOM-элемента создаётся очень просто — используется метод cloneNode:
let picture2 = picture.cloneNode();
Однако если мы просто добавием сейчас picture2 в document, изображение появится в самом конце страницы. Нам же нужно, чтобы картинка появилось поверх исходной и при этом могла свободно перемещаться по экрану без учета влияния других элементов. Для этого зададим ей position: fixed и явно укажем координаты left и top на основе тех значений, которые мы получили раньше. И ещё на всякий случай пропишем z-index побольше:
  picture2.style.position="fixed";   picture2.style.left=picture_pos['left']+"px";   picture2.style.top=picture_pos['top']+"px";   picture2.style.border="none";   picture2.style.zIndex=32767;
Не забываем про то, что для свойств top и left нужно указывать и единицы измерения (пиксели)!

Расчёт расстояния перемещения

На первый взгляд, кажется, что тут всё просто: нужно из координат корзины вычесть начальные координаты картинки, и всё. Но поскольку значок корзины значительно меньше фотографии товара, то тогда поверх корзины окажется только левый верхний угол фотографии. Гораздо естетсвеннее будет смотрется, если на центр корзины попадёт центр фотографии. Чтобы получить координаты центра, прибавим к координатам верхнего левого угла половину высоты и ширины соответственно:
let start_x = picture_pos['left']+0.5*picture_pos['width']; let start_y = picture_pos['top']+0.5*picture_pos['height']; let delta_x = (cart_pos['left']+0.5*cart_pos['width'])-start_x; let delta_y = (cart_pos['top']+0.5*cart_pos['height'])-start_y;
Теперь в delta_x и delta_y у нас лежат расстояния, на которые нужно переместить фото товара так, чтобы его центр оказался поверх корзины.

Добавление эффектов и отображение

Новая картинка готова. Теперь зададим её перемещение по экрану через transform и его время в свойстве transition. Первое что приходит в голову — это менять в transition свойства left и top, но это плохое решение с точки зрения производительности, так как при каждом изменении делается перестройка DOM-дерева. Лучше использовать CSS-трансформации translateX и translateY: они перемещают только нужный элемент, не перестраивая дерево целиком, и делают это с использованием аппаратного ускорения на видеокарте, если есть такая возможность. В результате анимация получается более плавной, а ресурсов процессора уходит меньше. Кроме того, совместно с ними можно использовать и трансформацию scale для того, чтобы фотография плавно уменьшалась (в приведенном примере при подлете к корзине она будет уменьшена до 25%). И добавим её в DOM.
  document.body.appendChild(picture2);    void picture2.offsetWidth;   picture2.style.transform="translateX("+delta_x+"px)";   picture2.style.transform+="translateY("+delta_y+"px)";   picture2.style.transform+="scale(0.25)"; // уменьшаем до 25%   picture2.style.transition="1s"; // всё происходит за 1 секунду
Снова не забываем, что нужно указывать единицы измерения (px). Также обратите внимание на строку void picture2.offsetWidth;. Без неё броузер сделает ненужную оптимизиацию: сразу рассчитает конечную точку перемещения изображения и отобразит его там. Сбросив же свойство offsetWidth, мы вынуждаем броузер перерисовать страницу сразу, а уже потом показать нужный эффект перемещения.
Теперь остаётся только убрать элемент с экрана, когда анимация закончится. Сделаем это с помощью setTimeout и, для краткости, стрелочной функции:
setTimeout(()=>document.body.removeChild(picture2),960);

Итоговый код

Соберём все вместе и оформим в виде функции, которая принимает два параметра: DOM-элемент изображения товара и DOM-элемент корзины:
function move_to_cart(picture,cart) {   let picture_pos = picture.getBoundingClientRect();   let cart_pos = cart.getBoundingClientRect();     let picture2 = picture.cloneNode();   picture2.style.position="fixed";   picture2.style.left=picture_pos['x']+"px";   picture2.style.top=picture_pos['y']+"px";   picture2.style.border="none";   picture2.style.zIndex=32767;   let start_x = picture_pos['x']+0.5*picture_pos['width'];   let start_y = picture_pos['y']+0.5*picture_pos['height'];   let delta_x = (cart_pos['x']+0.5*cart_pos['width'])-start_x;   let delta_y = (cart_pos['y']+0.5*cart_pos['height'])-start_y;   document.body.appendChild(picture2);    void picture2.offsetWidth;   picture2.style.transform="translateX("+delta_x+"px)";   picture2.style.transform+="translateY("+delta_y+"px)";   picture2.style.transform+="scale(0.25)"; // уменьшаем до 25%   picture2.style.transition="1s"; // всё происходит за 1 секунду   setTimeout(()=>document.body.removeChild(picture2),960); }