Марейн Хавербеке - Выразительный JavaScript
<canvas width="400" height="400"></canvas>
<script>
var cx = document.querySelector("canvas").getContext("2d");
var lastTime = null;
function frame(time) {
if (lastTime != null)
updateAnimation(Math.min(100, time - lastTime) / 1000);
lastTime = time;
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
function updateAnimation(step) {
// Ваш код
}
</script>
Предварительно рассчитанное отзеркаливание
Преобразования, к сожалению, замедляют рисование растровых изображений. Для векторной графики эффект не так заметен, потому что преобразованиям подвергаются всего лишь несколько точек, после чего рисование продолжается как обычно. Для растра позиция каждого пикселя должна быть преобразована, и хотя возможно, что браузеры в будущем будут делать это по-умному, это приводит к ненужному увеличению времени на отрисовку растра.
В нашей игре, где есть всего один преобразуемый спрайт, это не проблема. Но представьте, что вам надо рисовать сотни персонажей или тысячи вращающихся частиц от взрыва.
Подумайте, как можно было бы рисовать инвертированного персонажа без подгрузок дополнительных файлов и без постоянных преобразований вызовов drawImage.
17. HTTP
Мечта, ради которой создавалась Сеть – это общее информационное пространство, в котором мы общаемся, делясь информацией. Его универсальность является его неотъемлемой частью: ссылка в гипертексте может вести куда угодно, будь то персональная, локальная или глобальная информация, черновик или выверенный текст.
Тим Бернес-Ли, «Всемирная паутина: Очень короткая личная история»Протокол передачи гипертекста (Hypertext Transfer Protocol), уже упоминавшийся в главе 12, это механизм, посредством которого данные запрашиваются и предоставляются во Всемирной паутине. В этой главе более подробно описывается этот протокол и объясняется, каким образом JavaScript в браузере получает к нему доступ.
Протокол
Если в адресной строке браузера набрать eloquentjavascript.net/17_http.html, браузер сначала распознает адрес сервера, связанный с именем eloquentjavascript.net и попробует открыть TCP соединение по 80 порту – порт для HTTP по умолчанию. Если сервер существует и принимает соединение, браузер отправляет что-то вроде:
GET /17_http.html HTTP/1.1
Host: eloquentjavascript.net
User-Agent: Название браузера
Сервер отвечает по тому же соединению:
HTTP/1.1 200 OK
Content-Length: 65585
Content-Type: text/html
Last-Modified: Wed, 09 Apr 2014 10:48:09 GMT
<!doctype html>
... остаток документа
Браузер берёт ту часть, что идёт за ответом после пустой строки и показывает её в виде HTML-документа.
Информация, отправленная клиентом, называется запросом. Он начинается со строки:
GET /17_http.html HTTP/1.1
Первое слово – метод запроса. GET означает, что нам нужно получить определённый ресурс. Другие распространённые методы – DELETE для удаления, PUT для замещения и POST для отправки информации. Заметьте, что сервер не обязан выполнять каждый полученный запрос. Если вы выберете случайный сайт и скажете ему DELETE главную страницу – он, скорее всего, откажется.
Часть после названия метода – путь к ресурсу, к которому отправлен запрос. В простейшем случае, ресурс – просто файл на сервере, но протокол не ограничивается этой возможностью. Ресурс может быть чем угодно, что можно передать в качестве файла. Многие серверы создают ответы на лету. К примеру, если вы откроете twitter.com/marijnjh, сервер посмотрит в базе данных пользователя marijnjh, и если найдёт – создаст страницу профиля этого пользователя.
После пути к ресурсу первая строка запроса упоминает HTTP/1.1, чтобы сообщить о версии HTTP – протокола, которую она использует.
Ответ сервера также начинается с версии протокола, за которой идёт статус ответа – сначала код из трёх цифр, затем строчка.
HTTP/1.1 200 OK
Коды статуса, начинающиеся с 2, обозначают успешные запросы. Коды, начинающиеся с 4, означают, что что-то пошло не так. 404 – самый знаменитый статус HTTP, обозначающий, что запрошенный ресурс не найден. Коды, начинающиеся с 5, обозначают, что на сервере произошла ошибка, но не по вине запроса.
За первой строкой запроса или ответа может идти любое число строк заголовка. Это строки в виде “имя: значение”, которые обозначают дополнительную информацию о запросе или ответе. Эти заголовки были включены в пример:
Content-Length: 65585
Content-Type: text/html
Last-Modified: Wed, 09 Apr 2014 10:48:09 GMT
Тут определяется размер и тип документа, полученного в ответ. В данном случае это HTML-документ размером 65.585 байт. Также тут указано, когда документ был изменён последний раз.
По большей части клиент или сервер определяют, какие заголовки необходимо включать в запрос или ответ, хотя некоторые заголовки обязательны. К примеру, Host, обозначающий имя хоста, должен быть включён в запрос, потому что один сервер может обслуживать много имён хостов на одном ip-адресе, и без этого заголовка сервер не узнает, с каким хостом клиент пытается общаться.
После заголовков, как запрос, так и ответ могут указать пустую строку, за которой следует тело, содержащее передаваемые данные. Запросы GET и DELETE не пересылают дополнительных данных, а PUT и POST пересылают. Некоторые ответы, например, сообщения об ошибке, не требуют наличия тела.
Браузер и HTTP
Как мы видели в примере, браузер отправляет запрос, когда мы вводим URL в адресную строку. Когда в полученном HTML документе содержатся упоминания других файлов, такие, как картинки или файлы JavaScript, они тоже запрашиваются с сервера.
Веб-сайт средней руки легко может содержать от 10 до 200 ресурсов. Чтобы иметь возможность запросить их побыстрее, браузеры делают несколько запросов одновременно, а не ждут окончания запросов одного за другим. Такие документы всегда запрашиваются через запросы GET.
На страницах HTML могут быть формы, которые позволяют пользователям вписывать информацию и отправлять её на сервер. Вот пример формы:
<form method="GET" action="example/message.html">
Имя: <input type="text" name="name">
Сообщение:<br><textarea name="message"></textarea>
<button type="submit">Отправить </button>
</form>
Код описывает форму с двумя полями: маленькое запрашивает имя, а большое – сообщение. При нажатии кнопки «Отправить» информация из этих полей будет закодирована в строку запроса (query string). Когда атрибут method элемента равен GET, или когда он вообще не указан, строка запроса помещается в URL из поля action, и браузер делает GET-запрос с этим URL.
GET /example/message.html?name=Jean&message=Yes%3F HTTP/1.1
Начало строки запроса обозначено знаком вопроса. После этого идут пары имён и значений, соответствующие атрибуту name полей формы и содержимому этих полей. Амперсанд (&) используется для их разделения.
Сообщение, отправляемое в примере, содержит строку “Yes?”, хотя знак вопроса и заменён каким-то странным кодом. Некоторые символы в строке запроса нужно экранировать (escape). Знак вопроса в том числе, и он представляется кодом %3F. Есть какое-то неписаное правило, по которому у каждого формата должен быть способ экранировать символы. Это правило под названием кодирование URL использует процент, за которым идут две шестнадцатеричные цифры, которые представляют код символа. 3F в десятичной системе будет 63, и это код знака вопроса. У JavaScript есть функции encodeURIComponent и decodeURIComponent для кодирования и раскодирования.
console.log(encodeURIComponent("Hello & goodbye"));
// → Hello%20%26%20goodbye
console.log(decodeURIComponent("Hello%20%26%20goodbye"));
// → Hello & goodbye
Если мы поменяем атрибут method в форме в предыдущем примере на POST, запрос HTTP с отправкой формы пройдёт при помощи метода POST, который отправит строку запроса в теле запроса, вместо добавления её к URL.
POST /example/message.html HTTP/1.1
Content-length: 24
Content-type: application/x-www-form-urlencoded
name=Jean&message=Yes%3F
По соглашению метод GET используется для запросов, не имеющих побочных эффектов, таких, как поиск. Запросы, которые что-то меняют на сервере – создают новый аккаунт или размещают сообщение, должны отправляться методом POST. Клиентские программы типа браузера знают, что просто так делать запросы формата POST не нужно, и иногда незаметно для пользователя делают запросы GET – к примеру, чтобы загрузить заранее контент, который может вскоре понадобиться пользователю.