Статьи

Выживание зомби-апокалипсиса: манипулирование SVG с помощью JavaScript

Многое уходит на то, чтобы пережить апокалипсис. Или, по крайней мере, на создание приложения HTML5, что может быть более или менее сложным в зависимости от приложения и апокалипсиса. До сих пор в первой части этой серии мы рассмотрели статические элементы SVG — текст, изображение, круги, прямоугольники и, что наиболее важно, пути. Во второй части описаны некоторые интересные анимационные трюки, которые можно выполнить без JavaScript.

Но наше приложение не собирается никого спасать от зомби, не так, как сейчас. Для этого нужно что-то дополнительное, например, кнопки, которые работают. Таким образом, в этом пошаговом руководстве, третьем в серии, будут представлены два различных метода реагирования на взаимодействие с пользователем, включая анимацию атрибутов внутри самого элемента SVG и использование JavaScript для манипулирования элементами Core DOM и SVG DOM.

Примечание. Код в этой статье основан на окончательном исходном коде части 2 данной серии. Сам код можно найти за http://justinwhitney.com/zombies/zombies_part2.htm . Также в этом пошаговом руководстве добавлены новые изображения. Полный архив ресурсов, использованных в этой серии, можно найти по адресу http://justinwhitney.com/zombies/zombieAssets.zip .

Использование элемента <set>

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

Традиционно это реализуется с помощью JavaScript для обмена изображениями, или с помощью CSS для определения стиля наведения, или с помощью любой из тысячи подобных техник. SVG добавляет свои собственные инструменты в пояс летучей мыши, позволяя встраивать изменения свойств в сами элементы, а затем связывать их с событиями мыши. Одним из наиболее распространенных примеров является использование элемента <set> для изменения обводки и / или цвета заливки.

Вернитесь к дремучим зомби. Прямо сейчас, поскольку они первоначально определены, медленный зомби обведен толстым красным ударом, в то время как быстрый зомби получает тонкий черный удар. Очевидно, что когда пользователь нажимает на быстрого зомби, это нужно будет изменить. Обычно, что-то должно быть добавлено к элементу <image>, чтобы ответить на щелчок. Но не в этом случае.

Взгляните на элемент <circle> с идентификатором «fastCircle». Прямо сейчас это определяется как:

  <circle id = "fastCircle" cx = "275" cy = "325" r = "40" stroke = "black" fill = "white" stroke-width = "2" /> 

Чтобы этот элемент реагировал на щелчок, вставьте элементы <set>, определяющие все возможные изменения штриха и ширины штриха, и (здесь важная часть) события мыши, с которых они должны начаться. В этом случае атрибуты «fastCircle» изменятся, когда изображение «fastZombie» получит событие mousedown, поэтому синтаксис будет следующим:

  <circle id = "fastCircle" cx = "275" cy = "325" r = "40" stroke = "black" fill = "white" stroke-width = "2">

   <set attributeName = "stroke" from = "black" to = "red" begin = "fastZombie.mousedown" />

   <set attributeName = "stroke-width" от = "2" до = "4" begin = "fastZombie.mousedown" />

 </ Круг> 

(Обратите внимание, что закрывающая скобка была заменена на закрывающий тег </ circle>.) Конечно, этот же элемент также должен реагировать на щелчок slowZombie, поэтому завершите его:

  <circle id = "fastCircle" cx = "275" cy = "325" r = "40" stroke = "black" fill = "white" stroke-width = "2">

    <set attributeName = "stroke" from = "black" to = "red" begin = "fastZombie.mousedown" />

   <set attributeName = "stroke-width" от = "2" до = "4" begin = "fastZombie.mousedown" />

   <set attributeName = "stroke" от = "красный" до = "черный" begin = "slowZombie.mousedown" />

   <set attributeName = "stroke-width" от = "4" до = "2" begin = "slowZombie.mousedown" />

 </ Круг> 

И «slowCircle» нуждается в том же лечении, наоборот:

  <circle id = "slowCircle" cx = "75" cy = "325" r = "40" stroke = "red" fill = "white" stroke-width = "4">

   <set attributeName = "stroke" from = "black" to = "red" begin = "slowZombie.mousedown" />

   <set attributeName = "stroke-width" от = "2" до = "4" begin = "slowZombie.mousedown" />

   <set attributeName = "stroke" от = "красный" до = "черный" begin = "fastZombie.mousedown" />

   <set attributeName = "stroke-width" от = "4" до = "2" begin = "fastZombie.mousedown" />

 </ Круг> 

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

Использование <set> для изменения атрибутов обводки в mousedown

Рисунок 1. Использование <set> для изменения атрибутов обводки в mousedown

Управление <text> DOM с помощью JavaScript: textContent

Использование <set> — изящный трюк, но у него есть одна большая проблема: не все SVG-совместимые браузеры реализуют эту конкретную функциональность. К счастью, в качестве спецификации на основе XML к SVG можно обращаться так же, как JavaScript обращается к любому документу на основе спецификации Core DOM, то есть через getElement () и setElement (). Итак, чтобы обеспечить максимальную совместимость с браузером, добавьте новую функцию setSpeed ​​(speed):

  <Скрипт>
 & NBSP;
 function setSpeed ​​(speed) {

   if (speed == 'Fast') {

     var circleSelected = document.getElementById ('fastCircle');

     var circleUnselected = document.getElementById ('slowCircle');

   } еще {

     var circleSelected = document.getElementById ('slowCircle');

     var circleUnselected = document.getElementById ('fastCircle');

   }

   circleSelected.setAttribute ( 'инсульт', 'красный');

   circleSelected.setAttribute ( 'штрих-ширина', '4');
  
   circleUnselected.setAttribute ( 'инсульт', 'черный');

   circleUnselected.setAttribute ( 'штрих-ширина', '2');

 }

 </ Скрипт> 

Эта функция захватывает элементы fastCircle и slowCircle, а затем устанавливает атрибуты «stroke» и «stroke-width», обращаясь к ним напрямую.

Теперь, когда круги переключаются на основе выбора пользователем быстрых или медленных зомби, текст также должен измениться. Это можно сделать, используя ту же технику — доступ к атрибутам SVG через Core DOM. Но в некоторых случаях к атрибуту элемента можно получить доступ напрямую через SVG DOM. Это не только сокращает код, но и приводит к повышению производительности. Хитрость заключается в знании синтаксиса нужного вам атрибута.

Содержимое элемента <text> является одним из тех атрибутов, к которым можно получить доступ через SVG DOM, и в этом случае textContent является правильным ответом. Это «textContent». Пожалуйста.

  function setSpeed ​​(speed) {

   if (speed == 'Fast') {

     var circleSelected = document.getElementById ('fastCircle');

     var circleUnselected = document.getElementById ('slowCircle');

   } еще {

     var circleSelected = document.getElementById ('slowCircle');

     var circleUnselected = document.getElementById ('fastCircle');

   }

   circleSelected.setAttribute ( 'инсульт', 'красный');

   circleSelected.setAttribute ( 'штрих-ширина', '4');

   circleUnselected.setAttribute ( 'инсульт', 'черный');

   circleUnselected.setAttribute ( 'штрих-ширина', '2');

   var speedText = document.getElementById ('speedText');

   speedText.textContent = скорость;

 } 

Не забудьте добавить событие onmouseup к изображениям зомби:

  <image id = "slowZombie" x = "375" y = "1875" width = "175" height = "304" transform = "scale (.16, .16)" xlink: href = "zombie.svg" onmouseup = "SetSpeed ​​( 'Slow');">

 <image id = "fastZombie" x = "1630" y = "1875" width = "175" height = "304" transform = "scale (.16, .16)" xlink: href = "zombie.svg" onmouseup = "SetSpeed ​​( 'Fast');"> 

Нажатие зомби теперь должно изменить текст, а также их кружки контейнера, как на рисунке 2.

textContent Изменение элемента <text>

Рисунок 2. Изменение textContent для элемента <text>

IEBlog в MSDN более детально сравнивает SVG DOM с Core DOM и охватывает другие передовые практики. , Спецификацию SVG DOM можно найти здесь .

Добавление новых элементов SVG

Еще в первой части этой серии мы представили элемент <path> и использовали несколько из них для создания элементов управления приращением / уменьшением на панели controlPanelBox. Сейчас самое время использовать возможности JavaScript, чтобы вдохнуть жизнь в эти элементы управления, сначала создавая новых зомби, затем добавляя быдло и здания и, наконец, удаляя эти элементы по убыванию.

Любой, кто знаком с созданием новых элементов на основе определения пространства имен, распознает команду document.createElementNS. Это также ключ к созданию новых элементов SVG.

В заголовке создайте новую функцию JavaScript с именем newZombie (). Более надежный код будет добавлен на мгновение, но сейчас создайте зомби, ссылаясь на определение «изображения» пространства имен «http://www.w3.org/2000/svg»:

  function newZombie () {

   var svg = document.createElementNS ("http://www.w3.org/2000/svg", "image");

 } 

Обратите внимание, что после создания элемента SVG следующий шаг демонстрирует некоторую аномалию в манипулировании его атрибутами. Хотя на большинство атрибутов элемента <image> можно ссылаться через setAttribute, источник самого изображения, его атрибут xlink: href, не может. Этот атрибут должен быть определен путем ссылки на спецификацию источника, в данном случае это href-определение пространства имен «http://www.w3.org/1999/xlink».

Вики W3 покрывает эту путаницу, указывая на наиболее распространенные ошибки при попытке создать новый элемент <image>.

  function newZombie () {

   var svg = document.createElementNS ("http://www.w3.org/2000/svg", "image");

   svg.setAttributeNS ( 'http://www.w3.org/1999/xlink','href','zombie.svg');

 } 

Ранее в этой серии при размещении зомби-элементов <image> для элемента управления «Скорость» потребовалась некоторая безумная хитрость, чтобы сделать изображения полностью совместимыми с браузерами. Интуитивно понятно, что установка ширины и высоты, необходимой для изображения, а затем размещение его в желаемых координатах приведет к желаемым результатам. И в большинстве браузеров это так. Но для выбросов требуется некоторое масштабирование. В качестве примера рассмотрим определение slowZombie <image>:

  <image id = "slowZombie" x = "375" y = "1875" width = "175" height = "304" transform = "scale (.16, .16)" xlink: href = "zombie.svg" onclick = "SetSpeed ​​( 'Slow');"> 

Цель здесь состояла в том, чтобы разместить изображение 50 × 50 (точнее, «высота» равна 50, а ширина пропорциональна). Фактический источник zombie.svg определяет изображение размером 175 × 304. Итак, чтобы сделать эту работу, размеры элемента <image> определены как 175 × 304, а затем преобразование: масштаб применяется с использованием 0,16 в качестве коэффициента масштабирования. Из-за масштабирования координаты x, y также необходимо изменить, чтобы после масштабирования результат составил 60 300.

При динамическом создании нового элемента <image> должно произойти подобное voodoo:

  function newZombie () {

   var svg = document.createElementNS ("http://www.w3.org/2000/svg", "image");

   svg.setAttributeNS ( 'http://www.w3.org/1999/xlink','href','zombie.svg');

   svg.setAttribute ( 'ширина', '175');

   svg.setAttribute ( 'высота', '304');

 } 

Но вместо вычисления координат x, y на основе коэффициента масштабирования и размещения изображения в этих координатах попробуйте другой подход. Вместо этого используйте transform: translate, чтобы установить местоположение зомби. Преобразование translate переопределяет происхождение элемента. Так, например, вместо размещения объекта с координатами x, y в 50 100 на холсте с началом координат 0,0, translate переместит саму точку начала холста в 50 100 и поместит объект в 0,0. Синтаксис для этого будет:

  svg.setAttribute ('transform', 'translate (50, 100)'); 

Несколько преобразований могут быть объединены в одной строке, поэтому завершите функцию с помощью «масштабного» преобразования:

  function newZombie () {

   var svg = document.createElementNS ("http://www.w3.org/2000/svg", "image");

   svg.setAttributeNS ( 'http://www.w3.org/1999/xlink','href','zombie.svg');

   svg.setAttribute ( 'ширина', '175');

   svg.setAttribute ( 'высота', '304');

   шкала вар = .16;

   var x = Math.floor (Math.random () * 550);

   var y = Math.floor (Math.random () * 350);

   svg.setAttribute ('transform', 'translate (' + (x) + ',' + (y) + ') scale (' + scale + ',' + scale + ')');

   document.getElementById ( 'Citybox') AppendChild (SVG).

 } 

Этот пример также устанавливает x, y в случайное место на панели «cityBox», которое составляет 600 × 400 (компенсируя размер самого изображения 50 × 50). Источник (0,0) по умолчанию находится в верхнем левом углу. В конце новый элемент добавляется в DOM, как и любой другой элемент, в этом случае в качестве родительского элемента указывается элемент «cityBox» <svg>.

Чтобы вызвать функцию, найдите элемент <path> с идентификатором «zombieMore» и добавьте функцию newZombie () к событию onmouseup. Это будет кнопка увеличения «Население города (в тысячах)», важный фактор при прогнозировании распространенности зомби во время апокалипсиса. Пока не беспокойтесь о других кнопках, так как это только для тестирования.

<path id = ”zombieMore” d = ”M 300 50 l -50 -25 l 0 50 l 50 -25 ″ stroke =” black ”stroke-width =” 1 ″ fill = ”red” onmouseup = ”newZombie (); />

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

БЕГАТЬ!!!

Рисунок 3. БЕГ !!!

Манипулирование DOM работает, но код может использовать несколько эстетических настроек. Во-первых, в то время как голодный зомби может выглядеть точно ловящим и предположительно потребляющим мозг человека, это скорее сценарий конечной игры. Чтобы служить предиктором «выживания», приложение могло бы лучше работать с безопасной зоной вокруг бегуна. Во-вторых, если зомби не находятся в непосредственной близости от мозгов или шума, они, как правило, сталкиваются с проблемами в направлении (о чем свидетельствует тот, кто пытается писать и ходить одновременно). Так что для разнообразия может показаться лучше перевернуть некоторые изображения по горизонтали.

Первая настройка может быть выполнена с помощью базового JavaScript. Следующий код устанавливает безопасную зону 200 × 100 вокруг скримера.

  function newZombie () {

   var svg = document.createElementNS ("http://www.w3.org/2000/svg", "image");

   svg.setAttributeNS ( 'http://www.w3.org/1999/xlink','href','zombie.svg');

   svg.setAttribute ( 'ширина', '175');

   svg.setAttribute ( 'высота', '304');

   шкала вар = .16;

   var x = Math.floor (Math.random () * 550);

   var y = Math.floor (Math.random () * 350);

   var cityWidth = 600;

   var cityHeight = 400;

   var safezoneWidth = 200;

   var safezoneHeight = 100;

   var safezoneX = Math.round ((cityWidth - safezoneWidth) / 2, 0);

   var safezoneY = Math.round ((cityHeight - safezoneHeight) / 2, 0);

   if (((safezoneX - 50) <= x) && (x <= (safezoneX + safezoneWidth)) &&

   ((safezoneY - 50) <= y) && (y <= (safezoneY + safezoneHeight))) {

     switch (Math.floor (Math.random () * 4)) {

       случай 0:

       x = safezoneX - 50;

       перемена;

       Дело 1:

       x = safezoneX + safezoneWidth;

       перемена;

       случай 2:

       у = безопасная зона Y - 50;

       перемена;

       случай 3:

       y = safezoneY + safezoneHeight;

       перемена;

     }

   }

   svg.setAttribute ('transform', 'translate (' + (x) + ',' + (y) + ') scale (' + scale + ',' + scale + ')');

   document.getElementById ( 'Citybox') AppendChild (SVG).

 } 

Хотя этот код не имеет прямого отношения к SVG, этот код влияет на размещение изображения. Во-первых, он устанавливает безопасную зону 200 × 100 и вычисляет исходные координаты x, y зоны, предполагая, что она находится в центре холста 600 × 400. Во-вторых, если текущие координаты x, y зомби находятся в этой зоне, изображение перемещается в произвольно выбранном направлении, пока оно не окажется за пределами безопасной зоны.

На рисунке 4 показано, как выглядит безопасная зона после особенно неприятной волны нежити.

Безопасная зона

Рисунок 4. Безопасная зона

Лучше, но это все равно больше похоже на распродажу в Черную пятницу, чем на заражение зомби (небольшая разница, но, тем не менее, разница). Было бы лучше, если бы некоторые из этих зомби были перевернуты. Но есть проблема: как отмечено во второй части этой серии, атрибут transform может использоваться для масштабирования, поворота, перекоса, преобразования или определения совершенно новой матрицы. Обратите внимание на то, чего нет в этом списке: отразить, горизонтальное или вертикальное. Трагический недосмотр, но тот, который можно преодолеть с помощью комбинации перевода и масштаба.

Вот хитрость: элемент можно перевернуть по вертикали или по горизонтали, установив его масштабный коэффициент на отрицательное число. Однако это масштабирует элемент по отношению к исходной точке его холста. Таким образом, если оставить точку отсчета в (0,0), а затем применить шкалу (-1,1), элемент будет перевернут в своего рода отрицательную зону слева от монитора. Он будет существовать, и преобразование будет действительным, но в действительности оно заставит элемент исчезнуть.

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

Подвести итоги:

* Нет преобразования: атрибут flip — он не существует.

* Использование transform: scale (-1,1) само по себе приведет к полному отключению изображения от родительского элемента <svg>.

* Использование комбинации transform: translate и transform: scale эффективно позволяет перевернуть изображение на месте; в этом случае transform: scale выполняет двойную функцию, масштабируя изображение до нужного вам размера, что нужно было сделать в любом случае.

Чтобы это произошло, добавьте некоторую умную рандомизацию, корректировку размещения, чтобы компенсировать размер изображения, и настройку кода transform: scale:

  function newZombie () {

   var svg = document.createElementNS ("http://www.w3.org/2000/svg", "image");

   svg.setAttributeNS ( 'http://www.w3.org/1999/xlink','href','zombie.svg');

   svg.setAttribute ( 'ширина', '175');

   svg.setAttribute ( 'высота', '304');

   шкала вар = .16;

   var x = Math.floor (Math.random () * 550);

   var y = Math.floor (Math.random () * 350);

   var cityWidth = 600;

   var cityHeight = 400;

   var safezoneWidth = 200;

   var safezoneHeight = 100;

   var safezoneX = Math.round ((cityWidth - safezoneWidth) / 2, 0);

   var safezoneY = Math.round ((cityHeight - safezoneHeight) / 2, 0);

   if (((safezoneX - 50) <= x) && (x <= (safezoneX + safezoneWidth)) &&

   ((safezoneY - 50) <= y) && (y <= (safezoneY + safezoneHeight))) {

     switch (Math.floor (Math.random () * 4)) {

     случай 0:

     x = safezoneX - 50;

     перемена;

     Дело 1:

     x = safezoneX + safezoneWidth;

     перемена;

     случай 2:

     у = безопасная зона Y - 50;

     перемена;

     случай 3:

     y = safezoneY + safezoneHeight;

     перемена;

     }

   }

   flip = Math.floor (Math.random () * 2) * 2-1;  // результаты в -1 или 1

   х + = 25 - 25 * флип;  // настроить размер 50x50 зомби;  приводит к +50 или +0

   svg.setAttribute ('transform', 'translate (' + (x) + ',' + (y) + ') scale (' + (scale * flip) + ',' + scale + ')');

   document.getElementById ( 'Citybox') AppendChild (SVG).

 } 

Это приводит к немного более хаотичному взгляду на апокалипсис, показанный на рисунке 5.

Zombies. Листать зомби. С помощью transform: перевод и преобразование: масштабирование применяется

Рисунок 5. Зомби. Листать зомби. С помощью transform: перевод и преобразование: масштабирование применяется

Чтобы увидеть прогноз выживания зомби-апокалипсиса в действии, как сейчас, перейдите на http://justinwhitney.com/zombies/zombies_part3.htm . Обязательно посмотрите исходный код страницы для примера кода до этого момента.

Завершение

Это начинает выглядеть серьезно. Бедный жевательный человек обречен. Теперь есть только две вещи, которые могут спасти крикуна: торговые центры и быдло. Но, как и в любом хорошем сериале, это должен быть обломок. Будут ли быдло вести человека к безопасности? Найдут ли они торговый центр вовремя, чтобы избежать хищных полчищ? Будьте на связи.


Эта статья является частью технической серии HTML5 от команды Internet Explorer. Испытайте концепции, изложенные в этой статье, в течение трех месяцев бесплатного кросс-браузерного тестирования BrowserStack @ http://modern.IE .