From 980fe00a74a9ad013b945755415ace2e5429c3c2 Mon Sep 17 00:00:00 2001
From: Alexey Pyltsyn Попробуйте выполнить в консоли браузера следующий код: Функция возвращает "Hello" — ничего необычного, верно ? Но что если мы сделаем её асинхронной ? Проверим: Как было сказано ранее, вызов асинхронной функции возвращает объект Promise. Вот пример с async function expression: Также можно использовать стрелочные функции: Все они в общем случае делают одно и то же. Чтобы получить значение, которое возвращает Promise, мы как обычно можем использовать метод или ещё короче Итак, ключевое слово Небольшой пример: Давайте посмотрим на пример из предыдущей статьи: К этому моменту вы должны понимать как работают Promises, чтобы понять все остальное. Давайте перепишем код используя async/await и оценим разницу. Так как ключевое слово function hello() { return "Hello" };
+
function hello() { return "Hello" };
hello();
async function hello() { return "Hello" };
+
async function hello() { return "Hello" };
hello();
let hello = async function() { return "Hello" };
+
let hello = async function() { return "Hello" };
hello();
let hello = async () => { return "Hello" };
+let hello = async () => { return "Hello" };
.then()
:hello().then((value) => console.log(value))
+hello().then((value) => console.log(value))
hello().then(console.log)
+
hello().then(console.log)
async
, превращает обычную функцию в асинхронную и результат вызова функции оборачивает в Promise. Также асинхронная функция позволяет использовать в своём теле ключевое слово await, о котором далее.async function hello() {
+
async function hello() {
return greeting = await Promise.resolve("Hello");
};
@@ -90,7 +90,7 @@ hello().then(alert);
fetch('coffee.jpg')
+
fetch('coffee.jpg')
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
@@ -110,7 +110,7 @@ hello().then(alert);
async function myFetch() {
+
async function myFetch() {
let response = await fetch('coffee.jpg');
if (!response.ok) {
@@ -134,7 +134,7 @@ myFetch()
async
заставляет функцию вернуть Promise, мы можем использовать гибридный подход:async function myFetch() {
+
async function myFetch() {
let response = await fetch('coffee.jpg');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
@@ -160,7 +160,7 @@ myFetch().then((blob) => {
Пример:
let response = await fetch('coffee.jpg');+
let response = await fetch('coffee.jpg');
Значение Promise, которое вернёт fetch()
будет присвоено переменной response
только тогда, когда оно будет доступно - парсер делает паузу на данной строке дожидаясь этого момента. Как только значение доступно, парсер переходит к следующей строке, в которой создаётся объект Blob
из результата Promise. В этой строке, кстати, также используется await
, потому что метод .blob()
также возвращает Promise. Когда результат готов, мы возвращаем его наружу из myFetch()
.
Мы можем использовать синхронную try...catch
структуру с async
/await
. Вот изменённая версия первого примера выше:
async function myFetch() { +async function myFetch() { try { let response = await fetch('coffee.jpg'); @@ -199,7 +199,7 @@ myFetch();Если вы хотите использовать гибридный подходы (пример выше), лучше использовать блок
-.catch()
после блока.then()
вот так:async function myFetch() { +async function myFetch() { let response = await fetch('coffee.jpg'); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); @@ -233,7 +233,7 @@ myFetch().then((blob) => {Версия с async/await (смотрите live demo и source code), сейчас выглядит так:
-async function fetchAndDecode(url, type) { +async function fetchAndDecode(url, type) { let response = await fetch(url); let content; @@ -282,7 +282,7 @@ displayContent()Вы видите, что мы легко изменили
-fetchAndDecode()
функцию в асинхронный вариант. Взгляните на строку сPromise.all()
:let values = await Promise.all([coffee, tea, description]);+let values = await Promise.all([coffee, tea, description]);С помощью
await
мы ждём массив результатов всех трёх Promises и присваиваем его в переменнуюvalues
. Это асинхронный код, но он написан в синхронном стиле, за счёт чего он гораздо читабельнее.
@@ -306,7 +306,7 @@ displayContent()Мы подготовили два примера — slow-async-await.html (см. source code) и fast-async-await.html (см. source code). Они оба начинаются с функции возвращающей promise, имитирующей асинхронность процессов при помощи вызова
-setTimeout()
:function timeoutPromise(interval) { +function timeoutPromise(interval) { return new Promise((resolve, reject) => { setTimeout(function(){ resolve("done"); @@ -316,13 +316,13 @@ displayContent()Далее в каждом примере есть асинхронная функция
-timeTest()
ожидающая три вызоваtimeoutPromise()
:async function timeTest() { +async function timeTest() { ... }В каждом примере функция записывает время начала исполнения и сколько времени понадобилось на исполнение
-timeTest()
промисов, вычитая время в момент запуска функции из времени в момент разрешения промисов:let startTime = Date.now(); +let startTime = Date.now(); timeTest().then(() => { let finishTime = Date.now(); let timeTaken = finishTime - startTime; @@ -333,7 +333,7 @@ timeTest().then(() => {В случае с медленным примером
-slow-async-await.html
,timeTest()
выглядит:async function timeTest() { +async function timeTest() { await timeoutPromise(3000); await timeoutPromise(3000); await timeoutPromise(3000); @@ -343,7 +343,7 @@ timeTest().then(() => {Во втором
-fast-async-await.html
примере, функцияtimeTest()
выглядит как:async function timeTest() { +async function timeTest() { const timeoutPromise1 = timeoutPromise(3000); const timeoutPromise2 = timeoutPromise(3000); const timeoutPromise3 = timeoutPromise(3000); @@ -365,7 +365,7 @@ timeTest().then(() => {В качестве последнего замечания, вы можете использовать
-async
перед методами классов или объектов, вынуждая их возвращать promises. А также await внутри методов объявленных таким образом. Посмотрите на пример ES class code, который мы наблюдали в статье object-oriented JavaScript, и сравните его с модифицированной (асинхронной)async
версией ниже:class Person { +class Person { constructor(first, last, age, gender, interests) { this.name = { first, @@ -389,7 +389,7 @@ let han = new Person('Han', 'Solo', 25, 'male', ['Smuggling']);Первый метод класса теперь можно использовать таким образом:
-han.greeting().then(console.log);+han.greeting().then(console.log);Browser support (Поддержка браузерами)
diff --git a/files/ru/learn/javascript/asynchronous/concepts/index.html b/files/ru/learn/javascript/asynchronous/concepts/index.html index bdc84b6f72..fe4e6c7343 100644 --- a/files/ru/learn/javascript/asynchronous/concepts/index.html +++ b/files/ru/learn/javascript/asynchronous/concepts/index.html @@ -45,7 +45,7 @@ translation_of: Learn/JavaScript/Asynchronous/ConceptsВ нашем simple-sync.html примере (see it running live), добавим кнопке событие на клик, чтобы при нажатии на неё запускалась трудоёмкая операция (расчёт 10000000 дат, и вывод последней рассчитанной даты на консоль) после чего в DOM добавляется ещё один параграф:
-const btn = document.querySelector('button'); +const btn = document.querySelector('button'); btn.addEventListener('click', () => { let myDate; for(let i = 0; i < 10000000; i++) { @@ -75,7 +75,7 @@ btn.addEventListener('click', () => {
function expensiveOperation() { +function expensiveOperation() { for(let i = 0; i < 1000000; i++) { ctx.fillStyle = 'rgba(0,0,255, 0.2)'; ctx.beginPath(); @@ -102,24 +102,24 @@ alertBtn.addEventListener('click', () =>Под потоком, обычно, понимают одиночный процесс, который может использовать программа, для выполнения своих нужд. Каждый поток может выполнять только одну в текущий момент времени:
-Task A --> Task B --> Task C+Task A --> Task B --> Task CКаждая задача будет выполнена последовательно; только когда текущая задача завершится, следующая сможет начаться.
Как мы говорили выше, большинство компьютеров теперь имеют процессор с несколькими ядрами, т.е. могут выполнять несколько задач одновременно. Языки программирования, поддерживающие многопоточность, могут использовать несколько ядер, чтобы выполнять несколько задач одновременно:
-Thread 1: Task A --> Task B +Thread 1: Task A --> Task B Thread 2: Task C --> Task DJavaScript однопоточный
JavaScript, традиционно для скриптовых языков, однопоточный. Даже, если есть несколько ядер, вы можете использовать их только для выполнения задач в одном потоке, называемом основной поток. Наш пример выше, выполняется следующим образом:
-Main thread: Render circles to canvas --> Display alert()+Main thread: Render circles to canvas --> Display alert()В итоге, JavaScript получил несколько инструментов, которые могут помочь в решении подобных проблем. Web workers позволяют вам обработать некоторый JavaScript-код в отдельном потоке, который называется обработчик, таким образом вы можете запускать отдельные блоки JavaScript-кода одновременно. В основном, вы будете использовать воркеры, чтобы запустить ресурсоёмкий процесс, отдельно от основного потока, чтобы не блокировать действия пользователя.
-Main thread: Task A --> Task C +Main thread: Task A --> Task C Worker thread: Expensive task BПомня об этом, выполните наш следующий пример simple-sync-worker.html (посмотреть пример в действии), с открытой консолью. Это переписанный предыдущий пример, который теперь рассчитывает 10 миллионов дат в отдельном потоке обработчика. Теперь, когда вы нажимаете на кнопку, браузер может добавить новый элемент на страницу, до того как все даты будут посчитаны. Самая первая операция больше не блокирует выполнение следующей.
@@ -130,18 +130,18 @@ Worker thread: Expensive task BСледующая проблема заключается в том, что даже если код запущенный в воркере ничего не блокирует, он в целом остаётся синхронным. Это проблема появляется, когда какой-то функции требуются результаты выполнения нескольких предыдущих функций. Рассмотрим следующую диаграмму потоков:
-Main thread: Task A --> Task B+Main thread: Task A --> Task BВ этом примере, предположим Task A делает что-то вроде получения картинки с сервера а Task B затем делает что-нибудь с полученной картинкой, например, применяет к ней фильтр. Если запустить выполняться Task A и тут же попытаться выполнить Task B, то вы получите ошибку, поскольку картинка ещё не будет доступна.
-Main thread: Task A --> Task B --> |Task D| +Main thread: Task A --> Task B --> |Task D| Worker thread: Task C -----------> | |Теперь, давайте предположим, что Task D использует результат выполнения обеих задач Task B и Task C. Если мы уверенны, что оба результата будут доступны одновременно, тогда не возникнет проблем, однако, часто это не так. Если Task D попытаться запустить, когда какого-то нужного ей результата ещё нет, выполнение закончится ошибкой.
Чтобы избежать подобных проблем, браузеры позволяют нам выполнять определённые операции асинхронно. Такие возможности, как Promises позволяют запустить некоторую операцию (например, получение картинки с сервера), и затем подождать пока операция не вернёт результат, перед тем как начать выполнение другой задачи:
-Main thread: Task A Task B +Main thread: Task A Task B Promise: |__async operation__|Поскольку операция выполняется где-то отдельно, основной поток не блокируется, при выполнении асинхронных задач.
diff --git a/files/ru/learn/javascript/asynchronous/introducing/index.html b/files/ru/learn/javascript/asynchronous/introducing/index.html index 2d457b6888..0b53770834 100644 --- a/files/ru/learn/javascript/asynchronous/introducing/index.html +++ b/files/ru/learn/javascript/asynchronous/introducing/index.html @@ -36,7 +36,7 @@ translation_of: Learn/JavaScript/Asynchronous/IntroducingБольшая часть функциональности, которую мы рассматривали в предыдущих обучающих модулях, является синхронной — вы запускаете какой-то код, а результат возвращается, как только браузер может его вернуть. Давайте рассмотрим простой пример ( посмотрите онлайн, как это работает и посмотрите исходный код):
-const btn = document.querySelector('button'); +const btn = document.querySelector('button'); btn.addEventListener('click', () => { alert('You clicked me!'); @@ -65,7 +65,7 @@ btn.addEventListener('click', () => {Так и в примере выше: после нажатия кнопки абзац не сможет появиться пока не будет нажата кнопка OK в окне сообщения. Попробуйте сами:
-<button>Нажми меня</button>+<button>Нажми меня</button>{{EmbedLiveSample('Синхронный_JavaScript', '100%', '70px')}}
@@ -80,7 +80,7 @@ btn.addEventListener('click', () => {Почему трудно работать, используя синхронный код? Давайте посмотрим на небольшой пример. Когда вы получаете картинку с сервера, вы не можете мгновенно вернуть результат. Это значит что следующий (псевдо) код не сработает:
-let response = fetch('myImage.png'); +let response = fetch('myImage.png'); let blob = response.blob(); // display your image blob in the UI somehow@@ -94,7 +94,7 @@ let blob = response.blob();Пример асинхронного колбэка вторым параметром {{domxref("EventTarget.addEventListener", "addEventListener()")}} (как мы видели выше):
-btn.addEventListener('click', () => { +btn.addEventListener('click', () => { alert('You clicked me!'); let pElem = document.createElement('p'); @@ -108,7 +108,7 @@ let blob = response.blob();Вы можете написать свою собственную функцию, содержащую колбэк-функцию. Давайте взглянем на ещё один пример, в котором происходит загрузка ресурсов через
-XMLHttpRequest
API (запустите пример, и посмотрите исходный код):function loadAsset(url, type, callback) { +function loadAsset(url, type, callback) { let xhr = new XMLHttpRequest(); xhr.open('GET', url); xhr.responseType = type; @@ -136,7 +136,7 @@ loadAsset('coffee.jpg', 'blob', displayImage);Заметьте, что не все колбэк-функции асинхронны — некоторые запускаются синхронно. Например, при использовании {{jsxref("Array.prototype.forEach()")}} для перебора элементов массива (запустите пример, и посмотрите исходный код):
-const gods = ['Apollo', 'Artemis', 'Ares', 'Zeus']; +const gods = ['Apollo', 'Artemis', 'Ares', 'Zeus']; gods.forEach(function (eachName, index){ console.log(index + '. ' + eachName); @@ -148,7 +148,7 @@ gods.forEach(function (eachName, index){Промисы — новый стиль написания асинхронного кода, который используется в современных Web API. Хорошим примером является
-fetch()
API, который современнее и эффективнее чем {{domxref("XMLHttpRequest")}}. Посмотрим на краткий пример, из нашей статьи Fetching data from the server:fetch('products.json').then(function(response) { +fetch('products.json').then(function(response) { return response.json(); }).then(function(json) { products = json; @@ -195,7 +195,7 @@ gods.forEach(function (eachName, index){Давайте рассмотрим пример, который дополнительно иллюстрирует природу асинхронного кода, показывая, что может произойти, когда мы не полностью осознаем порядок выполнения кода, и проблемы, связанные с попыткой трактовать асинхронный код как синхронный. Следующий пример довольно похож на тот, что мы видели раньше (запустите пример, и посмотреть исходный код). Одно из отличий состоит в том, что мы включили ряд операторов {{domxref("console.log()")}} чтобы проиллюстрировать порядок, в котором, как вы думаете, будет выполняться код.
-console.log ('Starting'); +console.log ('Starting'); let image; fetch('coffee.jpg').then((response) => { @@ -226,7 +226,7 @@ console.log ('All done!');Если вы запутались, рассмотрим следующий небольшой пример:
-console.log("registering click handler"); +console.log("registering click handler"); button.addEventListener('click', () => { console.log("get click"); @@ -240,11 +240,11 @@ console.log("all done");Чтобы увидеть это в действии, попробуйте взять локальную копию нашего примера и измените третий вызов
-console.log ()
следующим образом:console.log ('All done! ' + image + 'displayed.');+console.log ('All done! ' + image + 'displayed.');Теперь вместо третьего сообщения должна возникнуть следующая ошибка:
-+
Это происходит потому, что в то же время браузер пытается запустить третий
diff --git a/files/ru/learn/javascript/asynchronous/timeouts_and_intervals/index.html b/files/ru/learn/javascript/asynchronous/timeouts_and_intervals/index.html index 8d00ba98af..19019a19a9 100644 --- a/files/ru/learn/javascript/asynchronous/timeouts_and_intervals/index.html +++ b/files/ru/learn/javascript/asynchronous/timeouts_and_intervals/index.html @@ -64,13 +64,13 @@ original_slug: Learn/JavaScript/Asynchronous/Таймауты_и_интерваconsole.log()
, блокfetch()
ещё не закончил выполнение, поэтому переменнаяimage
ещё не имеет значения.В следующем примере, браузер будет ожидать две секунды перед тем как выполнит анонимную функцию, тогда отобразит сообщение (живой пример, и исходный код):
-let myGreeting = setTimeout(function() { +let myGreeting = setTimeout(function() { alert('Hello, Mr. Universe!'); }, 2000)Указанные вами функции не обязательно должны быть анонимными. Вы можете дать своей функции имя и даже определить её где-нибудь ещё и передать ссылку на функцию в setTimeout (). Следующие две версии фрагмента кода эквивалентны первой:
-// С именованной функцией +// С именованной функцией let myGreeting = setTimeout(function sayHi() { alert('Hello, Mr. Universe!'); }, 2000) @@ -92,19 +92,19 @@ let myGreeting = setTimeout(sayHi, 2000);Например, вы можете реорганизовать предыдущую функцию, чтобы она передавала привет любому имени, переданному ей:
-function sayHi(who) { +function sayHi(who) { alert(`Hello ${who}!`); }Теперь вы можете передать имя в вызов setTimeout () в качестве третьего параметра:
-let myGreeting = setTimeout(sayHi, 2000, 'Mr. Universe');+let myGreeting = setTimeout(sayHi, 2000, 'Mr. Universe');Очистка таймаутов
Наконец, если был создан тайм-аут, вы можете отменить его до истечения указанного времени, вызвав
-clearTimeout()
, передав ему идентификатор вызоваsetTimeout()
в качестве параметра. Итак, чтобы отменить указанный выше тайм-аут, вы должны сделать следующее:clearTimeout(myGreeting);+clearTimeout(myGreeting);Note: См.
@@ -118,7 +118,7 @@ let myGreeting = setTimeout(sayHi, 2000);greeter-app.html
для более полной демонстрации, которая позволяет вам указать имя для приветствия и отменить приветствие с помощью отдельной кнопки (см. исходный код).Давайте посмотрим на пример. Следующая функция создаёт новый объект
-Date()
, с помощьюtoLocaleTimeString()
извлекает из него строку с временем и отображает её в пользовательском интерфейсе. Затем он запускает функцию один раз в секунду с помощьюsetInterval()
, создавая эффект цифровых часов, которые обновляются раз в секунду ( реальный пример, и исходный код):function displayTime() { +function displayTime() { let date = new Date(); let time = date.toLocaleTimeString(); document.getElementById('demo').textContent = time; @@ -132,7 +132,7 @@ const createClock = setInterval(displayTime, 1000);-
setInterval ()
выполняет задачу постоянно. setInterval () продолжает выполнять задачу вечно, если вы что-то с ней не сделаете. Возможно, вам понадобится способ остановить такие задачи, иначе вы можете получить ошибки, если браузер не сможет выполнить какие-либо другие версии задачи или если анимация, обрабатываемая задачей, завершилась. Вы можете сделать это так же, как останавливаетеtimeouts
- передавая идентификатор, возвращаемый вызовомsetInterval ()
, в функциюclearInterval ()
:const myInterval = setInterval(myFunction, 2000); +const myInterval = setInterval(myFunction, 2000); clearInterval(myInterval);@@ -182,7 +182,7 @@ clearInterval(myInterval);В приведённом ниже примере используется рекурсивный setTimeout () для запуска переданной функции каждые 100 миллисекунд:
-let i = 1; +let i = 1; setTimeout(function run() { console.log(i); @@ -192,7 +192,7 @@ setTimeout(function run() {Сравните приведённый выше пример со следующим - здесь используется
-setInterval ()
для достижения того же эффекта:let i = 1; +let i = 1; setInterval(function run() { console.log(i); @@ -217,7 +217,7 @@ setInterval(function run() {Например, код приведённый ниже (рабочий код) выводит alert содержащий
-"Hello"
, затем alert содержащий"World"
как только вы нажмёте ОК в первом alert.setTimeout(function() { +setTimeout(function() { alert('World'); }, 0); @@ -243,7 +243,7 @@ alert('Hello');Метод принимает в качестве аргумента колбэк, который должен быть вызван перед перерисовкой. Это общий шаблон, в котором он используется:
-function draw() { +function draw() { // Drawing code goes here requestAnimationFrame(draw); } @@ -272,7 +272,7 @@ draw();Давайте поговорим ещё немного о том, чем метод
-requestAnimationFrame ()
отличается от других методов, используемых ранее. Глядя на наш код сверху:function draw() { +function draw() { // Drawing code goes here requestAnimationFrame(draw); } @@ -281,7 +281,7 @@ draw();Такой же код с использованием
-setInterval()
:function draw() { +function draw() { // Drawing code goes here } @@ -297,7 +297,7 @@ setInterval(draw, 17);Это полезно, поскольку позволяет запускать вещи в определённое время и в постоянном темпе, независимо от того, насколько быстрым или медленным может быть ваше устройство. Общий шаблон, который вы бы использовали, выглядит примерно так:
-let startTime = null; +let startTime = null; function draw(timestamp) { if (!startTime) { @@ -337,7 +337,7 @@ draw();Примените следующий CSS к HTML шаблону (любым предпочитаемым способом). Он установ красный фон на странице, высоту
-<body>
равную100%
высоты {{htmlelement("html")}} , и центрирует<div>
внутри<body>
, по горизонтали и вертикали.html { +html { background-color: white; height: 100%; } @@ -362,7 +362,7 @@ div {Разместите следующий JavaScript-код в
-<script>
. Здесь вы сохраняете ссылку на<div>
внутри, устанавливаете для переменнойrotateCount
значение0
, устанавливаете неинициализированную переменную, которая позже будет использоваться для хранения ссылки на вызовrequestAnimationFrame()
, и устанавливаете для переменнойstartTime
значениеnull
, которая будет позже использоваться для хранения времени началаrequestAnimationFrame()
.const spinner = document.querySelector('div'); +const spinner = document.querySelector('div'); let rotateCount = 0; let startTime = null; let rAF; @@ -371,14 +371,14 @@ let rAF;Под предыдущим кодом вставьте функцию
-draw()
которая будет использоваться для хранения нашего кода анимации, который включает параметрtimestamp
:function draw(timestamp) { +function draw(timestamp) { }Внутри
-draw ()
добавьте следующие строки. Они определят время начала, если оно ещё не определено (это произойдёт только на первой итерации цикла), и установят для параметраrotateCount
значение для поворота счётчика (текущая временная метка, возьмите начальную временную метку, разделённую на три, чтобы замедлиться):if (!startTime) { +if (!startTime) { startTime = timestamp; } @@ -388,22 +388,22 @@ let rAF;Под предыдущей строкой внутри
-draw ()
добавьте следующий блок - он проверяет, превышает ли значениеrotateCount 359
(например,360
, полный круг). Если это так, он устанавливает значение по модулю360
(то есть остаток, оставшийся после деления значения на360
), поэтому круговая анимация может продолжаться непрерывно с разумным низким значением. Обратите внимание, что это не является строго необходимым, но легче работать со значениями от 0 до359
градусов, чем со значениями типа «128000
градусов».if (rotateCount > 359) { +if (rotateCount > 359) { rotateCount %= 360; }Затем, под предыдущим блоком, добавьте следующую строку, чтобы вращать spinner: - spinner.style.transform = `rotate(${rotateCount}deg)`;+spinner.style.transform = `rotate(${rotateCount}deg)`;В самом низу внутри функции draw () вставьте следующую строку. Это ключ ко всей операции - вы устанавливаете для переменной, определённой ранее, активный вызов requestAnimation (), который принимает функцию draw () в качестве своего параметра. Это запускает анимацию, постоянно выполняя функцию draw () со скоростью, близкой к 60 FPS.
-rAF = requestAnimationFrame(draw);+rAF = requestAnimationFrame(draw);@@ -417,7 +417,7 @@ let rAF; Ниже, вызовите функцию
-draw()
для запуска анимации.draw();+draw();Просто передайте ему значение, возвращаемое вызовом requestAnimationFrame () для отмены, которое вы сохранили в переменной rAF:
-cancelAnimationFrame(rAF);+cancelAnimationFrame(rAF);Активное обучение: запуск и остановка нашей анимации
@@ -444,7 +444,7 @@ let rAF;В этом примере вы должны анимировать как положение персонажа на экране, так и отображаемый спрайт. В анимации спрайта всего 6 кадров. Если бы вы показывали разные кадры спрайта для каждого кадра, отображаемого на экране, с помощью requestAnimationFrame (), Guybrush двигал бы конечностями слишком быстро, и анимация выглядела бы нелепо. Следовательно, в этом примере регулируется скорость, с которой спрайт циклически повторяет свои кадры, используя следующий код:
-if (posX % 13 === 0) { +if (posX % 13 === 0) { if (sprite === 5) { sprite = 0; } else { @@ -456,7 +456,7 @@ let rAF;... Фактически, это примерно каждые 6,5 кадров, поскольку мы обновляем posX (положение персонажа на экране) на два кадра:
-if (posX > width/2) { +if (posX > width/2) { newStartPos = -( (width/2) + 102 ); posX = Math.ceil(newStartPos / 13) * 13; console.log(posX); @@ -485,7 +485,7 @@ let rAF;Внутри пустого элемента {{htmlelement("script")}} на вашей странице, начните с добавления следующих строк кода, которые определяют некоторые переменные и константы, которые вам понадобятся в дальнейшем:
-const spinner = document.querySelector('.spinner p'); +const spinner = document.querySelector('.spinner p'); const spinnerContainer = document.querySelector('.spinner'); let rotateCount = 0; let startTime = null; @@ -508,7 +508,7 @@ const result = document.querySelector('.result');Ниже добавьте следующую функцию. Она просто берёт два числа и возвращает случайное число между ними. Это понадобится вам позже, чтобы сгенерировать случайный интервал ожидания.
-function random(min,max) { +function random(min,max) { var num = Math.floor(Math.random()*(max-min)) + min; return num; }@@ -516,7 +516,7 @@ const result = document.querySelector('.result');Затем добавьте функцию draw(), которая анимирует спиннер. Это очень похоже на версию из предыдущего примера простого счётчика:
-function draw(timestamp) { +function draw(timestamp) { if(!startTime) { startTime = timestamp; } @@ -534,13 +534,13 @@ const result = document.querySelector('.result');Теперь пришло время настроить начальное состояние приложения при первой загрузке страницы. Добавьте следующие две строки, которые просто скрывают абзац результатов и контейнер счётчика с помощью
-display: none
;.result.style.display = 'none'; +result.style.display = 'none'; spinnerContainer.style.display = 'none';Затем определите функцию
-reset ()
, которая возвращает приложение в исходное состояние, необходимое для повторного запуска игры после её завершения. Добавьте в конец кода следующее:function reset() { +function reset() { btn.style.display = 'block'; result.textContent = ''; result.style.display = 'none'; @@ -549,7 +549,7 @@ spinnerContainer.style.display = 'none';Хорошо, хватит подготовки! Пришло время сделать игру доступной! Добавьте в свой код следующий блок. Функция
-start ()
вызываетdraw ()
, чтобы запустить вращение спиннера и отобразить его в пользовательском интерфейсе, скрыть кнопку Start, чтобы вы не могли испортить игру, запустив её несколько раз одновременно, и запускает вызовsetTimeout ()
, который выполняется функцияsetEndgame ()
по прошествии случайного интервала от 5 до 10 секунд. Следующий блок также добавляет обработчик событий к вашей кнопке для запуска функцииstart ()
при её нажатии.btn.addEventListener('click', start); +btn.addEventListener('click', start); function start() { draw(); @@ -569,7 +569,7 @@ function start() {Добавьте в свой код следующую функцию:
-function setEndgame() { +function setEndgame() { cancelAnimationFrame(rAF); spinnerContainer.style.display = 'none'; result.style.display = 'block'; -- cgit v1.2.3-54-g00ecf