Взаимодействие HTML5 и JavaScript (HTML5 and JavaScript interaction)

Автор: | 22.12.2018

Введение
Структура  HTML документа
Подключение и выполнение JavaScript
Объектная модель HTML-документа
Отображение графики с помощью элемента <canvas>
Описание игры “Уголки”
Полезные ссылки

 

Введение

В статье описываются особенности создания графических приложений для WEB, связанных с аффинными преобразованиями. В Web приложениях элементы интерфейса описывается в HTML файлах, а логика приложения – в коде javascript (JS).

При создании программных систем стремятся, чтобы описание данных, по возможности, было отделено от описания управляющих процедур. Это позволяет легко структурировать данные. Отделение данных от управляющих процедур обусловило создание новых языков, обеспечивающих описание данных и управление ими.

HTML (HyperText Markup Language) – язык разметки гипертекста для описания структуры Web-страницы. Основным компонентом HTML является тег (tag) – код, который командует Web-браузеру выполнить определенную задачу типа создания абзаца или вставки изображения. HTML не является языком программирования, но для организации динамических Web-страниц в него можно включать программы на языке Javascript, в пределах тегов <script> и </script>.

Основным инструментом работы и динамических изменений на HTML странице является DOM (Document Object Model) – объектная модель. Согласно DOM-модели,  объекты, описываемые в пределах HTML-тегов, структурированы иерархическим способом по мере вложенности один в другой. Ссылку на объекты можно обнаружить  в DOM с помощью JavaScript кода.

В HTML5 определен элемент <canvas> как «растровый холст, который может быть использован для отображения  2D графики.  Каждый холст имеет контекст рисования, для которого определены методы и свойства рисования. Объект canvas позволяет также получить WebGL контекст,  который обеспечивает 3D графику, используя возможности библиотеки OpenGL.

Структура  HTML документа

HTML (HyperText Markup Language) – язык разметки гипертекста,  предназначенный для создания Web-страниц [16]. HTML представляет собой достаточно простой набор кодов, которые описывают структуру документа. Основным компонентом HTML является тег (tag) – это код, который командует Web-браузеру выполнить определенную задачу типа создания абзаца или вставки изображения. Теги имеют атрибуты, значения которых могут быть текстовыми, типа left или right, а также числовыми, как например ширина и высота изображения. Теги представляют собой зарезервированные последовательности символов, начинающиеся с < (знака меньше) и заканчивающиеся > (знаком больше).  Закрытие тега отличается от открытия только наличием символа ‘/‘. Предположим, у нас есть гипотетический атрибут форматирования текста, управляемый кодом <X>, и мы хотим применить его к словам «Это мой текст». HTML-последовательность кодов будет выглядеть так:

   <X>Это мой текст</X>

Теги могут вкладываться друг в друга иерархически, но без пересечений, то есть допустимо вложение вида <teg1><teg2></teg2> </teg1>, но недопустимо: <teg1><teg2> </teg1></teg2>.

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

HTML- программа должна начинаться тегом <html> и заканчиваться тегом </html>. Суммируя вышесказанное приведем общую структуру HTML-файла :

<html>
     <head>
           <title>Заголовок документа</title>
     </head>
          <body>
           ...    (здесь будут другие теги тела программы)
          </body>
</html>

Вы можете думать об HTML как о тегах и угловых скобках. Это конечно важная часть, но не вся. Спецификация HTML5 также устанавливает, как эти угловые скобки взаимодействуют с JavaScript посредством объектной модели документа (Document Object Model, DOM). HTML не является языком программирования, но web-страницы могут включать в себя встроенные программы-скрипты на языках Javascript и Visual Basic Script и программы-апплеты на языке Java. Для вставки в HTML-программу фрагмента, написанного на языке JavaScript или Viual Basic Script сценариев, используют теги <script> и </script>.

JavaScript – это скриптовый язык программирования, код которого, выполняется на стороне клиента (пользователя). Применяется обычно для организации на сайте динамических HTML страниц, без перезагрузки самой страницы, т.е. без обращения к серверу. Javascript – не Java, а совсем другой язык. Он похоже называется, но не более того.

Подключение и выполнение JavaScript

Можно выделить 3 способа подключения скриптов:

  1. подключение в любом месте;
  2. вынос скриптов в заголовок HEAD;
  3. внешние скрипты.

Когда браузер читает HTML-страничку, и видит <script> – он первым делом читает и выполняет код, а только потом продолжает читать страницу дальше.

Так, в следующем примере показано начало страницы, затем три раза выполнится функция alert, которая выводит окошко с информацией, а только потом появится остальная часть страницы

Код странички:

<html> 
  <body> 
     <h1>Считаем кроликов</h1> 
     <script type="text/javascript"> 
         for(var i=1; i<=3; i++) { 
             alert("Из шляпы достали "+i+" кролика!") 
         } 
     </script> 
     <h1>...Посчитали</h1> 
 </body> 
</html>

Обычно javascript стараются отделить от собственно документа. Для этого его помещают внутрь тега HEAD, а в теле страницы по возможности оставляется чистая верстка. В следующем примере javascript-код только описывает функцию count_rabbits, а ее вызов осуществляется по нажатию на кнопку input.

<html> 
     <head> 
         <script type="text/javascript"> 
             function count_rabbits() { 
                 for(var i=1; i<=3; i++) { 
                    // оператор + соединяет строки 
                    alert("Из шляпы достали "+i+" кролика!") 
                 } 
             } 
          </script> 
     </head> 
     <body> 
  <input type="button" onclick="count_rabbits()" value="Считать кролей!"/> 
     </body> 
 </html>

Для указания запускаемой по клику функции в input был использован атрибут onclick. Это лишь вершина мощного айсберга javascript-событий.

Обычно javascript-код вообще не пишут в HTML, а подключают отдельный файл со скриптом, вот так:

<script src="/my/script.js"></script>

При этом файл /my/script.js содержит javascript-код, который иначе мог бы находиться внутри тега <script>.

Это очень удобно, потому что один и тот же файл со скриптами можно подключать на разных страницах. При правильных настройках сервера браузер закеширует его и не будет скачивать каждый раз заново.

Чтобы подключить несколько скриптов используйте несколько таких тегов:

<script src="/js/script1.js"></script> 
<script src="/js/script2.js"></script> 
...

При указании атрибута src содержимое тега игнорируется. То есть, одновременно подключить внешний файл и написать что-то внутри тега нельзя. Придется делать два разных тега <script>: первый с src, второй – с командами, которые будут выполнены после выполнения внешнего файла.

Объектная модель HTML-документа

Javascript умеет:

  1. Изменять страницу (писать на ней текст, добавлять и удалять теги, менять стили элементов и т.п.).
  2. Реагировать на события (клик мыши, окончание загрузки страницы) выполнением соответствующей функции.
  3. Выполнять запросы к серверу и загружать данные без перезагрузки страницы.

Основным инструментом работы и динамических изменений на странице является DOM (Document Object Model) – объектная модель, используемая для XML/HTML-документов. Согласно DOM-модели, документ является иерархией. Каждый HTML-тег образует отдельный элемент-узел, каждый фрагмент текста – текстовый элемент, и т.п.  Проще говоря, DOM – это представление документа в виде дерева тегов. Это дерево образуется за счет вложенной структуры тегов плюс текстовые фрагменты страницы, каждый из которых образует отдельный узел.

Например, для html-документа представлено дерево DOM.

 

Теги образуют узлы-элементы (element node). Текст представлен текстовыми узлами (text node). И то и другое – равноправные узлы дерева DOM.

Как нам изменять эти самые узлы в этом дереве. Рассмотрим это на примере работы со списками. На рисунке ниже представлена  html-страничка  с небольшим списком. Кнопки на страничке позволяют  добавить (или удалить) несколько значений из списка.  Если нажать кнопку «Добавить в список», у вас откроется окно для ввода, куда вы можете ввести название нового пункта. После нажатия кнопки OK к списку добавится новый элемент списка. Если нажать кнопку «Удалить из списка», то исчезнет нижняя строка из списка.

Фрагмент кода HTML:

<ol id="spisokst" style = "display:block;width:270px;background-color:#4EAEC1;color:black;">
             <li>Первоя строка</li>
             <li>Вторая строка</li>
             <li>Третья строка</li>
       </ol>
<table>
  <tr>
   <td>
    <p onClick="javascript:addLi()"
  style = "display:block;width:150px;background-color:#4EAEC1;color:black;" 
     align = "center"
  onmouseover="this.style.color = '#ffffff';this.style.cursor = 'pointer';"
     onmouseout="this.style.color = 'black';">
     Добавить в список
    </p>
  </td>
  <td>
   <p onClick="javascript:deleteLi()"
  style = "display:block;width:150px;background-color:#4EAEC1;color:black;" 
    align = "center"
    onmouseover="this.style.color = '#ffffff';this.style.cursor = 'pointer';"
    onmouseout="this.style.color = 'black';" >
    Удалить из списка
   </p>
  </td>
 </tr>
</table>

Код JavaScript:

//пишем функцию добавления нового пункта в списке
function addLi() {
var stroka = prompt("Введите название строки:", "");//для ввода названия
       if (stroka){
var ol = document.getElementById ("spisokst");//находим наш список
var li = document.createElement("LI");//создаем элемент списка
 ol.appendChild(li);//присваиваем нашему списку новый элемент
var text = document.createTextNode(stroka);//создаем узел текст
li.appendChild(text);//присваиваем текст новому пункту списка
       }
}
//пишем функцию удаления пунктов из списка
function deleteLi() {
    var ol = document.getElementById ("spisokst");//находим список
    var lastLi = ol.lastChild;//в переменной храним элемент списка
//проверяем на наличие элемента, исключая пробелы, таб. и коммен.
        while (lastLi && lastLi.nodeType != 1){
          lastLi = lastLi.previousSibling;
       }
       if (lastLi){
lastLi.parentNode.removeChild(lastLi);//удаляем пункт списка,
//если в списке еще что-то осталось
         }
       }

Как все это происходит, давайте разбираться. У нас есть некий список, помеченный id = spisokst для того чтобы нам было его легче найти, из JavaScript. Затем мы пишем две функции на javascript:

  1. addLi() – для добавления новых строк. В функции addLi мы просим пользователя ввести название новой строки с помощью встроенной функции prompt, и помещаем это название в переменную stroka (переменные объявляются с помощью оператора var). Затем с помощью метода getElementById объекта document находим нужный нам id списка. Потом с помощью метода createElement все того же объекта document создаем элемент LI (вы, наверное, заметили, что при создании элемента, его название пишется с большой буквы). Далее с помощью метода appendChild мы присваиваем нашему списку еще один новый пункт LI. Затем мы создаем новый узел текст и передаем ему значение, которое ввел пользователь в нашу переменную stroka. И снова присваиваем, только уже элементу li, новое его значение (или его дочерний узел т.е. текст) с помощью метода appendChild.
  2. deleteLi() – для удаления этих строк. Здесь мы также находим наш список с помощью метода document.getElementById и передаем ему значение spisokst. Затем заводим новую переменную lastLi и присваиваем ей значение последнего пункта в нашем списке (ol.lastChild). Здесь существует небольшая проблема, по стандарту при формировании страницы HTML все пробелы, табуляции, комментарии в коде HTML учитываются, но не выводятся на экран. Поэтому запомните, при обращении к последним элементам, какого-нибудь узла, там может оказаться пробел, табуляция или комментарии, поэтому результат может немного отличаться от действительного. Например, если бы здесь не было учтено этого, то пункты в списке удалялись через один или, в некоторых случаях, через два клика на кнопку «Удалить из списка» и чтобы этого избежать, необходимо проверять, что находиться в последнем узле.

Функционал кнопок реализован на javascript путем обработки событий, например, при клике (событие – onClick=»javascript:addLi()») на кнопку «Добавить в список» запускается функция addLi() а при клике на кнопку «Удалить из списка» запускается функция deleteLi(). Остальное сделано для визуальных эффектов. Например, событие onmouseover означает, что при наведении курсора мыши будет что-то выполняться.

Отображение графики с помощью элемента <canvas>

В HTML5 определен элемент <canvas> как «растровый холст, который может быть использован для отображения диаграмм, игровой графики или изображений на лету [19]. Холст – это прямоугольная область на вашей странице, где с помощью JavaScript можно рисовать что пожелаете. Холст — это двумерная сетка. Координата 0,0 находится в левом верхнем углу холста. Вдоль оси X значения растут к правому краю холста. По оси Y значения растут к нижнему краю холста.

HTML код для определения холста выглядит так.

<canvas width="300" height="225"></canvas>

У вас может быть несколько элементов <canvas> на одной странице. Каждый холст будет отображаться в DOM и сохранять свое собственное состояние. Если вы добавите каждому холсту атрибут id, то можете получить к ним доступ, как и к любому другому элементу. Расширим наш код, включив атрибут id.

<canvas id="b" width="300" height="225"></canvas>

Теперь легко можно обнаружить элемент <canvas> в DOM с помощью JavaScript кода.

<html> 
     <head> 
         <script src="draw.js" type="text/javascript"> </script>
     </head> 
     <body onload="draw_b();"> 
  <canvas  id="b"  width="300" height="225" >  </canvas>
     </body> 
 </html>

Каждый холст изначально пустой. Давайте что-нибудь нарисуем:

function draw_b() {
  var b_canvas = document.getElementById("b");
  var b_context = b_canvas.getContext("2d");
  b_context.fillRect(50, 25, 150, 100);
}

Первая строка функции не делает ничего особенного, она просто находит элемент <canvas> в DOM. Каждый холст имеет контекст рисования, в котором и происходят все эти смешные штучки. Как только вы нашли элемент <canvas> в DOM (с помощью document.getElementById() или любым другим способом), вызываете метод getContext(). При этом необходимо указать строку «2d» в методе getContext().

Итак, у вас есть элемент <canvas> и есть контекст рисования, где определены методы и свойства рисования. Имеется целая группа свойств и методов посвященных рисованию прямоугольников. Вызов метода fillRect() рисует прямоугольник и заполняет его текущим стилем заливки, исходно черный цвет, пока вы его не измените. Прямоугольник задается левым верхним углом (50, 25), шириной (150) и высотой (100).  Чтобы рисовать прямые линии карандашом, можно использовать следующие два метода: moveTo(х, у) перемещает карандаш к указанной начальной точке; lineTo(х, у) рисует линии до указанной конечной точки.

Описание игры “Уголки”

Код к игре уголки

“Уголки” – это многовековая настольная игра с множеством разных вариантов. В этом примере (рис.7.6) версия для одного игрока с девятью фишками и полем 9х9. В начале игры фишки заполняют поле 3х3 в левом нижнем углу доски. Цель игры в том, чтобы переместить все фишки в поле 3х3 в правый верхний угол доски за наименьшее число ходов.

В Уголках существует два типа правильных перемещений:

  • взять фишку и переместить ее на любую соседнюю пустую клетку;
  • взять фишку и перепрыгнуть через одну или более соседних фишек.

Ниже выделены несколько фрагментов кода, которые непосредственно выполняют рисование на холсте и отвечают на щелчки мыши по нему. Во время загрузки страницы мы инициализируем игру, установив размеры самого <canvas> и сохраняя указатель на его контекст рисования.

gCanvasElement.width = kPixelWidth;
gCanvasElement.height = kPixelHeight;
gDrawingContext = gCanvasElement.getContext("2d");

Затем мы добавим отслеживание события щелчка для элемента <canvas>.

gCanvasElement.addEventListener("click", halmaOnClick, false);

Функция halmaOnClick() вызывается, когда пользователь щелкает где-нибудь внутри холста. Его аргумент — это объект MouseEvent, который содержит информацию о том, где пользователь щелкал.

function halmaOnClick(e) {
var cell = getCursorPosition(e);
// Остальное это просто логика игры
for (var i = 0; i < gNumPieces; i++) {
if ((gPieces[i].row == cell.row) &&
(gPieces[i].column == cell.column)) {
clickOnPiece(i);
return;
}
}
clickOnEmptyCell(cell);
}

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

function getCursorPosition(e) {
var x;
var y;
if (e.pageX != undefined && e.pageY != undefined) {
x = e.pageX;
y = e.pageY;
}
else {
x = e.clientX + document.body.scrollLeft +
document.documentElement.scrollLeft;
y = e.clientY + document.body.scrollTop +
document.documentElement.scrollTop;
}

На данный момент у нас есть координаты x и y по отношению к документу (т.е., всей HTML-страницы). Это не совсем полезно. Мы хотим координаты относительно холста.

x -= gCanvasElement.offsetLeft;
y -= gCanvasElement.offsetTop;

Теперь у нас есть координаты x и y, которые относятся к холсту. То есть, если в этой точке х это 0 и у это 0, мы знаем, что пользователь просто щелкнул на верхний левый пиксел холста. Отсюда мы можем рассчитать, по какой клетке пользователь щелкнул, а затем действовать соответственно.

var cell = new Cell(Math.floor(y/kPieceHeight),
Math.floor(x/kPieceWidth));
return cell;
}

Вот так! События мыши трудны. Но вы можете использовать ту же логику во всех ваших собственных приложениях с холстом. Помните: щелчок мыши → координаты относительно документа → координаты относительно холста → код конкретного приложения.

Хорошо, давайте посмотрим на основные типовые задачи рисования. Потому что графика это так просто, что я решил очищать и перерисовывать доску в полном объеме каждый раз, когда что-нибудь изменяется в игре. Это не является строго обязательным. Контекст рисования холста будет сохранять все, что вы ранее нарисовали на нем, даже если пользователь прокручивает холст за пределы видимости или выбирает другую вкладку, а затем возвращается позже назад. Если вы разрабатываете приложение с более сложной графикой (например, аркадные игры), вы можете оптимизировать производительность путем отслеживания тех регионов холста, которые «загрязнились» и перерисовывать только их. Но это выходит за рамки данной книги.

gDrawingContext.beginPath();
/* вертикальные линии */
for (var x = 0; x <= kPixelWidth; x += kPieceWidth) {
gDrawingContext.moveTo(0.5 + x, 0);
gDrawingContext.lineTo(0.5 + x, kPixelHeight);
}
/* горизонтальные линии */
for (var y = 0; y <= kPixelHeight; y += kPieceHeight) {
gDrawingContext.moveTo(0, 0.5 + y);
gDrawingContext.lineTo(kPixelWidth, 0.5 +  y);
}
/* рисуем их! */
gDrawingContext.strokeStyle = "#ccc";
gDrawingContext.stroke();

Самое интересное начинается тогда, когда мы идем рисовать каждую фишку отдельно. Фишка представляет собой окружность, которую мы прежде не рисовали. Кроме того, если пользователь выберет фишку в ожидании перемещения, мы хотим нарисовать фишку как заполненную окружность. Здесь аргумент p соответствует фишке, которая имеет свойства строки и столбца, обозначающих текущее местоположение фишки на доске. Мы используем некоторые игровые константы (столбец, строка) и переводим их в относительные координаты холста (х, у), затем рисуем окружность, а затем (если фишка выбрана) заполняем окружность цветом.

function drawPiece(p, selected) {
var column = p.column;
var row = p.row;
var x = (column * kPieceWidth) + (kPieceWidth/2);
var y = (row * kPieceHeight) + (kPieceHeight/2);
var radius = (kPieceWidth/2) - (kPieceWidth/10);

Вот и вся специфичная игровая логика. Теперь у нас есть координаты (х, у) относительно холста для получения центра окружности, которую мы хотим нарисовать. В API Canvas нет метода circle(), но есть метод arc(). И действительно, что есть окружность, как не замкнутая дуга? Помните основы геометрии? Метод arc() задает центральную точку (х, у), радиус, начальный и конечный угол (в радианах) и флаг направления (false по часовой стрелке, true против часовой стрелки). Вы можете использовать модуль Math встроенный в JavaScript для расчета радиан.

gDrawingContext.beginPath();
gDrawingContext.arc(x, y, radius, 0, Math.PI * 2, false);
gDrawingContext.closePath();

Но подождите! Ничего еще не нарисовано. Метод arc() подобен moveTo() и lineTo() и относится к «карандашным» методам. Чтобы действительно нарисовать окружность, мы должны установить strokeStyle и вызвать метод stroke() для обводки «чернилами».

gDrawingContext.strokeStyle = "#000";
gDrawingContext.stroke();

Что если фишка выбрана? Мы можем повторно использовать тот же созданный контур, чтобы нарисовать границу фишки и заполнить окружность цветом.

if (selected) {
gDrawingContext.fillStyle = "#000";
gDrawingContext.fill();
}

Остальная часть программы это игровая логика – различия между правильными и неверными движениями, отслеживание количества ходов, определение, закончилась ли игра. С девятью окружностями, несколькими линиями и одним обработчиком onclick мы создали всю игру на <canvas>.

 

Полезные ссылки:

 

 

Автор: Николай Свирневский

Раздел: Web

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *