BackgroundFetchRegistration: progress event - Интерфейсы веб API

BackgroundFetchRegistration: progress event
===========================================

Limited availability
--------------------

This feature is not Baseline because it does not work in some of the most widely-used browsers.

* Learn more
* See full compatibility
* Report feedback
**Экспериментальная возможность:** **Это экспериментальная технология**
Так как спецификация этой технологии ещё не стабилизировалась, смотрите таблицу совместимости по поводу использования в различных браузерах. Также заметьте, что синтаксис и поведение экспериментальной технологии может измениться в будущих версиях браузеров, вслед за изменениями спецификации.

Событие **`progress`** интерфейса `BackgroundFetchRegistration` происходит когда выполняется связанные фоновый запрос.

Практически это событие происходит, когда любое из следующих свойств возвращает новое значение:

* `uploaded`,
* `downloaded`,
* `result`, или
* `failureReason`.
Синтаксис
---------

Используйте имя события в таких методах как `addEventListener()`, или установите свойство события `onprogress` в соответствующем объекте `BackgroundFetchRegistration`.

```js
addEventListener("progress", (event) => {});
onprogress = (event) => {};
```

Тип события
-----------

Общий `Event` без дополнительных параметров.

Пример
------

Следующий пример демонстрирует как вывести ход загрузки в консоль. Сначала код проверяет был ли предоставлен `downloadTotal`, когда фоновый запрос был зарегистрирован. The code first checks that a `downloadTotal` was provided when the background fetch was registered. Затем это значение используется для расчета процента на основе свойства `downloaded`.

```js
bgFetch.addEventListener("progress", () => {
  if (!bgFetch.downloadTotal) return;
  const percent = Math.round(
    (bgFetch.downloaded / bgFetch.downloadTotal) \* 100,
  );
  console.log(`Download progress: ${percent}%`);
});
```

Спецификации
------------

| Specification |
| --- |
| Background Fetch # background-fetch-registration-events |

Совместимость с браузерами
--------------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


BackgroundFetchRegistration.downloaded - Интерфейсы веб API

BackgroundFetchRegistration.downloaded
======================================

Limited availability
--------------------

This feature is not Baseline because it does not work in some of the most widely-used browsers.

* Learn more
* See full compatibility
* Report feedback
**Экспериментальная возможность:** **Это экспериментальная технология**
Так как спецификация этой технологии ещё не стабилизировалась, смотрите таблицу совместимости по поводу использования в различных браузерах. Также заметьте, что синтаксис и поведение экспериментальной технологии может измениться в будущих версиях браузеров, вслед за изменениями спецификации.

Доступное только для чтения свойство **`downloaded`** интерфейса `BackgroundFetchRegistration` возвращает количество скачанных байт, изначально `0`.

Когда значение свойства меняется, то у связанного объекта `BackgroundFetchRegistration` срабатывает событие progress.

Значение
--------

`number`.

Примеры
-------

Логирование этого свойства в консоль вернёт количество скачанных байт.

```js
console.log(bgFetch.downloaded);
```

Спецификации
------------

| Specification |
| --- |
| Background Fetch # dom-backgroundfetchregistration-downloaded |

Совместимость с браузерами
--------------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


BackgroundFetchRegistration.id - Интерфейсы веб API

BackgroundFetchRegistration.id
==============================

Limited availability
--------------------

This feature is not Baseline because it does not work in some of the most widely-used browsers.

* Learn more
* See full compatibility
* Report feedback
**Экспериментальная возможность:** **Это экспериментальная технология**
Так как спецификация этой технологии ещё не стабилизировалась, смотрите таблицу совместимости по поводу использования в различных браузерах. Также заметьте, что синтаксис и поведение экспериментальной технологии может измениться в будущих версиях браузеров, вслед за изменениями спецификации.

Доступное только для чтения свойство **`id`** интерфейса `BackgroundFetchRegistration` возвращает копию идентификатора запроса.

Значение
--------

Строка.

Примеры
-------

Вывод этого значения в консоль возвращает идентификатор, который был установлен при регистрации фонового запроса. В этом случает это `"my-fetch"`.

```js
console.log(bgFetch.id); // "my-fetch"
```

Спецификации
------------

| Specification |
| --- |
| Background Fetch # dom-backgroundfetchregistration-id |

Совместимость с браузерами
--------------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


BackgroundFetchRegistration.uploadTotal - Интерфейсы веб API

BackgroundFetchRegistration.uploadTotal
=======================================

Limited availability
--------------------

This feature is not Baseline because it does not work in some of the most widely-used browsers.

* Learn more
* See full compatibility
* Report feedback
**Экспериментальная возможность:** **Это экспериментальная технология**
Так как спецификация этой технологии ещё не стабилизировалась, смотрите таблицу совместимости по поводу использования в различных браузерах. Также заметьте, что синтаксис и поведение экспериментальной технологии может измениться в будущих версиях браузеров, вслед за изменениями спецификации.

Доступное только для чтения свойство **`uploadTotal`** интерфейса `BackgroundFetchRegistration` возвращает общее число байтов для отправки на сервер.

Значение
--------

`number`.

Примеры
-------

Вывод `BackgroundFetchRegistration.uploadTotal` в консоль возвращает uploadTotal.

```js
console.log(bgFetch.uploadTotal);
```

Спецификации
------------

| Specification |
| --- |
| Background Fetch # dom-backgroundfetchregistration-uploadtotal |

Совместимость с браузерами
--------------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


BackgroundFetchRegistration.matchAll() - Интерфейсы веб API

BackgroundFetchRegistration.matchAll()
======================================

Limited availability
--------------------

This feature is not Baseline because it does not work in some of the most widely-used browsers.

* Learn more
* See full compatibility
* Report feedback
**Экспериментальная возможность:** **Это экспериментальная технология**
Так как спецификация этой технологии ещё не стабилизировалась, смотрите таблицу совместимости по поводу использования в различных браузерах. Также заметьте, что синтаксис и поведение экспериментальной технологии может измениться в будущих версиях браузеров, вслед за изменениями спецификации.

Метод **`matchAll()`** интерфейса `BackgroundFetchRegistration` возвращает массив всех совпавших объектов `BackgroundFetchRecord`.

Синтаксис
---------

```js
matchAll(request);
matchAll(request, options);
```

### Параметры

`request`

`Request` для которого вы пытаетесь найти записи.
 Может быть объектом `Request` или URL.

`options` Необязательный

Объект, который устанавливает параметры для операции `match`. Доступные параметры:

`ignoreSearch`

 Булево значение, которое определяет нужно ли игнорировать строку поиска в URL.
 Например, если параметр установлен как `true`, то подстрока `?value=bar`, которая является частью
 `http://foo.com/?value=bar`, будет игнорироваться при выполении сопоставления.
 Значение по умолчанию - `false`.

`ignoreMethod`

 Булево значение. Если оно установлено как `true`, то
 операциям сопоставления запрещается проверять метод `http` объекта `Request`.
 Если `false` (значение по умолчанию) только `GET` и `HEAD` разрешены.

`ignoreVary`

 Булево значение. Когда `true` сигнализирует, что заголовок `VARY`
 должен быть проигнорирован.
 Значение по умолчанию - `false`.

### Возвращаемое значение

`Promise` результатом обработки которого будет массив всех совпавших объектов `BackgroundFetchRecord`.

### Exceptions

`InvalidStateError` `DOMException` (en-US)

Возвращается, если флаг `recordsAvailable` установлен как `false`, указывает на то, что запрос не выполняется.

Примеры
-------

Используйте `matchAll()` без параметров, чтобы вернуть все записи в фоновом запросе.

```js
const records = await bgFetch.matchAll();
console.log(records); // массив объектов BackgroundFetchRecord
```

Спецификации
------------

| Specification |
| --- |
| Background Fetch # background-fetch-registration-match-all |

Совместимость с браузерами
--------------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


BackgroundFetchRegistration.failureReason - Интерфейсы веб API

BackgroundFetchRegistration.failureReason
=========================================

Limited availability
--------------------

This feature is not Baseline because it does not work in some of the most widely-used browsers.

* Learn more
* See full compatibility
* Report feedback
**Экспериментальная возможность:** **Это экспериментальная технология**
Так как спецификация этой технологии ещё не стабилизировалась, смотрите таблицу совместимости по поводу использования в различных браузерах. Также заметьте, что синтаксис и поведение экспериментальной технологии может измениться в будущих версиях браузеров, вслед за изменениями спецификации.

Доступное только для чтения свойство **`failureReason`** интерфейса `BackgroundFetchRegistration` возвращает строку, значение которой указывает на причину неудачного выполнения фонового запроса.

Когда значение свойства меняется, то у связанного объекта `BackgroundFetchRegistration` срабатывает событие progress.

Значение
--------

Может принимать одну из следующих строк:

`""`

Фоновый запрос не завершён, либо завершён успешно.

`"aborted"`

Операция была прервана пользователем, либо был вызван `abort()`.

`"bad-status"`

Статус ответа не-ok (статус находится за пределами диапазона 200-299).

`"fetch-error"`

Запрос провалился по другим причинам, например CORS, или ошибки сети.

`"quota-exceeded"`

Во время операции была достигнута квота хранения.

`"download-total-exceeded"`

Было превышено указанное значение `downloadTotal`. Это значение было установлено при регистрациии фонового запроса.

Примеры
-------

Логирование этого свойства в консоль печатает причину завершения запроса с ошибкой, или пустую строку, если запрос был завершён успешно, либо ещё не завершён.

```js
console.log(bgFetch.failureReason);
```

Спецификации
------------

| Specification |
| --- |
| Background Fetch # dom-backgroundfetchregistration-failurereason |

Совместимость с браузерами
--------------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


BackgroundFetchRegistration.uploaded - Интерфейсы веб API

BackgroundFetchRegistration.uploaded
====================================

Limited availability
--------------------

This feature is not Baseline because it does not work in some of the most widely-used browsers.

* Learn more
* See full compatibility
* Report feedback
**Экспериментальная возможность:** **Это экспериментальная технология**
Так как спецификация этой технологии ещё не стабилизировалась, смотрите таблицу совместимости по поводу использования в различных браузерах. Также заметьте, что синтаксис и поведение экспериментальной технологии может измениться в будущих версиях браузеров, вслед за изменениями спецификации.

Доступное только для чтения свойство **`uploaded`** интерфейса `BackgroundFetchRegistration` возвращает количество успешно переданных байт, изначально `0`.

Если значение этого свойства изменилось, то в связанном объекте `BackgroundFetchRegistration` происходит событие progress.

Значение
--------

`number`.

Примеры
-------

Вывод этого свойства в консоль возвращает число загруженных байт.

```js
console.log(bgFetch.uploaded);
```

Спецификации
------------

| Specification |
| --- |
| Background Fetch # dom-backgroundfetchregistration-uploaded |

Совместимость с браузерами
--------------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


BackgroundFetchRegistration.abort() - Интерфейсы веб API

BackgroundFetchRegistration.abort()
===================================

Limited availability
--------------------

This feature is not Baseline because it does not work in some of the most widely-used browsers.

* Learn more
* See full compatibility
* Report feedback
**Экспериментальная возможность:** **Это экспериментальная технология**
Так как спецификация этой технологии ещё не стабилизировалась, смотрите таблицу совместимости по поводу использования в различных браузерах. Также заметьте, что синтаксис и поведение экспериментальной технологии может измениться в будущих версиях браузеров, вслед за изменениями спецификации.

Метод **`abort()`** интерфейса `BackgroundFetchRegistration` прерывает активный фоновый запрос.

Синтаксис
---------

```js
abort();
```

### Параметры

Нет.

### Возвращаемое значение

`Promise`, результатом обработки которого будет `true`, если запрос успешно прерван.

Примеры
-------

Используйте `abort()`, чтобы прервать фоновый запрос в процессе работы.

```js
bgFetch.abort();
```

Спецификации
------------

| Specification |
| --- |
| Background Fetch # background-fetch-registration-abort |

Совместимость с браузерами
--------------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


BackgroundFetchRegistration.downloadTotal - Интерфейсы веб API

BackgroundFetchRegistration.downloadTotal
=========================================

Limited availability
--------------------

This feature is not Baseline because it does not work in some of the most widely-used browsers.

* Learn more
* See full compatibility
* Report feedback
**Экспериментальная возможность:** **Это экспериментальная технология**
Так как спецификация этой технологии ещё не стабилизировалась, смотрите таблицу совместимости по поводу использования в различных браузерах. Также заметьте, что синтаксис и поведение экспериментальной технологии может измениться в будущих версиях браузеров, вслед за изменениями спецификации.

Доступное только для чтения свойство **`downloadTotal`** интерфейса `BackgroundFetchRegistration` возвращает общий размер загрузки в байтах. Это значение устанавливается при регистрации запроса, или `0` если не установлено.

Значение
--------

`number`.

Примеры
-------

Логирование этого свойства в консоль возвращает общий размер загрузки в байтах.

```js
console.log(bgFetch.downloadTotal);
```

Спецификации
------------

| Specification |
| --- |
| Background Fetch # dom-backgroundfetchregistration-downloadtotal |

Совместимость с браузерами
--------------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


BackgroundFetchRegistration.match() - Интерфейсы веб API

BackgroundFetchRegistration.match()
===================================

Limited availability
--------------------

This feature is not Baseline because it does not work in some of the most widely-used browsers.

* Learn more
* See full compatibility
* Report feedback
**Экспериментальная возможность:** **Это экспериментальная технология**
Так как спецификация этой технологии ещё не стабилизировалась, смотрите таблицу совместимости по поводу использования в различных браузерах. Также заметьте, что синтаксис и поведение экспериментальной технологии может измениться в будущих версиях браузеров, вслед за изменениями спецификации.

Метод **`match()`** интерфейса `BackgroundFetchRegistration` возвращает первое совпадение `BackgroundFetchRecord`.

Синтаксис
---------

```js
match(request);
match(request, options);
```

### Параметры

`request`

`Request` для которого вы пытаетесь найти записи.
 Может быть объектом `Request` или URL.

`options` Необязательный

Объект, который устанавливает параметры для операции `match`. Доступные параметры:

`ignoreSearch`

 Булево значение, которое определяет нужно ли игнорировать строку поиска в URL.
 Например, если параметр установлен как `true`, то подстрока `?value=bar`, которая является частью
 `http://foo.com/?value=bar`, будет игнорироваться при выполении сопоставления.
 Значение по умолчанию - `false`.

`ignoreMethod`

 Булево значение. Если оно установлено как `true`, то
 операциям сопоставления запрещается проверять метод `http` объекта `Request`.
 Если `false` (значение по умолчанию) только `GET` и `HEAD` разрешены.

`ignoreVary`

 Булево значение. Когда `true` сигнализирует, что заголовок `VARY`
 должен быть проигнорирован.
 Значение по умолчанию - `false`.

### Возвращаемое значение

`Promise` результатом обработки которого будет первый `BackgroundFetchRecord`, который соответствует запросу
 или `undefined`, если соответствий не найдено.

>
>
> **Note:** `BackgroundFetchRegistration.match()` в основном идентичен
>  `BackgroundFetchRegistration.matchAll()`, за исключением того, что вместо возвращения массива
>  всех совпадений, он возвращает только первое совпадение.
>
>
>
>

### Исключения

`InvalidStateError` `DOMException` (en-US)

Возвращается при вызове `match()` в тот момент, когда нет запросов в процессе выполнения. Это состояние будет отражено установкой флага `BackgroundFetchRegistration.recordsAvailable` как `false`.

Примеры
-------

В этом примере мы ищем запись с URL "/ep-5.mp3". Если `BackgroundFetchRecord` найдена, то мы можем вернуть некоторую информацию ней.

```js
bgFetch.match("/ep-5.mp3").then(async (record) => {
  if (!record) {
    console.log("Запись не найдена");
    return;
  }

  console.log(`Запрос`, record.request);
  const response = await record.responseReady;
  console.log(`Ответ`, response);
});
```

Спецификации
------------

| Specification |
| --- |
| Background Fetch # background-fetch-registration-match |

Совместимость с браузерами
--------------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


BackgroundFetchEvent.registration - Интерфейсы веб API

BackgroundFetchEvent.registration
=================================

Limited availability
--------------------

This feature is not Baseline because it does not work in some of the most widely-used browsers.

* Learn more
* See full compatibility
* Report feedback
**Экспериментальная возможность:** **Это экспериментальная технология**
Так как спецификация этой технологии ещё не стабилизировалась, смотрите таблицу совместимости по поводу использования в различных браузерах. Также заметьте, что синтаксис и поведение экспериментальной технологии может измениться в будущих версиях браузеров, вслед за изменениями спецификации.

Доступное только для чтения свойство **`registration`** интерфейса `BackgroundFetchEvent` возвращает объект `BackgroundFetchRegistration`.

Значение
--------

`BackgroundFetchRegistration`.

Примеры
-------

В этом примере, если пользователь кликнет по элементу, отображающем ход загрузки, то это действие вызовет событие `onbackgroundfetchclick`. Текущий `BackgroundFetchRegistration` возвращается вызовом `event.registration`.

```js
addEventListener("backgroundfetchclick", (event) => {
  console.log(event.registration);
});
```

Спецификации
------------

| Specification |
| --- |
| Background Fetch # dom-backgroundfetchevent-registration |

Совместимость с браузерами
--------------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


BackgroundFetchEvent() - Интерфейсы веб API

BackgroundFetchEvent()
======================

Limited availability
--------------------

This feature is not Baseline because it does not work in some of the most widely-used browsers.

* Learn more
* See full compatibility
* Report feedback
**Экспериментальная возможность:** **Это экспериментальная технология**
Так как спецификация этой технологии ещё не стабилизировалась, смотрите таблицу совместимости по поводу использования в различных браузерах. Также заметьте, что синтаксис и поведение экспериментальной технологии может измениться в будущих версиях браузеров, вслед за изменениями спецификации.

Конструктор **`BackgroundFetchEvent()`** создаёт новый объект `BackgroundFetchEvent`. Этот конструктор обычно не используется, так как браузер создаёт такие объекты самостоятельно и, затем, передаёт их для колбэков событий фоновых запросов.

Синтаксис
---------

```js
new BackgroundFetchEvent(type, options);
```

### Параметры

`type`

 Строка с именем события.
 Он чувствителен к регистру, и браузеры устанавливают его как `backgroundfetchabort` или `backgroundfetchclick`.

`options`

Объект, который, *в дополнение к свойствам, определённым в `ExtendableEvent()` (en-US)*, имеет следующие свойства:

`registration`

Объект `BackgroundFetchRegistration`.

### Возвращаемое значение

Новый объект `BackgroundFetchEvent`.

Спецификации
------------

| Specification |
| --- |
| Background Fetch # dom-backgroundfetchevent-backgroundfetchevent |

Совместимость с браузерами
--------------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


URL() - Интерфейсы веб API

URL()
=====

Конструктор **`URL()`** возвращает вновь созданный `URL` объект, отражающий URL, определяемый параметрами.

Если предоставленный базовый URL-адрес или итоговый URL-адрес не является валидным, то будет сгенерировано JavaScript исключение `TypeError`.

**Примечание:** Эта возможность доступна в Web Workers

Синтаксис
---------

```js
const url = new URL(url [, base])
```

### Параметры

`url`

Строка типа `USVString` (en-US) или любой объект, который может быть преобразован в строку (en-US), включая, например, элементы `<a>` и `<area>`, представляющие абсолютный и относительный URL-адрес соответственно. Если `url` это относительный URL-адрес, то параметр `base` становится обязательным, так как он будет использован в качестве базового URL-адреса. Если же `url` — абсолютный URL-адрес, то значение параметра `base` будет проигнорировано.

`base` Необязательный

Строка типа `USVString` (en-US). Используется в качестве базового адреса, когда `url` это относительный URL-адрес. Значение по умолчанию: `undefined`.

>
>
> **Note:** Как и в случае с любым другим Web API,
>  которое принимает на вход строку типа `USVString` (en-US),
>  аргументы `url` и `base` будут преобразованы в строку
>  независимо от исходного типа переданного значения.
>  В частности, вы можете использовать существующий `URL` объект в качестве аргумента
>  и он будет преобразован в строку значение которой равно значению свойства
>  `href` (en-US) переданного объекта.
>
>
>
>

### Исключения

| Исключение | Объяснение |
| --- | --- |
| `TypeError` | `url` (в случае с абсолютным URL-адресом) или `base` + `url` (в случае с относительным URL-адресом) является невалидным URL-адресом. |

Примеры
-------

```js
// Базовые URL-адреса
let m = "https://developer.mozilla.org";
let a = new URL("/", m); // => 'https://developer.mozilla.org/'
let b = new URL(m); // => 'https://developer.mozilla.org/'

new URL("en-US/docs", b); // => 'https://developer.mozilla.org/en-US/docs'
let d = new URL("/en-US/docs", b); // => 'https://developer.mozilla.org/en-US/docs'
new URL("/en-US/docs", d); // => 'https://developer.mozilla.org/en-US/docs'
new URL("/en-US/docs", a); // => 'https://developer.mozilla.org/en-US/docs'

new URL("/en-US/docs", "https://developer.mozilla.org/fr-FR/toto");
// => 'https://developer.mozilla.org/en-US/docs'

new URL("/en-US/docs", ""); // Генерирует TypeError исключение так как '' не является валидным URL-адресом
new URL("/en-US/docs"); // Генерирует TypeError исключение так как '/en-US/docs' не является валидным URL-адресом
new URL("http://www.example.com"); // => 'http://www.example.com/'
new URL("http://www.example.com", b); // => 'http://www.example.com/'

new URL("//foo.com", "https://example.com"); // => 'https://foo.com' (см. относительные URL-адреса)
```

Спецификации
------------

| Specification |
| --- |
| URL Standard # dom-url-url |

Совместимость с браузерами
--------------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

Смотрите также
--------------

* Полифил для `URL` доступен в core-js
* Этот интерфейс принадлежит: `URL`.


URL.createObjectURL() - Интерфейсы веб API

URL.createObjectURL()
=====================

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

Определение
-----------

**`URL.createObjectURL()`** - статический метод, который создаёт `DOMString`, содержащий URL с указанием на объект, заданный как параметр. Время жизни URL связано с `document` окна в котором он был создан. Новый URL объект может представлять собой `File` объект или `Blob` объект.

**Примечание:** Эта возможность доступна в Web Workers

Синтаксис
---------
```

objectURL = URL.createObjectURL(blob);
```

Параметры
---------

*blob*

Объект типа `File` или `Blob` для которого создаётся URL объект.

Пример
------

Смотрите Using object URLs to display images.

Примечания
----------

При каждом вызове `createObjectURL()`, создаётся новый URL объект, даже если он уже был ранее создан для того же самого объекта. Память для каждого из них нужно освобождать с помощью `URL.revokeObjectURL()` (en-US), в том случае, если они больше не нужны. Браузер сделает это автоматически с выгрузкой объекта, тем не менее в целях оптимизации производительности и использования памяти, следует принудительно выгружать их по возможности.

Спецификации
------------

| Specification |
| --- |
| File API # dfn-createObjectURL |

Совместимость с браузерами
--------------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

Смотрите также
--------------

* `URL.revokeObjectURL()` (en-US)
* Using files from web applications


URL.hash - Интерфейсы веб API

URL.hash
========

 Свойство **`hash`** интерфейса `URL` — это строка типа `USVString` (en-US),
 содержащая символ `'#'`, после которого идет идентификатор фрагмента URL-адреса.

 Фрагмент не кодируется (en-US). Если URL-адрес не имеет
 идентификатора фрагмента, то данное свойство содержит пустую строку — `""`.

**Примечание:** Эта возможность доступна в Web Workers

Синтаксис
---------

```js
const string = url.hash;
url.hash = newHash;
```

### Значение

Строка типа `USVString` (en-US).

Примеры
-------

```js
const url = new URL(
  "https://developer.mozilla.org/ru/docs/Web/API/URL/href#Examples",
);
console.log(url.hash); // Выведет: '#Examples'
```

Спецификации
------------

| Specification |
| --- |
| URL Standard # dom-url-hash |

Совместимость с браузерами
--------------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

Смотрите также
--------------

* Интерфейс `URL`, которому принадлежит это свойство.


Использование событий касания - Интерфейсы веб API

Использование событий касания
=============================

На сегодняшний день бо́льшая часть содержимого в вебе рассчитана на работу с использованием клавиатуры и мыши. Тем не менее, всё больше появляется устройств с сенсорным экраном (особенно мобильных) и веб-приложения могут либо напрямую обрабатывать сенсорный ввод с помощью `Событий касаний`, либо интерпретировать события мыши. Недостатком использования событий мыши является то, что они не поддерживают одновременный ввод, тогда как события касаний поддерживают несколько одновременных прикосновений (возможно в разных местах сенсорной поверхности), повышая тем самым удобство работы с интерфейсом.

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

Интерфейсы
----------

События касаний включают три интерфейса `Touch`, `TouchEvent` и `TouchList`, а также следующие типы событий

* `touchstart (en-US)` - вызывается в момент касания сенсорной поверхности
* `touchmove (en-US)` - вызывается при перемещении по сенсорной поверхности
* `touchend (en-US)` - вызывается, если убрали палец
* `touchcancel (en-US)` - вызывается в момент прерывания события (например, создано слишком много точек касания).

Интерфейс `Touch` представляет собой одну точка контакта с сенсорной поверхностью. Точка контакта обычно называется точкой касания или просто касанием. Касание обычно генерируется пальцем или стилусом на сенсорной поверхности. Свойства точки касания включают уникальный идентификатор, целевой элемент точки касания, а также координаты X и Y точки касания относительно области видимости, страницы или экрана.

Интерфейс `TouchList` представляет список точек контакта с сенсорной поверхностью, по одной точке касания на каждый контакт с поверхностью. Таким образом, если пользователь активировал сенсорную поверхность одним пальцем, список будет содержать один элемент, а если пользователь прикоснулся к поверхности тремя пальцами, в списке будет три элемента.

Интерфейс `TouchEvent` представляет событие, которое отправляется, когда меняется состояние контактов с сенсорной поверхностью. Изменения состояния включают начальный контакт с сенсорной поверхностью, перемещение точки касания при сохранении контакта с поверхностью, отпускание точки касания и прерывание события касания. Атрибуты интерфейса включают состояние нескольких клавиш-модификаторов (например, клавиши `Shift`) и следующий список данных:

* `touches` - список всех точек касания, находящихся в данный момент на экране
* `targetTouches` (en-US) - список точек касания на целевом DOM-элементе
* `changedTouches` - список точек касания, элементы которых зависят от типа связанного события:
    + Для события `touchstart (en-US)` - список точек касания, которые стали активными во время события
    + Для события `touchmove (en-US)` - список точек касания, которые изменились с последнего события
    + Для события `touchend (en-US)` - список точек касания, которые были удалены с поверхности (то есть, набор точек касания, соответствующих пальцам, которые больше не касаются поверхности)

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

От интерфейсов к жестам
-----------------------

При определении семантики жеста, приложение может учитывать различные факторы. Например, расстояние, которое прошла точка от начала до конца касания. Другим возможным фактором может быть время, например, количество времени между началом касания и завершением, или количество времени между двумя последовательными касаниями, предназначенными для создания жеста двойного нажатия. Направленность свайпа (например, слева направо, справа налево и т.д) является ещё одним фактором.

Списки касаний, которые использует приложение, зависят от семантики его жестов. Например, если приложение поддерживает однократное касание (тап) одного элемента, будет использоваться список `targetTouches` (en-US) в обработчике события `touchstart (en-US)` для обработки точки касания в свойственной приложению манере. Если приложение поддерживает свайп двумя пальцами для любых двух точек касания, оно будет использовать список `changedTouches` в обработчике `touchmove (en-US)` для того, чтобы определить, были ли перемещены эти две точки касания, а затем реализовать семантику этого жеста в свойственной приложению манере.

Когда есть только одна активная точка касания, браузер обычно отправляет эмулированные события и мыши и клика. Мультитач-действия, включающие две и больше активных точек касания, обычно генерируют только события касания. Чтобы предотвратить отправку эмулированных событий мыши, используйте метод `preventDefault()` в обработчиках событий касания. Дополнительную информацию о взаимодействии между событиями мыши и событиями касаний можно найти в статье `Поддержка TouchEvent и MouseEvent` (en-US).

Основные шаги
-------------

Этот раздел содержит пример базового использования приведённых выше интерфейсов. Более подробный пример можно найти в статье `События касаний (тач-события)`.

Назначьте обработчик событий для каждого типа события касания.

```js
// Назначение обработчика событий касания
someElement.addEventListener("touchstart", process_touchstart, false);
someElement.addEventListener("touchmove", process_touchmove, false);
someElement.addEventListener("touchcancel", process_touchcancel, false);
someElement.addEventListener("touchend", process_touchend, false);
```

Обработчик события, реализующий семантику жестов приложения
```

// Обработчик touchstart
function process_touchstart(ev) {
  // Используется данные события, чтобы вызвать соответствующие обработчики жестов
  switch (ev.touches.length) {
    case 1: handle_one_touch(ev); break;
    case 2: handle_two_touches(ev); break;
    case 3: handle_three_touches(ev); break;
    default: gesture_not_supported(ev); break;
  }
}
```

Доступ к атрибутам точки касания.

```js
// Создание обработчика события "touchstart"
someElement.addEventListener(
  "touchstart",
  function (ev) {
    // Перебор точек события, которые были активированы
    // для этого элемента и обработка каждого целевого элемента события
    for (var i = 0; i < ev.targetTouches.length; i++) {
      process\_target(ev.targetTouches[i].target);
    }
  },
  false,
);
```

Предотвращение эмуляции событий мыши

```js
// Обработчик события "touchmove"
function process\_touchmove(ev) {
  // Вызов "preventDefault()"
  ev.preventDefault();
}
```

Лучшие практики
---------------

Вот несколько моментов, которых следует придерживаться при работе с событиями касаний:

* Сводите к минимуму объём работы, выполняемой в обработчиках касаний
* Добавляйте обработчики точек касаний к определённому целевому элементу, а не ко всему документу или узлам, расположенным выше в дереве
* Добавляйте обработчики событий `touchmove (en-US)`, `touchend (en-US)` и `touchcancel (en-US)` внутрь `touchstart (en-US)`.
* Целевой элемент или узел для события касания должен быть достаточно большим, для касания пальцем. Если целевая область слишком мала, прикосновений к ней может привести к вызову событий для других прилегающих элементов.
Информация о совместимости с браузерами
---------------------------------------

Согласно данным о совместимости с браузерами, поддержка событий касания среди мобильных браузеров относительно неплохая. Десктопные браузеры немного отстают, хотя над этим уже ведётся работа.

Некоторые новые функции, связанные с областью касания (областью контакта между пользователем и сенсорной поверхностью) - находятся в процессе стандартизации. Новый функционал включает в себя радиусы эллипса по осям X и Y, которые наиболее точно описывают область контакта с сенсорной поверхностью. *Угол поворота* точки касания - градус, на который нужно повернуть упомянутый эллипс, чтобы соответствовать области контакта - также стандартизирован, как и степень давления в точке касания.

А что насчёт Событий Указателя?
-------------------------------

Внедрение новых механизмов ввода усложняет обработку всех возможных событий, таких как события клавиатуры, мыши, пера, касаний. Чтобы помочь решить эту проблему, стандарт "Pointer Events" определяет события и связанные с ними интерфейсы для обработки ввода с таких указывающих устройств, как мышь, перо, сенсорный экран и т.д. То есть, абстрактный *указатель* создаёт унифицированную модель ввода, которая может представлять точку контакта пальца, пера/стилуса или мыши. Подробнее в MDN-статье События указателя.

Модель событий указателя может упростить обработку разных способов ввода, поскольку указатель представляет ввод с любого устройства. В дополнение к этому, типы событий указателя очень похожи на типы событий мыши (например, `pointerdown` `pointerup`), таким образом, код для обработки событий указателя полностью совпадает с кодом обработки ввода с помощью мыши.

Статус реализации событий указателя в браузерах относительно высок, поскольку Chrome, Firefox, IE11 и Edge имеют полные реализации.

Примеры
-------

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

* `События касаний`
* Реализация кастомных жестов
* Введение в события касаний в JavaScript
* Добавление поддержки сенсорных экранов на сайта (простой способ)

Демонстрация событий касания:

* Paint Program (by Rick Byers)
* Touch/pointer tests and demos (by Patrick H. Lauke)
Сообщество
----------

* Группа сообщества Событий касания
* Mail list
* W3C #touchevents IRC channel
Связанные темы и источники
--------------------------

* Стандарт "Pointer Events"


Использование Web Storage API - Интерфейсы веб API

Использование Web Storage API
=============================

Web Storage API предоставляет механизм, при помощи которого браузер может безопасно хранить пары ключей/значений в намного более интуитивной форме, чем используя cookies. Эта статья предоставляет пошаговое руководство о том, как использовать эту простую технологию.

Основные концепции
------------------

Storage объекты простые хранилища вида ключ-значение, похожие чем-то на объекты, но они остаются неизменными при загрузке страницы. Ключи и значения всегда являются строками (обратите внимание, что числовые ключи будут автоматически конвертироваться в строку, точно также как объекты). Вы можете получить доступ к этим значениям как в объектах, или getItem() и setItem() методами. Все три строки ниже устанавливают одинаковое значение в colorSetting:
```

localStorage.colorSetting = '#a4509b';
localStorage['colorSetting'] = '#a4509b';
localStorage.setItem('colorSetting', '#a4509b');
```

В основе Веб хранилища лежат два механизма:

* `(sessionStorage)` обслуживает область хранения данных для каждого домена, доступное на протяжении сессии. (пока браузер открыт, даже в случае перезагрузки страницы)
* `(localStorage)` делает то же самое, но сохраняет данные даже в случае, если переоткрыть браузер.

Оба механизма доступны через `Window.sessionStorage` и `Window.localStorage` свойства (если быть более точным, в броузерах, поддерживающих хранилища объект `Window` выполняет объекты `WindowLocalStorage` и `WindowSessionStorage`, которые содержат свойства `localStorage` и `sessionStorage`) — вызов одного из них создаёт экземпляр объекта Storage, через который можно устанавливать, редактировать и удалять данные. Разные Storage объекты будут использовать `sessionStorage` и `localStorage поэтому они используются и управляются раздельно`

Так, например, изначально вызов `localStorage` в документе возвращает `Storage` объект; вызов `sessionStorage` в документе возвращает другой `Storage` объект. Оба объекта могут управляться одинаково, но отдельно.

Функция обнаружения localStorage
--------------------------------

Чтобы использовать localStorage, мы должны сперва проверить, что localStorage поддерживается и доступно в текущем браузере.

### Проверка на наличие

Браузеры поддерживаемые localStorage будут иметь свойство localStorage объекта window. Тем не менее по различным причинам, простое утверждение, что это свойство существует, может вызывать исключение. Если localStorage существует это ещё не даёт гарантии, что оно доступно, т.к. различные броузеры обладают настройками которые отключают его. Поэтому браузер может поддерживать localStorage, но не делает его доступным для скриптов на странице. Один из таких примеров браузер Safari, который в Private Browsing mode возвращает нам пустой localStorage объект, фактически делая его непригодным для использования . Наша функция должна принимать во внимание этот сценарий.

Функция, которая проверяет браузеры на поддержку и доступность localStorage:

```js
function storageAvailable(type) {
  try {
    var storage = window[type],
      x = "\_\_storage\_test\_\_";
    storage.setItem(x, x);
    storage.removeItem(x);
    return true;
  } catch (e) {
    return false;
  }
}
```

Вот как вы бы могли использовать это:

```js
if (storageAvailable("localStorage")) {
  // Yippee! We can use localStorage awesomeness
} else {
  // Too bad, no localStorage for us
}
```

Вы можете протестировать sessionStorage вместо этого используйте `storageAvailable('sessionStorage')` Смотрите здесь краткую историю функции-обнаружения localStorage

Пример
------

Чтобы проиллюстрировать типичное использование Web storage, мы создали простой пример, назвав его **Web Storage Demo.** На целевой странице представлены элементы управления, которые можно использовать для настройки цвета, шрифта и декоративного изображения:

![](/ru/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API/landing.png)Когда вы выбираете различные опции, страница немедленно перезагружается; в дополнение, ваш выбор сохраняется в localStorage, таким образом когда вы покидаете страницу и загружаете её снова спустя некоторое время, ваши параметры сохраняются.

Мы также предоставили страницу вывода событий - если вы загрузите эту страницу в другой вкладке, затем сделаете некоторые изменения в landing page, вы увидите обновлённую информацию о хранилище.

![](/ru/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API/event-output.png)

**Примечание:** Помимо просмотра примеров выше, используя приведённые ссылки выше , вы можете также просмотреть исходный код.

Проверка на заполненность хранилища
-----------------------------------

Начнём с main.js, мы проверим был ли уже заполнен объект Storage (т.е страница была ранее доступна)

```js
if (!localStorage.getItem("bgcolor")) {
  populateStorage();
} else {
  setStyles();
}
```

 Метод `Storage.getItem()` используется для получения данных из storage; в этом примере мы проверяем, чтобы увидеть существует ли bgcolor; если нет, мы запускаем `populateStorage(), чтобы добавить значение по умолчанию в storage. Если значения уже есть там, мы запускаем setStyles(), для обновления стиля страница с сохранёнными значениями.`
**Примечание**: вы можете также использовать `Storage.length` для проверки storage object.

Получение данных из Storage
---------------------------

Как было отмечено выше, значения хранилища могут быть извлечены используя `Storage.getItem()`. В качестве аргумента функция принимает значение ключа элемента хранилища, а возвращает значение этого элемента. Например:

```js
function setStyles() {
  var currentColor = localStorage.getItem("bgcolor");
  var currentFont = localStorage.getItem("font");
  var currentImage = localStorage.getItem("image");

  document.getElementById("bgcolor").value = currentColor;
  document.getElementById("font").value = currentFont;
  document.getElementById("image").value = currentImage;

  htmlElem.style.backgroundColor = "#" + currentColor;
  pElem.style.fontFamily = currentFont;
  imgElem.setAttribute("src", currentImage);
}
```

Первые три строки извлекают значения элементов локального хранилища. Next, we set the values displayed in the form elements to those values, so that they keep in sync when you reload the page. Finally, we update the styles/decorative image on the page, so your customization options come up again on reload.

Setting values in storage
-------------------------

`Storage.setItem()` is used both to create new data items, and (if the data item already exists) update existing values. This takes two arguments — the key of the data item to create/modify, and the value to store in it.

```js
function populateStorage() {
  localStorage.setItem("bgcolor", document.getElementById("bgcolor").value);
  localStorage.setItem("font", document.getElementById("font").value);
  localStorage.setItem("image", document.getElementById("image").value);

  setStyles();
}
```

The `populateStorage()` function sets three items in local storage — the background color, font, and image path. It then runs the `setStyles()` function to update the page styles, etc.

We've also included an `onchange` handler on each form element, so that the data and styling is updated whenever a form value is changed:

```js
bgcolorForm.onchange = populateStorage;
fontForm.onchange = populateStorage;
imageForm.onchange = populateStorage;
```

Responding to storage changes with the StorageEvent
---------------------------------------------------

The `StorageEvent` (en-US) is fired whenever a change is made to the `Storage` object. This won't work on the same page that is making the changes — it is really a way for other pages on the domain using the storage to sync any changes that are made. Pages on other domains can't access the same storage objects.

On the events page (see events.js) the only JavaScript is as follows:

```js
window.addEventListener("storage", function (e) {
  document.querySelector(".my-key").textContent = e.key;
  document.querySelector(".my-old").textContent = e.oldValue;
  document.querySelector(".my-new").textContent = e.newValue;
  document.querySelector(".my-url").textContent = e.url;
  document.querySelector(".my-storage").textContent = e.storageArea;
});
```

Here we add an event listener to the `window` object that fires when the `Storage` object associated with the current origin is changed. As you can see above, the event object associated with this event has a number of properties containing useful information — the key of the data that changed, the old value before the change, the new value after that change, the URL of the document that changed the storage, and the storage object itself.

Удаление записанных данных
--------------------------

Web Storage также предоставляет несколько простых методов для удаления данных. Мы не используем эти методы в нашем дёма, но они очень простые, чтобы добавить их в проект:

* `Storage.removeItem()` принимает единственный аргумент - ключ элемента данных, который вы хотите удалить - и удаляет его из объекта хранения для этого домена.
* `Storage.clear()` не принимает аргументов, полностью очищает объекта storage для данного домена.
Specifications
--------------

| Specification |
| --- |
| HTML Standard # dom-localstorage-dev |
| HTML Standard # dom-sessionstorage-dev |

Совместимость с браузерами
--------------------------

### api.Window.localStorage

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

### api.Window.sessionStorage

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

See also
--------

* Web Storage API landing page


WebGL tutorial - Интерфейсы веб API

WebGL tutorial
==============

WebGL позволяет использовать API, основанную на OpenGL ES 2.0 для осуществления 3D рендеринга в HTML `<canvas>` в браузерах, которые его поддерживают без использования плагинов. WebGL программы состоят из программ, написанных на JavaScript, и специального кода шейдеров, который выполняется на видеокарте. WebGL элементы могут использоваться с HTML-элементами и скомпонованы с другими частями страницы или фона страницы.

В этом руководстве с нуля рассказывается о том, как использовать элемент `<canvas>` для отрисовки WebGL. Предоставленные примеры помогут вам понять, как можно использовать WebGL, также вы сможете использовать код из примеров как сниппеты для ваших разработок.

Перед тем как начать
--------------------

Использование элемента `<canvas>` - это не сложно, но вы должны понимать основы HTML и JavaScript. `<canvas>` и WebGL не поддерживаются в некоторых старых браузерах, но поддерживаются в последних версиях всех основных. Чтобы рисовать графику на canvas нужно использовать объекты контекста JavaScript, который создаёт графику на лету.

В этом руководстве
------------------

Начало работы с WebGL

Как настроить WebGL контекст.

Добавление 2D контекста в WebGL контекст

Как визуализировать простые плоские фигуры используя WebGL.

Использование шейдеров для добавления цвета в WebGL

Демонстрация того, как добавить цвет фигурам используя шейдеры.

Анимирование объектов с WebGL

Как поворачивать и передвигать объекты для создания простых анимаций.

Создание 3D объектов с WebGL

Как создать и анимировать 3D объект (куб).

Использование текстур в WebGL

Как перевести текстуру на поверхность объекта.

Освещение объектов в WebGL

Как симулировать эффект света в контексте WebGL.

Анимирование текстур в WebGL (en-US)

Как анимировать текстуры; здесь путём отображения Ogg видео на поверхность вращающегося куба.


Лучшие практики WebGL - Интерфейсы веб API

Лучшие практики WebGL
=====================

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

Чего следует избегать
---------------------

* Убедитесь, что ваше приложение не выдаёт какие-либо ошибки WebGL, возвращаемые функцией `getError(). В Firefox при каждой ошибке (до определённого предела) или при любой другой проблеме в работе WebGL выводится JavaScript предупреждение с подробным описанием. Вам же не хочется, что бы ваше приложение выдавало множество ошибок в консоль, не так ли?`
* Не следует использовать `#ifdef GL_ES в шейдерах WebGL. Несмотря на то что в некоторых ранних примерах используются эти директивы, это не обязательно в том случае, если проверяемое условие всегда истинно.`
* Использование высокой точности (`highp` precision) во фрагментных шейдерах может приводить к несовместимости вашего приложения с некоторыми устаревшими мобильными устройствами. Вы можете использовать среднюю точность (`mediump), но помните, что это может привести к некорректному результату отрисовки из-за потери данных на большинстве мобильных устройств, причём этот некорректный результат не будет заметен на обычном компьютере. В общем, только использование высокой точности (highp) в вершинном и фрагментном шейдерах является более надёжными решением, если нет возможности тщательно проверить работу шейдеров на различных платформах. В Firefox версии 11 и выше реализована функция WebGL getShaderPrecisionFormat(), которая позволяет проверить, поддерживается ли высокая точность и, более того, запросить реальную точность всех поддерживаемых квалификаторов точности.`
О чем следует помнить
---------------------

* Некоторые возможности WebGL зависят от клиента. Перед тем как задействовать ту или иную возможность, используйте функцию WebGL `getParameter() чтобы определить, какие возможности поддерживаются на клиенте. Например, максимально допустимый размер двухмерной текстуры можно узнать с помощью вызова webgl.getParameter(webgl.MAX_TEXTURE_SIZE). В Firefox версии 10 и выше реализован параметр webgl.min_capability_mode, позволяющий имитировать минимальные значения возможностей WebGL для проверки переносимости приложения.`
* В частности, использование текстур в вершинном шейдере возможно только если значение `webgl.getParameter(webgl.MAX_VERTEX_TEXTURE_IMAGE_UNITS) больше ноля. Как правило, эта возможность не поддерживается на текущих мобильных устройствах.`
* Доступность большинства расширений WebGL зависит от клиента. Если это возможно, проектируйте приложение так, чтобы оно оставалось работоспособным даже в случае, когда используемое расширение недоступно. В Firefox версии 10 и выше есть настройка `webgl.disable-extensions, позволяющая сымитировать отсутствие всех расширений для проверки переносимости приложения.`
* Рендеринг в floating-point текстуру может не выполняться даже если расширение `OES_texture_float поддерживается. Обычно это случается на современных мобильных устройствах. Проверить эту возможность можно с помощью функции WebGL checkFramebufferStatus().`
* Вы можете выполнять отрисовку на холсте, реальные размеры которого отличается от значений, определённых в таблице стилей. При проблемах с производительностью рассмотрите возможность рендеринга в более низком разрешении. (*Уменьшение области рендеринга ускорит обработку пиксельных шейдеров, например, эффектов постобработки*, *однако, на скорость работы вершинных шейдеров это не повлияет.* *прим. перев.).*
Общие советы по повышению производительности
--------------------------------------------

* Все, что требует синхронизации ЦП и ГП потенциально приводит в уменьшению производительности. Поэтому избегайте в цикле отрисовки следующих вызовов функций WebGL: `getError()`, `readPixels()` и `finish()`. Вызовы функций, получающих значения, такие как `getParameter()` и `getUniformLocation() тоже должны рассматриваться как медленные и их значения следует сохраняться в переменных JavaScript.`
* Несколько больших операций отрисовки выполняются быстрее, чем много мелких. Если вам нужно нарисовать 1000 спрайтов, попробуйте реализовать это одним вызовом функции `drawArrays()` или `drawElements()`. Вы также можете использовать вырожденные (плоские) треугольники для рисования нескольких объектов за один вызов `drawArrays().`
* Уменьшение переключений состояний также увеличивает производительность. В частности, если есть возможность упаковать несколько изображений в одну текстуру *(т.н. текстурный атлас, прим. перев.)* и отображать требуемое изображение с помощью поправок текстурных координат, то это приведёт к уменьшению переключений между текстурами, что увеличит производительность.
    + В некоторых редких случаях разные одноцветные изображения можно упаковать в разные цветовые каналы текстуры.
* Маленькие текстуры обрабатываются быстрее, чем большие. Используйте mipmapping для ускорения отрисовки.
* Простые шейдеры выполняются быстрее, чем сложные. В частности, условия (if) замедляют работу. Операции деления и математические функции, например, `log() должны также рассматриваться как дорогие.`
    + Однако сегодня даже мобильные устройства обладают мощными графическими процессорами которые способны быстро обрабатывать относительно сложные шейдерные программы. Более того, шейдеры компилируются в машинные коды, которые могут быть оптимизированы под конкретный процессор. Может оказаться, что дорогой вызов функции может быть скомпилирован в несколько (или даже в одну) процессорную инструкцию. Частично это справедливо для функций GLSL, выполняющих операции над векторами, таких как `normalize()`, `dot()` и `mix()`. Лучшим советом будет использовать встроенные функции, нежели пытаться реализовать, например, собственную версию скалярного произведения или линейной интерполяции, которые будут скомпилированы в набор сложных и неоптимальных инструкций процессора.
* Выносите как можно больше операций в вершинный шейдер. Из-за того, что в процессе отрисовки фрагментные шейдеры выполняются гораздо чаще, чем вершинные, любые вычисления, которые можно выполнить с вершинами и интерполировать между пикселями, будут работать быстрее (интерполяция будет "бесплатна", т.к. это этап конвейера WebGL). Например, простая анимация текстурированной поверхности может быть реализована с помощью преобразований текстурных координат (простейший вариант - прибавлять значение uniform-вектора к attribute-вектору текстурных координат). Если результат будет визуально приемлем, то такой вариант будет работать быстрее, чем реализация во фрагментном шейдере.
* Всегда задействуйте атрибут вершин c нулевым индексом. Отрисовка с неактивным вершинным атрибутом с индексом 0 вынуждает браузер выполнять сложную эмуляцию настольного OpenGL (например, как на Mac OSX). Вызывайте функцию `bindAttribLocation()` чтобы вершинный атрибут использовал нулевой индекс и активируйте сам атрибут с помощью функции `enableVertexAttribArray()`.


Использование шейдеров для задания цвета в WebGL - Интерфейсы веб API

Использование шейдеров для задания цвета в WebGL
================================================

* « Предыдущая статья
* Следующая статья »

В предыдущей статье мы создали квадрат, следующим шагом будет добавление ему цвета. Мы можем сделать это, используя шейдеры.

Задание цвета вершинам
----------------------

В GL, объекты строятся с использованием наборов вершин, для каждой из которых задаётся положение в пространстве и цвет. По умолчанию, цвета всех остальных пикселей (и все их другие атрибуты, включая положение в пространстве) вычисляются с использованием линейной интерполяции, автоматически создавая плавный градиент. В прошлый раз наш вершинный шейдер не задавал определённых цветов вершинам, а фрагментный шейдер назначил фиксированный белый цвет каждому пикселю, поэтому квадрат целиком был отрисован белым цветом.

Предположим, что мы хотим отрисовать градиент, в котором каждый угол квадрата разного цвета: красного, синего, зелёного и белый. Первое, что необходимо сделать - назначить эти цвета четырём вершинам. Чтобы сделать это, нам сначала необходимо создать массив цветов вершин, а затем сохранить его в WebGL буфер. Мы сделаем это, добавив следующий код в нашу функцию `initBuffers()`:

```js
  var colors = [
    1.0,  1.0,  1.0,  1.0,    // белый
    1.0,  0.0,  0.0,  1.0,    // красный
    0.0,  1.0,  0.0,  1.0,    // зелёный
    0.0,  0.0,  1.0,  1.0     // синий
  ];

  squareVerticesColorBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY\_BUFFER, squareVerticesColorBuffer);
  gl.bufferData(gl.ARRAY\_BUFFER, new Float32Array(colors), gl.STATIC\_DRAW);
}
```

Этот код начинается с создания JavaScript массива, содержащего четыре вектора, содержащих по четыре значения каждый - по одному вектору для задания цвета каждой вершине. Затем новый WebGL буфер выделяет место в памяти для хранения этих цветов и массив приводится к вещественному формату WebGL и сохраняется в буфер.

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

```html
<script id="shader-vs" type="x-shader/x-vertex">
 attribute vec3 aVertexPosition;
 attribute vec4 aVertexColor;

 uniform mat4 uMVMatrix;
 uniform mat4 uPMatrix;

 varying lowp vec4 vColor;

 void main(void) {
 gl\_Position = uPMatrix \* uMVMatrix \* vec4(aVertexPosition, 1.0);
 vColor = aVertexColor;
 }
</script>
```

Ключевым отличием здесь является то, что для каждой вершины, мы задаём цвет на соответствующее значение из массива цвета.

Окраска фрагментов
------------------

Чтобы напомнить, как фрагментный шейдер выглядел ранее, ниже приведён его код:

```html
<script id="shader-fs" type="x-shader/x-fragment">
 void main(void) {
 gl\_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
 }
</script>
```

Для того, чтобы подобрать интерполируемый цвет каждому пикселю, нам просто необходимо изменить его, получив значение из переменной `vColor`:

```html
<script id="shader-fs" type="x-shader/x-fragment">
 varying lowp vec4 vColor;

 void main(void) {
 gl\_FragColor = vColor;
 }
</script>
```

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

Отрисовка с использованием цветов
---------------------------------

Далее, необходимо добавить код в процедуру `initShaders()` для задания значений атрибута цвета для шейдерной программы:

```js
vertexColorAttribute = gl.getAttribLocation(shaderProgram, "aVertexColor");
gl.enableVertexAttribArray(vertexColorAttribute);
```

Затем, drawScene() может быть исправлен на фактическое использование этих цветов при отрисовке квадрата:

```js
gl.bindBuffer(gl.ARRAY\_BUFFER, squareVerticesColorBuffer);
gl.vertexAttribPointer(vertexColorAttribute, 4, gl.FLOAT, false, 0, 0);
```

На этом этапе, вы можете посмотреть результат работы написанного кода, если используете браузер, поддерживающий WebGL. Вы увидите нечто похожее на следующий рисунок (расположенный по центру в большом чёрном поле):

Посмотреть полный исходный код | Открыть демо в новом окне

* « Предыдущая статья
* Следующая статья »


Lighting in WebGL - Интерфейсы веб API

Lighting in WebGL
=================

* « Предыдущая статья
* Следующая статья » (en-US)

Вам уже должно быть понятно,что у WebGL нет много "встроенного знания". Он просто выполняет две функции, которые вы написали - вершинный шейдер и фрагментный шейдер, а функции, которые рисуют сцену должны написать вы сами. Другими словами, если вы хотите добавить освещение, то вы должны рассчитать его самостоятельно. К счастью, сделать это не сложно. В этой статье мы опишем некоторые основы.

Симуляция освещения и затенения в 3D
------------------------------------

Хотя детали теории, лежащей в основе симуляции освещения в 3D-графике лежат далеко за пределами этой статьи, будет полезным немного узнать о том, как это работает. Посмотрите статью Затенение по Фонгу в Википедии, чтобы получить хороший обзор наиболее часто используемых моделей освещения. А в этой статье вы можете посмотреть объяснение, основанное на WebGL.

Существует три основных типа источников света:

**Окружающий свет** освещает всю сцену. Он не направленный и освещает все грани всех объектов одинаково, не зависимо от ориентации граней.

**Направленный свет** исходит из определённого направления. Этот свет приходит от настолько удалённого источника, что все фотоны летят параллельно друг другу. К примеру, солнечный свет можно считать.

**Точечный свет** исходит из одной точки во всех направлениях. В реальном мире многие источники освещения являются точечными, например электрическая лампочка.

В этой статье мы упростим модель освещения и будем использовать только простой направленный и окружающий свет. Мы не будем создавать блики на поверхности объектов и точечные источники света. Вместо этого мы добавим окружающий свет и направленный свет в сцену с вращающимся кубом из предыдущего примера.

Если оставить в стороне блики и точечные источники света, то останутся два пункта, которые нужно изучить по порядку:

1. Мы должны привязать **нормаль поверхности** к каждой вершине. Это вектор, который направлен перпендикулярно грани в данной вершине.
2. Нам нужно знать направление, в котором распространяется свет. Оно определяется **вектором направления**.

Затем мы обновим вершинный шейдер, чтобы скорректировать цвет каждой вершины в зависимости от окружающего и направленного освещения с учётом угла падения на грань. Мы увидим, как это делается, когда посмотрим на код шейдера.

Построение нормали для вершин
-----------------------------

Сначала нам нужно создать массив нормалей для всех вершин, из которых состоит наш куб. Это будет просто, потому что куб очень простой объект. Очевидно, что для более сложных объектов расчёт нормалей будет более затратным.

```js
  const normalBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY\_BUFFER, normalBuffer);

  const vertexNormals = [
    // Front
     0.0,  0.0,  1.0,
     0.0,  0.0,  1.0,
     0.0,  0.0,  1.0,
     0.0,  0.0,  1.0,

    // Back
     0.0,  0.0, -1.0,
     0.0,  0.0, -1.0,
     0.0,  0.0, -1.0,
     0.0,  0.0, -1.0,

    // Top
     0.0,  1.0,  0.0,
     0.0,  1.0,  0.0,
     0.0,  1.0,  0.0,
     0.0,  1.0,  0.0,

    // Bottom
     0.0, -1.0,  0.0,
     0.0, -1.0,  0.0,
     0.0, -1.0,  0.0,
     0.0, -1.0,  0.0,

    // Right
     1.0,  0.0,  0.0,
     1.0,  0.0,  0.0,
     1.0,  0.0,  0.0,
     1.0,  0.0,  0.0,

    // Left
    -1.0,  0.0,  0.0,
    -1.0,  0.0,  0.0,
    -1.0,  0.0,  0.0,
    -1.0,  0.0,  0.0
  ];

  gl.bufferData(gl.ARRAY\_BUFFER, new Float32Array(vertexNormals),
                gl.STATIC\_DRAW);

...

  return {
    position: positionBuffer,
    normal: normalBuffer,
    textureCoord: textureCoordBuffer,
    indices: indexBuffer,
  };
```

Код уже должен выглядеть узнаваемо. Мы создаём новый буфер, связываем его с рабочим буфером и записываем в него массив нормалей к вершинам при помощи `bufferData()`.

Затем добавим в `drawScene()` код, который свяжет массив нормалей с атрибутом шейдера. Таким образом шейдер сможет получить к нему доступ:

```js
// Указываем WebGL как извлекать нормали из
// буфера нормалей в атрибут vertexNormal.
{
  const numComponents = 3;
  const type = gl.FLOAT;
  const normalize = false;
  const stride = 0;
  const offset = 0;
  gl.bindBuffer(gl.ARRAY\_BUFFER, buffers.normal);
  gl.vertexAttribPointer(
    programInfo.attribLocations.vertexNormal,
    numComponents,
    type,
    normalize,
    stride,
    offset,
  );
  gl.enableVertexAttribArray(programInfo.attribLocations.vertexNormal);
}
```

В конце нужно обновить код, который строит матрицы для uniform-переменных, чтобы создать и передать в шейдер **матрицу нормалей**, которая используется для трансформации нормалей при расчёте ориентации куба относительно направления на источник света:

```js
  const normalMatrix = mat4.create();
  mat4.invert(normalMatrix, modelViewMatrix);
  mat4.transpose(normalMatrix, normalMatrix);

...

  gl.uniformMatrix4fv(
      programInfo.uniformLocations.normalMatrix,
      false,
      normalMatrix);
```

Обновление шейдеров
-------------------

Теперь у нас есть все данные для шейдеров. Пора обновить код самих шейдеров.

### Вершинный шейдер

Сначала обновим вершинный шейдер, чтобы он рассчитывал значение освещения для каждой вершины на основе окружающего и направленного света. Посмотрим на код:

```js
const vsSource = `
 attribute vec4 aVertexPosition;
 attribute vec3 aVertexNormal;
 attribute vec2 aTextureCoord;

 uniform mat4 uNormalMatrix;
 uniform mat4 uModelViewMatrix;
 uniform mat4 uProjectionMatrix;

 varying highp vec2 vTextureCoord;
 varying highp vec3 vLighting;

 void main(void) {
 gl\_Position = uProjectionMatrix \* uModelViewMatrix \* aVertexPosition;
 vTextureCoord = aTextureCoord;

 // Применяем эффект освещения

 highp vec3 ambientLight = vec3(0.3, 0.3, 0.3);
 highp vec3 directionalLightColor = vec3(1, 1, 1);
 highp vec3 directionalVector = normalize(vec3(0.85, 0.8, 0.75));

 highp vec4 transformedNormal = uNormalMatrix \* vec4(aVertexNormal, 1.0);

 highp float directional = max(dot(transformedNormal.xyz, directionalVector), 0.0);
 vLighting = ambientLight + (directionalLightColor \* directional);
 }
 `;
```

После расчёта позиции вершины мы передаём координаты текселя (texel (en-US)), соответствующего вершине, во фрагментный шейдер, и начинаем расчёт освещения вершины.

Сначала нужно преобразовать нормаль, основываясь на текущей ориентации куба - умножив нормаль вершины на матрицу нормалей. Затем мы можем рассчитать количество света от направленного источника, которое приходит в вершину, посчитав скалярное произведение преобразованной нормали и вектора направления (направления, с которого приходит свет). Если скалярное произведение меньше нуля, то мы принимаем его за ноль, потому что количество света не может быть меньше 0.

После расчёта количества падающего направленного света мы можем посчитать финальное освещение, сложив окружающий свет и произведение количества направленного света на его цвет. В результате получается значение RGB, которое используется фрагментным шейдером для изменения цвета каждого пикселя.

### Фрагментный шейдер

Фрагментный шейдер должен быть обновлён таким образом, чтобы он учитывал в значение освещения, рассчитанное в вершинном шейдере:

```js
const fsSource = `
 varying highp vec2 vTextureCoord;
 varying highp vec3 vLighting;

 uniform sampler2D uSampler;

 void main(void) {
 highp vec4 texelColor = texture2D(uSampler, vTextureCoord);

 gl\_FragColor = vec4(texelColor.rgb \* vLighting, texelColor.a);
 }
 `;
```

Здесь мы получаем цвет текселя, как и в предыдущем примере, но перед тем, как установить цвет фрагмента, мы умножаем цвет текселя на значение освещения, чтобы учесть влияние источников света.

Осталось только посмотреть на определение атрибута `aVertexNormal` и uniform-переменной `uNormalMatrix`.

```js
const programInfo = {
  program: shaderProgram,
  attribLocations: {
    vertexPosition: gl.getAttribLocation(shaderProgram, "aVertexPosition"),
    vertexNormal: gl.getAttribLocation(shaderProgram, "aVertexNormal"),
    textureCoord: gl.getAttribLocation(shaderProgram, "aTextureCoord"),
  },
  uniformLocations: {
    projectionMatrix: gl.getUniformLocation(shaderProgram, "uProjectionMatrix"),
    modelViewMatrix: gl.getUniformLocation(shaderProgram, "uModelViewMatrix"),
    normalMatrix: gl.getUniformLocation(shaderProgram, "uNormalMatrix"),
    uSampler: gl.getUniformLocation(shaderProgram, "uSampler"),
  },
};
```

И это всё!

Посмотреть код примера полностью | Открыть демо в новом окне

Самостоятельные упражнения
--------------------------

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

Также вы можете поэкспериментировать с направлением на источник света, цветами окружающего и направленного света, и т. д.

* « Предыдущая статья
* Следующая статья » (en-US)


Добавление двухмерного контента в контекст WebGL - Интерфейсы веб API

Добавление двухмерного контента в контекст WebGL
================================================

* « Предыдущая статья
* Следующая статья »

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

Отрисовка сцены
---------------

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

### Инициализация шейдеров

Шейдеры задаются при помощи языка высокого уровня для программирования шейдеров - OpenGL ES Shading Language. Для того, чтобы сделать проще процесс поддержки и обновления нашего контента, мы можем фактически написать наш код, загружающий шейдеры и помещающий их в HTML документ, вместо того, чтобы встраивать его весь в JavaScript. Давайте рассмотрим нашу процедуру `initShaders()`, которая выполнит эту задачу:

```js
function initShaders() {
  var fragmentShader = getShader(gl, "shader-fs");
  var vertexShader = getShader(gl, "shader-vs");

  // создать шейдерную программу

  shaderProgram = gl.createProgram();
  gl.attachShader(shaderProgram, vertexShader);
  gl.attachShader(shaderProgram, fragmentShader);
  gl.linkProgram(shaderProgram);

  // Если создать шейдерную программу не удалось, вывести предупреждение

  if (!gl.getProgramParameter(shaderProgram, gl.LINK\_STATUS)) {
    alert("Unable to initialize the shader program.");
  }

  gl.useProgram(shaderProgram);

  vertexPositionAttribute = gl.getAttribLocation(
    shaderProgram,
    "aVertexPosition",
  );
  gl.enableVertexAttribArray(vertexPositionAttribute);
}
```

Этой процедурой загружаются две шейдерные программы. Первая - фрагментный шейдер, загружается из элемента `script` с ID "shader-fs". Вторая - вершинный шейдер, загружается из элемента `script` с ID "shader-vs". Мы рассмотрим функцию `getShader()` чуть ниже. Эта процедура фактически отвечает за извлечение шейдерных программ из DOM.

Затем мы создаём шейдерную программу, вызывая функцию `createProgram()` объекта WebGL, присоединяя два шейдера к нему, и связывая шейдерную программу. После выполнения этого, проверяется значение параметра `LINK_STATUS` объекта `gl` для того, чтобы убедиться, что программа успешно скомпонована. Если это так, мы активируем новую шейдерную программу.

### Загрузка шейдеров из DOM

Функция `getShader()` получает из DOM шейдерную программу с определённым именем, возвращая скомпилированную шейдерную программу вызывающему, или значение null, если шейдерная программа не может быть загружена или скомпилирована.

```js
function getShader(gl, id) {
  var shaderScript, theSource, currentChild, shader;

  shaderScript = document.getElementById(id);

  if (!shaderScript) {
    return null;
  }

  theSource = "";
  currentChild = shaderScript.firstChild;

  while(currentChild) {
    if (currentChild.nodeType == currentChild.TEXT\_NODE) {
      theSource += currentChild.textContent;
    }

    currentChild = currentChild.nextSibling;
  }
```

Как только элемент с указанным ID найден, его текст помещается в переменную `theSource`.

```js
if (shaderScript.type == "x-shader/x-fragment") {
  shader = gl.createShader(gl.FRAGMENT\_SHADER);
} else if (shaderScript.type == "x-shader/x-vertex") {
  shader = gl.createShader(gl.VERTEX\_SHADER);
} else {
  // неизвестный тип шейдера
  return null;
}
```

После того, как код для шейдера считан, мы проверяем MIME тип шейдерного объекта, чтобы определить является он вершинным (MIME type "x-shader/x-vertex") или фрагментным (MIME type "x-shader/x-fragment") шейдером, а затем создаём соответствующий тип шейдера из полученного исходного кода.

```js
  gl.shaderSource(shader, theSource);

  // скомпилировать шейдерную программу
  gl.compileShader(shader);

  // Проверить успешное завершение компиляции
  if (!gl.getShaderParameter(shader, gl.COMPILE\_STATUS)) {
      alert("An error occurred compiling the shaders: " + gl.getShaderInfoLog(shader));
      return null;
  }

  return shader;
}
```

В результате, исходный код, передан в переменную shader и скомпилирован. Если произошли ошибки во время компиляции кода шейдера, мы отображаем окно с предупреждением и возвращаем значение null; Иначе, возвращается новый скомпилированный шейдер.

### Шейдеры

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

#### Фрагментный шейдер

Каждый пиксель в полигоне называется **фрагментом** в языке GL. Фрагментные шейдеры необходимы для назначения цвета для каждого пикселя. В данном случае, мы просто назначаем белый цвет каждому пикселю.

`gl_FragColor` - встроенная переменная GL, используемая для управления цветом фрагментов. Устанавливая её значение назначаем цвет пикселям. Ниже приведён пример этого.

```html
<script id="shader-fs" type="x-shader/x-fragment">
 void main(void) {
 gl\_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
 }
</script>
```

#### Вершинный шейдер

Вершинный шейдер определяет положение и форму каждой вершины.

```html
<script id="shader-vs" type="x-shader/x-vertex">
 attribute vec3 aVertexPosition;

 uniform mat4 uMVMatrix;
 uniform mat4 uPMatrix;

 void main(void) {
 gl\_Position = uPMatrix \* uMVMatrix \* vec4(aVertexPosition, 1.0);
 }
</script>
```

Создание объекта
----------------

Перед тем, как мы отрисуем наш квадрат, нам необходимо создать буфер, который содержит его вершины. Мы сделаем это, вызвав функцию `initBuffers().` По мере ознакомления с другими концепциями WebGL, эта функция будет усложняться при создании более сложных трёхмерных объектов.

```js
var horizAspect = 480.0 / 640.0;

function initBuffers() {
  squareVerticesBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY\_BUFFER, squareVerticesBuffer);

  var vertices = [
    1.0, 1.0, 0.0, -1.0, 1.0, 0.0, 1.0, -1.0, 0.0, -1.0, -1.0, 0.0,
  ];

  gl.bufferData(gl.ARRAY\_BUFFER, new Float32Array(vertices), gl.STATIC\_DRAW);
}
```

В этом примере эта функция упрощена и даёт оценить основную суть сцены. Она начинает работу с вызова метода `createBuffer()` объекта `gl` для получения буфера, в котором мы будем хранить вершины. Затем он привязывается к контексту, вызовом метода `bindBuffer()`.

После того, как мы это сделали, мы создаём JavaScript массив, содержащий координаты для каждой вершины квадрата. Затем этот массив преобразуется в массив вещественных чисел WebGL и передаётся в метод `bufferData()` объекта `gl` для назначения вершин объекту.

Рендеринг сцены
---------------

Как только шейдеры установлены и объект создан, мы можем действительно отрисовать сцену. Поскольку в этом примере нет какой-либо анимации, наша функция `drawScene()` имеет очень простой вид. Она использует несколько утилитарных процедур, которые мы кратко рассмотрим ниже.

```js
function drawScene() {
  gl.clear(gl.COLOR\_BUFFER\_BIT | gl.DEPTH\_BUFFER\_BIT);

  perspectiveMatrix = makePerspective(45, 640.0 / 480.0, 0.1, 100.0);

  loadIdentity();
  mvTranslate([-0.0, 0.0, -6.0]);

  gl.bindBuffer(gl.ARRAY\_BUFFER, squareVerticesBuffer);
  gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0);
  setMatrixUniforms();
  gl.drawArrays(gl.TRIANGLE\_STRIP, 0, 4);
}
```

Первый шаг - очистка цветом фона сцены контекста. Затем мы устанавливаем перспективу камеры. Мы устанавливаем угол обзора в 45°, с соотношением ширины к высоте равным 640/480 (размеры нашего объекта canvas). Мы также определяем, что мы хотим видеть отрисованными объекты на расстоянии от 0.1 до 100 единиц от камеры.

Затем мы устанавливаем позицию квадрата, загружая определённую позицию и размещая её от камеры на 6 единиц. После этого, мы привязываем буфер, содержащий вершины квадрата к контексту, настраиваем его, и отрисовываем объект, вызывая метод `drawArrays()`.

Вы можете посмотреть как это работает щёлкнув по этой ссылке. Но это только в том случае, если используемый браузер осуществляет поддержку WebGL.

Операции над матрицами
----------------------

Операции над матрицами достаточно сложны. Никому бы не хотелось самому писать весь код для работы с ними. К счастью, есть Sylvester - очень удобная библиотека для выполнения операций над векторами и матрицами, написанная на JavaScript.

Файл `glUtils.js`, используемый в этом примере, используется большинством примеров на WebGL, размещённых в сети интернет. Никто не знает точно откуда он появился. Он просто используется Sylvester и он даже пополняется методами для построения специальных типов матриц, а также вывода HTML для их отображения.

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

```js
function loadIdentity() {
  mvMatrix = Matrix.I(4);
}

function multMatrix(m) {
  mvMatrix = mvMatrix.x(m);
}

function mvTranslate(v) {
  multMatrix(Matrix.Translation($V([v[0], v[1], v[2]])).ensure4x4());
}

function setMatrixUniforms() {
  var pUniform = gl.getUniformLocation(shaderProgram, "uPMatrix");
  gl.uniformMatrix4fv(
    pUniform,
    false,
    new Float32Array(perspectiveMatrix.flatten()),
  );

  var mvUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix");
  gl.uniformMatrix4fv(mvUniform, false, new Float32Array(mvMatrix.flatten()));
}
```

Смотрите также
--------------

* Матрицы на Wolfram MathWorld
* Матрица (Wikipedia)
* « Предыдущая статья
* Следующая статья »


Начало работы с WebGL - Интерфейсы веб API

Начало работы с WebGL
=====================

* Следующая статья »

WebGL позволяет веб-контенту использовать API, основанный на OpenGL ES 2.0, для визуализации трёхмерной графики без использования плагинов в HTML элементе `canvas` в браузерах, которые осуществляют его поддержку. WebGL программы состоят из кода управления, написанном на JavaScript и кода специальных эффектов (шейдерного кода), который выполняется на графическом процессоре. WebGL элементы могут быть смешаны с другими HTML-элементами и собраны с другими частями веб-страницы или фоном веб-страницы.

Эта статья знакомит вас с основами использования WebGL. Предполагается, что вы уже знакомы с математическими основами трёхмерной графики. Данная статья не обучит вас OpenGL.

Подготовка к визуализации в 3D
------------------------------

Первое, что вам понадобится для использования WebGL для визуализации в 3D - это элемент canvas. Фрагмент на HTML ниже содержит элемент canvas и определяет обработчик события `onload`, которое инициализирует наш контекст WebGL.

```html
<body onload="start()">
  <canvas id="glcanvas" width="640" height="480">
    Your browser doesn't appear to support the HTML5
    <code><canvas></code> element.
  </canvas>
</body>
```

### Подготовка контекста WebGL

Функция `start()`, в нашем JavaScript-коде вызывается после загрузки документа. Её назначение - настройка контекста WebGL и начать отрисовку содержимого.

```js
var gl; // глобальная переменная для контекста WebGL

function start() {
  var canvas = document.getElementById("glcanvas");

  gl = initWebGL(canvas); // инициализация контекста GL

  // продолжать только если WebGL доступен и работает

  if (gl) {
    gl.clearColor(0.0, 0.0, 0.0, 1.0); // установить в качестве цвета очистки буфера цвета чёрный, полная непрозрачность
    gl.enable(gl.DEPTH\_TEST); // включает использование буфера глубины
    gl.depthFunc(gl.LEQUAL); // определяет работу буфера глубины: более ближние объекты перекрывают дальние
    gl.clear(gl.COLOR\_BUFFER\_BIT | gl.DEPTH\_BUFFER\_BIT); // очистить буфер цвета и буфер глубины.
  }
}
```

Первое, что мы здесь делаем - получаем ссылку на элемент canvas, помещаем её в переменную `canvas`. Очевидно, что если вам не требуется многократно получать ссылку на canvas, вы должны избежать сохранения этого значения глобально, а только сохранить её в локальной переменной или в поле объекта.

Как только мы получили ссылку на canvas, мы вызываем функцию `initWebGL()`; Эту функцию мы определяем незамедлительно, её работа - инициализировать контекст WebGL.

Если контекст успешно инициализирован, в `gl` будет содержаться ссылка на него. В этом случае, мы устанавливаем цвет очистки буфера цвета (цвет фона) на чёрный, затем очищаем контекст этим цветом. После этого, контекст конфигурируется параметрами настройки. В данном случае, мы включаем буфер глубины и определяем, что более близкие объекты будут перекрывать более дальние.

Всё вышеперечисленное необходимо сделать только для первоначальной инициализации. Чуть позже мы увидим работу по визуализации трёхмерных объектов.

### Создание контекста WebGL

Функция `initWebGL()` выглядит следующим образом:

```js
function initWebGL(canvas) {
  gl = null;

  try {
    // Попытаться получить стандартный контекст. Если не получится, попробовать получить экспериментальный.
    gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
  } catch (e) {}

  // Если мы не получили контекст GL, завершить работу
  if (!gl) {
    alert("Unable to initialize WebGL. Your browser may not support it.");
    gl = null;
  }

  return gl;
}
```

Чтобы получить контекст WebGL для canvas, мы запрашиваем у элемента canvas контекст именуемый как "webgl". Если данная попытка завершается неудачно, мы пытаемся получить контекст, именуемый как "experimental-webgl". Если данная попытка также завершается неудачно, мы отображаем окно с предупреждением, позволяющим пользователю понять, что его браузер не поддерживает WebGL. Это всё, что необходимо сделать. На данном этапе мы будем иметь в переменной `gl` либо значение null (означающее, что контекст WebGL не доступен), либо ссылку на контекст WebGL в котором, мы будем производить отрисовку.

**Примечание:** Контекст, именуемый как "experimental-webgl" - это временное имя для контекста, используемое на время процесса разработки спецификации. После того, как спецификация будет полностью разработана будет использоваться только имя контекста "webgl".

На данном этапе этого кода достаточно, чтобы успешно инициализировать контекст WebGL, и вы увидите пустой чёрный блок, готовый к заполнению контентом.

Если ваш браузер поддерживает WebGL, то вы можете посмотреть результат выполнения приведённого выше кода, щёлкнув по этой ссылке.

### Изменение размера контекста WebGL

Новый контекст WebGL будет иметь возможность задания размеров области отображения в момент получения контекста путём задания высоты и ширины элемента canvas, без использования CSS. Редактирование стиля элемента canvas будет изменять его отображаемый размер, без изменения размеров области отрисовки. Редактирование атрибутов ширины и высоты элемента canvas после создания контекста не будет также изменять число пикселей для отрисовки. Чтобы изменить размер области отрисовки, с которой WebGL производит работу, например, когда пользователь изменяет размер окна на весь экран или когда вам необходимо менять настройки графики в самом приложении, вам необходимо вызвать контекстную функцию WebGL `viewport()`, чтобы подтвердить изменения.

Чтобы изменить размер области отрисовки контекста WebGL с переменными `gl` и `canvas`, использующимися в примере выше:

```js
gl.viewport(0, 0, canvas.width, canvas.height);
```

Элемент canvas будет изменять размер, в случае, если его отрисовать с другими размерами области отрисовки, чем те, что указаны в его стилях CSS, согласно которым он занимает место на экране. Изменение размера с помощью CSS наиболее полезно для сохранения ресурсов, отрисовывая с низким разрешением и позволяя браузеру растягивать полученный результат; сжимать возможно при использовании эффекта избыточной выборки сглаживания ( super sample antialiasing - SSAA) (приводит к менее приемлемым результатам и серьёзным потерям производительности). В большинстве случаев лучше полагаться на множественную выборку сглаживания (MSAA) и на реализацию алгоритмов фильтрации текстур, которые поддерживаются браузером пользователя, чем делать то же самое посредством грубой силы и надеяться, что алгоритм масштабирования изображений, используемый браузером, выдаст красивый результат.

### Смотрите также

* Введение в WebGL - Автор: Luz Caballero Ресурс: DEV.OPERA. Эта статья адресована тем, кто хочет узнать что такое WebGL, как работает WebGL и как устроена концепция конвейера визуализации и знакомит с некоторыми библиотеками WebGL.
* Введение в современный OpenGL - Серия хороших статей о OpenGL, написанная Joe Groff. В них дана вводная информация о OpenGL, начиная с истории возникновения и заканчивая важной концепцией графического конвейера. Весь материал сопровождается примерами с демонстрацией работы OpenGL. Если вы не знакомы с OpenGL, этот материал будет вам полезен.
* Следующая статья »


Создание 3D объектов с помощью WebGL - Интерфейсы веб API

Создание 3D объектов с помощью WebGL
====================================

* « Предыдущая статья
* Следующая статья »

Давайте поместим наш квадрат в трёхмерное пространство, добавив ещё 5 граней, чтобы получить куб. Чтобы сделать это наиболее продуктивно, вместо рисования вершин непосредственным вызовом метода `gl.drawArrays()` (en-US) , мы будем использовать массив вершин в виде таблицы и ссылаться на каждую вершину в этой таблице, чтобы определить положение каждой вершины грани, вызывая `gl.drawElements()` (en-US).

Заметим: чтобы определить каждую грань необходимо четыре вершины, но каждая вершина принадлежит трём граням. Мы можем передавать намного меньше данных, построив список всех 24-х вершин, затем ссылаться на каждую из них в этом списке по её индексу, вместо того чтобы передавать все множество вершин. Если вы удивлены, почему нам нужны 24 вершины, а не только 8, так это потому, что каждое ребро принадлежит трём граням разных цветов, и каждая отдельная вершина должна иметь конкретный отдельный цвет - поэтому мы создадим 3 копии каждой вершины трёх разных цветов, по одной для каждой грани.

Определение позиций вершин куба
-------------------------------

Во первых, давайте построим буфер позиций вершин куба, обновив код в `initBuffers()`. Это в значительной степени то же самое как это было для квадрата, но несколько длиннее, так как здесь 24 вершины (4 с каждой стороны):

```js
var vertices = [
  // Передняя грань
  -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0,

  // Задняя грань
  -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0,

  // Верхняя грань
  -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0,

  // Нижняя грань
  -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0,

  // Правая грань
  1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0,

  // Левая грань
  -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0,
];
```

Определение цветов вершин
-------------------------

Нам также нужно построить массив цветов для каждой из 24-х вершин. Этот код начинается с определения цветов для каждой грани, затем используется цикл для составления массива все всех цветов для каждой из вершин.

```js
var colors = [
  [1.0, 1.0, 1.0, 1.0], // Front face: white
  [1.0, 0.0, 0.0, 1.0], // Back face: red
  [0.0, 1.0, 0.0, 1.0], // Top face: green
  [0.0, 0.0, 1.0, 1.0], // Bottom face: blue
  [1.0, 1.0, 0.0, 1.0], // Right face: yellow
  [1.0, 0.0, 1.0, 1.0], // Left face: purple
];

var generatedColors = [];

for (j = 0; j < 6; j++) {
  var c = colors[j];

  for (var i = 0; i < 4; i++) {
    generatedColors = generatedColors.concat(c);
  }
}

cubeVerticesColorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY\_BUFFER, cubeVerticesColorBuffer);
gl.bufferData(
  gl.ARRAY\_BUFFER,
  new Float32Array(generatedColors),
  gl.STATIC\_DRAW,
);
```

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

Как только массив вершин сгенерирован, нам нужно построить массив элементов.

```js
cubeVerticesIndexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT\_ARRAY\_BUFFER, cubeVerticesIndexBuffer);

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

var cubeVertexIndices = [
  0,
  1,
  2,
  0,
  2,
  3, // front
  4,
  5,
  6,
  4,
  6,
  7, // back
  8,
  9,
  10,
  8,
  10,
  11, // top
  12,
  13,
  14,
  12,
  14,
  15, // bottom
  16,
  17,
  18,
  16,
  18,
  19, // right
  20,
  21,
  22,
  20,
  22,
  23, // left
];

// Теперь отправим массив элементов в GL

gl.bufferData(
  gl.ELEMENT\_ARRAY\_BUFFER,
  new Uint16Array(cubeVertexIndices),
  gl.STATIC\_DRAW,
);
```

Массив `cubeVertexIndices` определяет каждую грань как пару треугольников, сопоставляя каждой вершине треугольника индекс в массиве вершин куба. Таким образом куб можно представить как набор из 12 треугольников.

Рисование куба
--------------

Далее нам нужно обновить код нашей функции `drawScene()` , чтобы рисовать, используя буфер индексов куба, добавив новые вызовы `gl.bindBuffer()` и `gl.drawElements()` (en-US):

```js
gl.bindBuffer(gl.ELEMENT\_ARRAY\_BUFFER, cubeVerticesIndexBuffer);
setMatrixUniforms();
gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED\_SHORT, 0);
```

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

В данный момент у нас есть анимированный куб с гранями 6 разных цветов, который прыгает и вращается.

View the complete code | Open this demo on a new page

* « Предыдущая статья
* Следующая статья »


Оживление объектов при помощи WebGL - Интерфейсы веб API

Оживление объектов при помощи WebGL
===================================

* « Предыдущая статья
* Следующая статья »

Наш код оставшийся от предыдущего примера уже настроен на перерисовку нашей WebGL сцены каждые 15 миллисекунд. До сих пор мы просто отрисовывали в точности одно и то же каждый раз. Пришло время изменить это так, чтобы наш квадрат начал двигаться.

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

Заставим квадрат вращаться
--------------------------

Давайте начнём с того, что заставим квадрат вращаться. Для начала нам понадобится переменная значение в которой будет отражать текущий угол поворота нашего квадрата:

```js
var squareRotation = 0.0;
```

Теперь нам надо изменить процедуру `drawScene()` и добавить в неё код, который бы учитывал этот угол поворота при отрисовке квадрата в нашей сцене. После первоначального расчёта матрицы положения для квадрата мы применяем условия поворота следующим образом:

```js
mvPushMatrix();
mvRotate(squareRotation, [1, 0, 1]);
```

Этот код сохраняет матрицу текущего положения, затем поворачивает матрицу на текущее значение переменной `squareRotation` вокруг осей X и Z.

После отрисовки мы восстанавливаем изначальное состояние матрицы положения:

```js
mvPopMatrix();
```

Мы сохраняем и восстанавливаем состояние матрицы положения чтобы избежать влияние поворота на другие объекты, которые мы можем захотеть отобразить.

Чтобы заставить квадрат крутиться нам нужно добавить в программу код, который будет изменять значение переменной `squareRotation` во времени. Для этого мы заведём ещё одну переменную в которую будем записывать время последней отрисовки (давайте назовём её `lastSquareUpdateTime`), затем добавим следующий код в конец нашей процедуры `drawScene()`:

```js
var currentTime = new Date().getTime();
if (lastSquareUpdateTime) {
  var delta = currentTime - lastSquareUpdateTime;

  squareRotation += (30 \* delta) / 1000.0;
}

lastSquareUpdateTime = currentTime;
```

Этот код использует количество времени прошедшего с момента, когда мы в последний раз изменяли значение переменной `squareRotation` чтобы определить как сильно нужно повернуть квадрат.

Сделаем так, чтобы квадрат начал двигаться
------------------------------------------

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

Давайте отслеживать сдвиг положения квадрата по каждой оси в новых переменных:

```js
var squareXOffset = 0.0;
var squareYOffset = 0.0;
var squareZOffset = 0.0;
```

И расстояние на которое нужно сместить квадрат по каждой из осей в следующих переменных:

```js
var xIncValue = 0.2;
var yIncValue = -0.4;
var zIncValue = 0.3;
```

Теперь мы можем просто добавить следующий код к уже написанному, который обновляет положение угла поворота:

```js
squareXOffset += xIncValue \* ((30 \* delta) / 1000.0);
squareYOffset += yIncValue \* ((30 \* delta) / 1000.0);
squareZOffset += zIncValue \* ((30 \* delta) / 1000.0);

if (Math.abs(squareYOffset) > 2.5) {
  xIncValue = -xIncValue;
  yIncValue = -yIncValue;
  zIncValue = -zIncValue;
}
```

И, наконец, мы добавляем в процедуру `drawScene()` следующий код:

```js
mvTranslate([squareXOffset, squareYOffset, squareZOffset]);
```

Это заставит наш квадрат двигаться по экрану, приближаясь и удаляясь от наблюдателя и при этом ещё и вращаться. Это похоже на работу заставки

Если ваш браузер поддерживает работу с WebGL, проследуйте по этой ссылке, чтобы посмотреть на этот пример в действии.

Больше операций с матрицами
---------------------------

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

```js
var mvMatrixStack = [];

function mvPushMatrix(m) {
  if (m) {
    mvMatrixStack.push(m.dup());
    mvMatrix = m.dup();
  } else {
    mvMatrixStack.push(mvMatrix.dup());
  }
}

function mvPopMatrix() {
  if (!mvMatrixStack.length) {
    throw "Can't pop from an empty matrix stack.";
  }

  mvMatrix = mvMatrixStack.pop();
  return mvMatrix;
}

function mvRotate(angle, v) {
  var inRadians = (angle \* Math.PI) / 180.0;

  var m = Matrix.Rotation(inRadians, $V([v[0], v[1], v[2]])).ensure4x4();
  multMatrix(m);
}
```

Эти утилиты были заимствованы из примера разработанного Владом Вукицевичем (Vlad Vukićević).

* « Предыдущая статья
* Следующая статья »