БлогNot. JavaScript: произвольно расставляем объекты на картинке

JavaScript: произвольно расставляем объекты на картинке

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

Порядок персонажей определяется порядком описания их разделов в разметке, то есть, #hero1 изначально находится "за спиной" у #hero2 и т.д., но последний перетаскиваемый персонаж должен оказаться впереди остальных, если их спрайты накладываются. Также в коде простым образом учтены мобильные устройства с их сенсорными экранами и отличными от классических событиями яваскрипта.

Код реализуется на разделах div, потому что у картинок img имеется собственная обработка событий drag-and-drop браузерами, надо её отдельно отключать и т.п.

Вообще говоря, все картинки должны быть одного размера и с прозрачным фоном, чтобы не было неожиданных эффектов. Удобно, если персонажей можно разместить на одной картинке, как и сделано здесь, а потом "нарезать" отображаемые части с помощью CSS (см. .hero и #hero1 ... #hero5 в стиле).

Вот содержательная задача для демонстрации кода в работе. С помощью перетаскивания мышкой или пальцем расставь марионеток на теле России... автору лучшей композиции - скидка в полтора года от срока с перспективой УДО!

Код этого примера с точностью до адресов картинок field.png и heroes.png (документ HTML без обрамления, кодировка Юникода UTF-8). В старом Internet Explorer это не должно работать, в остальных браузерах, в том числе, в MS Edge - вполне себе.

<style>
/* Игровое поле */
#field {
 background: url(field.png);
 width: 875px;
 height: 449px;
 float: left;
 margin: 0;
 padding: 0;
}

/* Герои (переносимые элементы) */
.hero {
 background: url(heroes.png);
 width: 175px;
 height: 310px;
 float: left;
 margin: 0;
 padding: 0;
}

/* Сдвиг по X и Y каждого героя с картинки heroes.png */
#hero1 { background-position: 0 0; }
#hero2 { background-position: -175px 0; }
#hero3 { background-position: -350px 0; }
#hero4 { background-position: -525px 0; }
#hero5 { background-position: -700px 0; }

.draggable { cursor: pointer; }
</style>

<div id="field"></div>
<div style="clear:both"></div>
<div class="hero draggable" id="hero1"></div>
<div class="hero draggable" id="hero2"></div>
<div class="hero draggable" id="hero3"></div>
<div class="hero draggable" id="hero4"></div>
<div class="hero draggable" id="hero5"></div>
<div style="clear:both"></div>

<script>
let mobile = /android|blackberry|ios|ipad|ipod|iphone|mobile|webos/i.test(navigator.userAgent);

function draggableHeroes (e) {
 let draggable = e.target;
 if (!draggable.classList.contains('draggable')) return;
 draggable.ondragstart = () => false;
 if (mobile) document.body.style.overflow = "hidden"
 e.preventDefault();
  
 let dragWidth = draggable.offsetWidth;
 let dragHeight = draggable.offsetHeight;
 let shiftX = (e.clientX || e.touches[0].clientX) - draggable.getBoundingClientRect().left;
 let shiftY = (e.clientY || e.touches[0].clientY) - draggable.getBoundingClientRect().top;
 draggable.style.position = 'absolute';
 draggable.style.zIndex = 99;
 document.body.append (draggable);

 function setPosition (x, y) {
  let top = document.documentElement.scrollTop;
  let maxX = document.documentElement.clientWidth;
  let maxY = document.documentElement.clientHeight + top;
  let shiftPlus = mobile ? 24 : 12;
  let posX = x - shiftX;
  let posY = y - shiftY + top;
  if (posX + dragWidth > maxX) posX = maxX - dragWidth;
  if (posY + dragHeight > maxY) {
   posY = maxY - dragHeight;
   window.scrollBy (0, shiftPlus);
  }
  if (posX < 0) posX = 0;
  if (posY < top) {
   posY = top;
   window.scrollBy(0, -shiftPlus)
  }
  draggable.style.left = posX+'px';
  draggable.style.top = posY+'px';
 }
  
 let x = mobile ? e.touches[0].clientX : e.clientX;
 let y = mobile ? e.touches[0].clientY : e.clientY;
 setPosition(x, y);
    
 function moveAt (e) {
  let x = mobile ? e.touches[0].clientX : e.clientX;
  let y = mobile ? e.touches[0].clientY : e.clientY;
  setPosition(x, y);
 }

 function drop () {
  if (mobile) {
   document.removeEventListener('touchmove', moveAt);
   document.removeEventListener('touchend', drop);
   document.body.style.overflow = '';
  } 
  else {
   document.removeEventListener('mousemove', moveAt);
   document.removeEventListener('mouseup', drop);
  }
 }

 if (mobile) {
  document.addEventListener('touchmove', moveAt);
  document.addEventListener('touchend', drop);
 } 
 else {
  document.addEventListener('mousemove', moveAt);
  document.addEventListener('mouseup', drop)
 }
}

if (mobile) document.addEventListener('touchstart', draggableHeroes);
else document.addEventListener('mousedown', draggableHeroes);
</script>
<noscript>
 <p>Требуется JavaScript для работы приложения</p>
</noscript>
картинка field.png
картинка field.png
картинка heroes.png
картинка heroes.png

Для фона использована картина И.С. Глазунова "Вечная Россия", "пиксель-артные" персонажи, которых, надеюсь, вы всех узнаёте, лежат вот в этом файле Photoshop во вдвое большем масштабе, чем в приложении.

 Архив .zip с файлом .psd персонажей (442 Кб)

P.S. Вот и первая расстановка, по-моему, изрядно.

расстановка
расстановка
ещё одна расстановка, тоже не на фоне картины, скрипт это позволяет
ещё одна расстановка, тоже не на фоне картины, скрипт это позволяет

теги: программирование javascript графика css искусство

21.06.2020, 17:34; рейтинг: 56