diff --git a/1-js/2-first-steps/5-variables/variable-change.svg b/1-js/2-first-steps/5-variables/variable-change.svg index b28c49be..472eb447 100644 --- a/1-js/2-first-steps/5-variables/variable-change.svg +++ b/1-js/2-first-steps/5-variables/variable-change.svg @@ -11,13 +11,13 @@ - + "World!" - + "Hello!" diff --git a/1-js/2-first-steps/5-variables/variable.svg b/1-js/2-first-steps/5-variables/variable.svg index 44fa8557..2199e553 100644 --- a/1-js/2-first-steps/5-variables/variable.svg +++ b/1-js/2-first-steps/5-variables/variable.svg @@ -12,7 +12,7 @@ - + "Hello!" diff --git a/1-js/3-writing-js/2-coding-style/code-style.svg b/1-js/3-writing-js/2-coding-style/code-style.svg index 85eaf5f2..4eeecc6c 100644 --- a/1-js/3-writing-js/2-coding-style/code-style.svg +++ b/1-js/3-writing-js/2-coding-style/code-style.svg @@ -22,65 +22,65 @@ Между именем функции и скобкой ( нет пробела - - + + Отступ 2 пробела - - + + Пробел после for - - + + } else { без перевода строки - - + + Пробелы вокруг вложенного вызова - - - - + + + + пустая строка между логическими блоками - - + + длина строки не более 80 символов - - + + точка с запятой ; обязательна - - + + Фигурная скобка { на той же строке, через пробел - - - - + + + + Пробел между параметрами - - + + Пробел между параметрами diff --git a/1-js/4-data-structures/4-object/object-person-empty.svg b/1-js/4-data-structures/4-object/object-person-empty.svg index 20b239c9..29ef8bf6 100644 --- a/1-js/4-data-structures/4-object/object-person-empty.svg +++ b/1-js/4-data-structures/4-object/object-person-empty.svg @@ -13,8 +13,8 @@ person - - + + \ No newline at end of file diff --git a/1-js/4-data-structures/6-object-reference/variable-copy-value.svg b/1-js/4-data-structures/6-object-reference/variable-copy-value.svg index f28a7c20..8892af98 100644 --- a/1-js/4-data-structures/6-object-reference/variable-copy-value.svg +++ b/1-js/4-data-structures/6-object-reference/variable-copy-value.svg @@ -12,7 +12,7 @@ - + "Привет!" @@ -27,7 +27,7 @@ - + "Привет!" diff --git a/1-js/4-data-structures/7-array/array-speed.svg b/1-js/4-data-structures/7-array/array-speed.svg index d6d5b45f..227243c6 100644 --- a/1-js/4-data-structures/7-array/array-speed.svg +++ b/1-js/4-data-structures/7-array/array-speed.svg @@ -8,18 +8,18 @@ - + 0 - + 1 - + 2 - + 3 diff --git a/1-js/4-data-structures/8-array-methods/9-output-single-linked-list/linked-list.svg b/1-js/4-data-structures/8-array-methods/9-output-single-linked-list/linked-list.svg index 5756498b..cb49665d 100644 --- a/1-js/4-data-structures/8-array-methods/9-output-single-linked-list/linked-list.svg +++ b/1-js/4-data-structures/8-array-methods/9-output-single-linked-list/linked-list.svg @@ -13,8 +13,8 @@ 1 - - + + next @@ -25,8 +25,8 @@ 2 - - + + next @@ -37,8 +37,8 @@ 3 - - + + next @@ -49,8 +49,8 @@ 4 - - + + next diff --git a/1-js/4-data-structures/9-array-iteration/reduce.svg b/1-js/4-data-structures/9-array-iteration/reduce.svg index 0c48980e..aadaaeae 100644 --- a/1-js/4-data-structures/9-array-iteration/reduce.svg +++ b/1-js/4-data-structures/9-array-iteration/reduce.svg @@ -56,8 +56,8 @@ current 5 - - + + 0+1+2+3+4+5 = 15 diff --git a/1-js/5-functions-closures/6-memory-management/family-no-family.svg b/1-js/5-functions-closures/6-memory-management/family-no-family.svg index 57f9ef73..8a6b18b5 100644 --- a/1-js/5-functions-closures/6-memory-management/family-no-family.svg +++ b/1-js/5-functions-closures/6-memory-management/family-no-family.svg @@ -6,8 +6,8 @@ - - + + window @@ -42,15 +42,15 @@ Object - - - - + + + + husband - - + + family: null diff --git a/1-js/5-functions-closures/6-memory-management/family-no-father-2.svg b/1-js/5-functions-closures/6-memory-management/family-no-father-2.svg index 82b1993a..19f25a06 100644 --- a/1-js/5-functions-closures/6-memory-management/family-no-father-2.svg +++ b/1-js/5-functions-closures/6-memory-management/family-no-father-2.svg @@ -30,10 +30,10 @@ Object - - - - + + + + \ No newline at end of file diff --git a/1-js/5-functions-closures/6-memory-management/family-no-father.svg b/1-js/5-functions-closures/6-memory-management/family-no-father.svg index f26e7e8f..3225b007 100644 --- a/1-js/5-functions-closures/6-memory-management/family-no-father.svg +++ b/1-js/5-functions-closures/6-memory-management/family-no-father.svg @@ -40,12 +40,12 @@ Object - - - - - - + + + + + + diff --git a/1-js/5-functions-closures/6-memory-management/family.svg b/1-js/5-functions-closures/6-memory-management/family.svg index 80f53e05..0f34310c 100644 --- a/1-js/5-functions-closures/6-memory-management/family.svg +++ b/1-js/5-functions-closures/6-memory-management/family.svg @@ -6,8 +6,8 @@ - - + + window @@ -45,17 +45,17 @@ Object - - - - - - + + + + + + husband - - + + \ No newline at end of file diff --git a/1-js/7-js-misc/3-setTimeout-setInterval/setinterval-interval.svg b/1-js/7-js-misc/3-setTimeout-setInterval/setinterval-interval.svg index 820dc80e..aa5e3ab9 100644 --- a/1-js/7-js-misc/3-setTimeout-setInterval/setinterval-interval.svg +++ b/1-js/7-js-misc/3-setTimeout-setInterval/setinterval-interval.svg @@ -11,19 +11,19 @@ func(1) - + func(2) - + func(3) - + 100 diff --git a/1-js/7-js-misc/3-setTimeout-setInterval/settimeout-interval.svg b/1-js/7-js-misc/3-setTimeout-setInterval/settimeout-interval.svg index 512dbf7e..f0895fed 100644 --- a/1-js/7-js-misc/3-setTimeout-setInterval/settimeout-interval.svg +++ b/1-js/7-js-misc/3-setTimeout-setInterval/settimeout-interval.svg @@ -11,19 +11,19 @@ func(1) - + func(2) - + func(3) - + @@ -33,16 +33,16 @@ 100 - - - - - - - - - - + + + + + + + + + + \ No newline at end of file diff --git a/1-js/9-prototypes/1-prototype/proto-animal-rabbit.svg b/1-js/9-prototypes/1-prototype/proto-animal-rabbit.svg index d6bf194d..5acdf9e3 100644 --- a/1-js/9-prototypes/1-prototype/proto-animal-rabbit.svg +++ b/1-js/9-prototypes/1-prototype/proto-animal-rabbit.svg @@ -20,8 +20,8 @@ rabbit - - + + __proto__ diff --git a/1-js/9-prototypes/3-native-prototypes/native-prototypes-array-tostring.svg b/1-js/9-prototypes/3-native-prototypes/native-prototypes-array-tostring.svg index 998745f2..4ad00ffa 100644 --- a/1-js/9-prototypes/3-native-prototypes/native-prototypes-array-tostring.svg +++ b/1-js/9-prototypes/3-native-prototypes/native-prototypes-array-tostring.svg @@ -23,13 +23,13 @@ Object.prototype - - + + __proto__ - - + + __proto__ diff --git a/1-js/9-prototypes/3-native-prototypes/native-prototypes-classes.svg b/1-js/9-prototypes/3-native-prototypes/native-prototypes-classes.svg index f4dc1d90..b24345b7 100644 --- a/1-js/9-prototypes/3-native-prototypes/native-prototypes-classes.svg +++ b/1-js/9-prototypes/3-native-prototypes/native-prototypes-classes.svg @@ -14,13 +14,13 @@ Object.prototype - - + + __proto__ - - + + null @@ -57,10 +57,10 @@ __proto__ - - - - + + + + [1, 2, 3] @@ -75,18 +75,18 @@ 5 - - + + __proto__ - - + + __proto__ - - + + __proto__ diff --git a/1-js/9-prototypes/3-native-prototypes/native-prototypes-object.svg b/1-js/9-prototypes/3-native-prototypes/native-prototypes-object.svg index 898b906e..8b9d9fef 100644 --- a/1-js/9-prototypes/3-native-prototypes/native-prototypes-object.svg +++ b/1-js/9-prototypes/3-native-prototypes/native-prototypes-object.svg @@ -18,13 +18,13 @@ obj - - + + __proto__ - - + + __proto__ diff --git a/1-js/9-prototypes/5-class-inheritance/class-inheritance-array-object.svg b/1-js/9-prototypes/5-class-inheritance/class-inheritance-array-object.svg index 8de1c1cd..731ad706 100644 --- a/1-js/9-prototypes/5-class-inheritance/class-inheritance-array-object.svg +++ b/1-js/9-prototypes/5-class-inheritance/class-inheritance-array-object.svg @@ -26,13 +26,13 @@ Object.prototype - - + + __proto__ - - + + __proto__ diff --git a/1-js/9-prototypes/5-class-inheritance/class-inheritance-rabbit-animal.svg b/1-js/9-prototypes/5-class-inheritance/class-inheritance-rabbit-animal.svg index 7beab7e7..77f4dc66 100644 --- a/1-js/9-prototypes/5-class-inheritance/class-inheritance-rabbit-animal.svg +++ b/1-js/9-prototypes/5-class-inheritance/class-inheritance-rabbit-animal.svg @@ -25,13 +25,13 @@ Animal.prototype - - + + __proto__ - - + + __proto__ diff --git a/1-js/9-prototypes/5-class-inheritance/class-inheritance-rabbit-run-animal.svg b/1-js/9-prototypes/5-class-inheritance/class-inheritance-rabbit-run-animal.svg index ccfa42d6..3a6bcf7e 100644 --- a/1-js/9-prototypes/5-class-inheritance/class-inheritance-rabbit-run-animal.svg +++ b/1-js/9-prototypes/5-class-inheritance/class-inheritance-rabbit-run-animal.svg @@ -27,13 +27,13 @@ Animal.prototype - - + + __proto__ - - + + __proto__ diff --git a/10-regular-expressions-javascript/1-regexp-introduction/article.md b/10-regular-expressions-javascript/1-regexp-introduction/article.md index 28a288b5..cce32c4e 100644 --- a/10-regular-expressions-javascript/1-regexp-introduction/article.md +++ b/10-regular-expressions-javascript/1-regexp-introduction/article.md @@ -15,20 +15,20 @@ var regexp = new RegExp("шаблон", "флаги"); ``` -Как правило, используют более короткую запись через слеши `/`: +Как правило, используют более короткую запись: шаблон внутри слешей `"/"`: ```js var regexp = /шаблон/; // без флагов var regexp = /шаблон/gmi; // с флагами gmi (изучим их дальше) ``` -Слэши `'/'` говорят JavaScript о том, что это регулярное выражение. Они играют здесь ту же роль, что и кавычки для обозначения строк. +Слэши `"/"` говорят JavaScript о том, что это регулярное выражение. Они играют здесь ту же роль, что и кавычки для обозначения строк. ## Использование Основа регулярного выражения -- паттерн. Это строка, которую можно расширить специальными символами, которые делают поиск намного мощнее. -Если флагов и специальных символов нет, то поиск по паттерну -- то же самое, что и обычный поиск подстроки: +В простейшем случае, если флагов и специальных символов нет, поиск по паттерну -- то же самое, что и обычный поиск подстроки: ```js //+ run @@ -48,7 +48,9 @@ var substr = "лю"; alert( str.indexOf(substr) ); // 2 ``` -Как видим, то же самое, разве что для регэкспа использован метод [:String#search(reg)], а для строки [:String#indexOf(substr)]. Но это соответствие лишь кажущееся. Очень скоро мы усложним регулярные выражения, и тогда появятся отличия. +Как видим, то же самое, разве что для регэкспа использован метод [search](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/String/search) -- он как раз работает с регулярными выражениями, а для подстроки -- [indexOf](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/String/indexOf). + +Но это соответствие лишь кажущееся. Очень скоро мы усложним регулярные выражения, и тогда увидим, что они гораздо мощнее. [smart header="Цветовые обозначения"] Здесь и далее используется следующая цветовая схема: diff --git a/2-ui/1-document/16-metrics-window/document-client-width-height.svg b/2-ui/1-document/16-metrics-window/document-client-width-height.svg index b88a533f..bf16af6c 100644 --- a/2-ui/1-document/16-metrics-window/document-client-width-height.svg +++ b/2-ui/1-document/16-metrics-window/document-client-width-height.svg @@ -22,12 +22,12 @@ document.documentElement.clientWidth - - - - - - + + + + + + \ No newline at end of file diff --git a/2-ui/1-document/17-coordinates/coords.svg b/2-ui/1-document/17-coordinates/coords.svg index a86d910c..f91fe800 100644 --- a/2-ui/1-document/17-coordinates/coords.svg +++ b/2-ui/1-document/17-coordinates/coords.svg @@ -33,18 +33,18 @@ right - - - - - - - - - - - - + + + + + + + + + + + + \ No newline at end of file diff --git a/2-ui/1-document/4-traversing-dom/dom-links-elements.svg b/2-ui/1-document/4-traversing-dom/dom-links-elements.svg index 104c271c..7fc062fe 100644 --- a/2-ui/1-document/4-traversing-dom/dom-links-elements.svg +++ b/2-ui/1-document/4-traversing-dom/dom-links-elements.svg @@ -26,17 +26,17 @@ <DIV> - - - - + + + + next Element Sibling - - + + previous Element diff --git a/2-ui/1-document/4-traversing-dom/dom-links.svg b/2-ui/1-document/4-traversing-dom/dom-links.svg index c3e1cb78..2a5be39f 100644 --- a/2-ui/1-document/4-traversing-dom/dom-links.svg +++ b/2-ui/1-document/4-traversing-dom/dom-links.svg @@ -31,15 +31,15 @@ <DIV> - - - - + + + + nextSibling - - + + previousSibling diff --git a/2-ui/2-events-and-interfaces/4-event-bubbling/event-order-bubbling.svg b/2-ui/2-events-and-interfaces/4-event-bubbling/event-order-bubbling.svg index 4a1fbbaf..14715f8a 100644 --- a/2-ui/2-events-and-interfaces/4-event-bubbling/event-order-bubbling.svg +++ b/2-ui/2-events-and-interfaces/4-event-bubbling/event-order-bubbling.svg @@ -19,9 +19,9 @@ 3 - - Самый глубокий - элемент + + Самый глубокий + элемент diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-from-outside.svg b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-from-outside.svg index 59fc73bb..fe29eca4 100644 --- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-from-outside.svg +++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-from-outside.svg @@ -18,7 +18,7 @@ #TO - + target diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-over-elems.svg b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-over-elems.svg index 9ff425b1..d9e9fad1 100644 --- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-over-elems.svg +++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-over-elems.svg @@ -26,7 +26,7 @@ <DIV> - + mouseover diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout.svg b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout.svg index 958e581f..8498bf2f 100644 --- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout.svg +++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout.svg @@ -10,11 +10,11 @@ <DIV> - + mouseover - + mouseoout diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-to-child.svg b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-to-child.svg index cd1569de..5a8f9f00 100644 --- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-to-child.svg +++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-to-child.svg @@ -14,7 +14,7 @@ #TO - + mouseover diff --git a/4-ajax/4-xhr-forms/article.md b/4-ajax/4-xhr-forms/article.md index e0059aaf..e54b8d90 100644 --- a/4-ajax/4-xhr-forms/article.md +++ b/4-ajax/4-xhr-forms/article.md @@ -48,10 +48,7 @@ alert( encodeURIComponent('В') ); // %D0%92 alert( encodeURIComponent('Виктор') ); // %D0%92%D0%B8%D0%BA%D1%82%D0%BE%D1%80 ``` -Формы с `method="GET"` всегда кодируются указанным образом. - -Для `method="POST"` существует альтернативная кодировка, которую мы рассмотрим позже. - +Эта кодировка используется в основном для метода GET, то есть для передачи параметра в строке запроса. По стандарту строка запроса не может содержать произвольные Unicode-символы, поэтому они кодируются как показано выше. ## GET-запрос @@ -92,11 +89,23 @@ xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); ## POST с urlencoded -В методе POST параметры передаются не в URL, а в теле, посылаемом через `send(body)`. Поэтому `params` нужно указывать не в `open`, а в `send`. +В методе POST параметры передаются не в URL, а в теле запроса. Оно указывается в вызове `send(body)`. -Кроме того, при POST обязателен заголовок `Content-Type`, содержащий кодировку. Это указание для сервера -- как обрабатывать (раскодировать) пришедший запрос. +В стандартных HTTP-формах для метода POST доступны [три кодировки](https://html.spec.whatwg.org/multipage/forms.html#submit-body), задаваемые через атрибут `enctype`: + + +В зависимости от `enctype` браузер кодирует данные соответствующим способом перед отправкой на сервер. + +В случае с `XMLHttpRequest` мы, вообще говоря, не обязаны использовать ни один из этих способов. Главное, чтобы сервер наш запрос понял. Но обычно проще всего выбрать какой-то из стандартных. + +В частности, при POST обязателен заголовок `Content-Type`, содержащий кодировку. Это указание для сервера -- как обрабатывать (раскодировать) пришедший запрос. + +Для примера отправим запрос в кодировке `application/x-www-form-urlencoded`: -Полный код для POST: ```js var xhr = new XMLHttpRequest(); diff --git a/4-ajax/5-xhr-crossdomain/xhr-another-domain.svg b/4-ajax/5-xhr-crossdomain/xhr-another-domain.svg index ee815012..7a11b490 100644 --- a/4-ajax/5-xhr-crossdomain/xhr-another-domain.svg +++ b/4-ajax/5-xhr-crossdomain/xhr-another-domain.svg @@ -21,13 +21,13 @@ - - + + send() - - + + HTTP-запрос с заголовком Origin diff --git a/4-ajax/5-xhr-crossdomain/xhr-preflight.svg b/4-ajax/5-xhr-crossdomain/xhr-preflight.svg index 687a5c18..86d951d4 100644 --- a/4-ajax/5-xhr-crossdomain/xhr-preflight.svg +++ b/4-ajax/5-xhr-crossdomain/xhr-preflight.svg @@ -21,8 +21,8 @@ - - + + send() @@ -32,8 +32,8 @@ Access-Control-Request-Method Access-Control-Request-Headers - - + + 200 OK Access-Control-Allow-Method @@ -41,8 +41,8 @@ Access-Control-Max-Age - - + + @@ -56,8 +56,8 @@ если сервер разрешил: onload иначе onerror - - + + Основной HTTP-запрос Origin diff --git a/5-animation/1-bezier/article.md b/5-animation/1-bezier/article.md index 5d7cf395..13c91881 100644 --- a/5-animation/1-bezier/article.md +++ b/5-animation/1-bezier/article.md @@ -4,9 +4,8 @@ Несмотря на "умное" название -- это очень простая штука. -В принципе, можно создавать анимацию и без знания кривых Безье, но стоит один раз прочитать, что это такое, так как в векторной графике и продвинутых анимаций без них никак. Это образовательный минимум. +В принципе, можно создавать анимацию и без знания кривых Безье, но стоит один раз изучить эту тему хотя бы для того, чтобы в дальнейшем с комфортом пользоваться этим замечательным инструментом. Тем более что в мире векторной графики и продвинутых анимаций без них никак. -Тему эту стоит изучить один раз, чтобы в дальнейшем с комфортом пользоваться этим замечательным инструментом. [cut] ## Виды кривых Безье @@ -17,15 +16,15 @@ По двум точкам: - + По трём точкам: - + По четырём точкам: - + Если вы посмотрите внимательно на эти кривые, то "на глазок" заметите: @@ -35,7 +34,7 @@ Для двух точек -- это линейная кривая (т.е. прямая), для трёх точек -- квадратическая кривая (парабола), для четырёх -- кубическая.
  • **Кривая всегда находится внутри [выпуклой оболочки](http://ru.wikipedia.org/wiki/%D0%92%D1%8B%D0%BF%D1%83%D0%BA%D0%BB%D0%B0%D1%8F_%D0%BE%D0%B1%D0%BE%D0%BB%D0%BE%D1%87%D0%BA%D0%B0), образованной опорными точками:** - + Благодаря последнему свойству в компьютерной графике можно оптимизировать проверку пересечений двух кривых. Если их выпуклые оболочки не пересекаются, то и кривые тоже не пересекутся.
  • @@ -53,7 +52,7 @@ Вот некоторые примеры: - + ## Математика @@ -122,8 +121,8 @@ - - + +
    При `t=0.25`При `t=0.5`
    @@ -136,7 +135,8 @@ По мере того как `t` пробегает последовательность от `0` до `1`, каждое значение `t` добавляет к кривой точку. **Совокупность таких точек для всех значений `t` образуют кривую Безье.** -**Это был процесс для построения по трём точкам. Но то же самое происходит и с четырьмя точками.** + +Это был процесс для построения по трём точкам. Но то же самое происходит и с четырьмя точками. Демо для четырёх точек (точки можно двигать): @@ -173,15 +173,18 @@ Аналогичным образом могут быть построены кривые Безье и более высокого порядка: по пяти точкам, шести и так далее. Но обычно используются 2-3 точки, а для сложных линий несколько кривых соединяются. Это гораздо проще с точки зрения поддержки и расчётов. [smart header="Как провести кривую Безье через нужные точки?"] -Кривые Безье обычно проводятся через "опорные" точки, которые, как можно видеть из примеров выше, редко лежат на кривой. +Кривые Безье обычно проводятся при помощи "опорных" точек, из которых, как можно видеть из примеров выше, только первая и последняя лежат на кривой. -Если нужно провести кривую именно через нужные точки, то это уже другая задача. Она называется [интерполяцией](http://ru.wikipedia.org/wiki/%D0%98%D0%BD%D1%82%D0%B5%D1%80%D0%BF%D0%BE%D0%BB%D1%8F%D1%86%D0%B8%D1%8F). Существуют математические формулы, которые подбирают коэффициенты кривой по точкам, исходя из требований, например [многочлен Лагранжа](http://ru.wikipedia.org/wiki/%D0%98%D0%BD%D1%82%D0%B5%D1%80%D0%BF%D0%BE%D0%BB%D1%8F%D1%86%D0%B8%D0%BE%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BC%D0%BD%D0%BE%D0%B3%D0%BE%D1%87%D0%BB%D0%B5%D0%BD_%D0%9B%D0%B0%D0%B3%D1%80%D0%B0%D0%BD%D0%B6%D0%B0). +Если нужно провести кривую именно *через* нужные точки, то это уже другая задача, она называется [интерполяцией](http://ru.wikipedia.org/wiki/%D0%98%D0%BD%D1%82%D0%B5%D1%80%D0%BF%D0%BE%D0%BB%D1%8F%D1%86%D0%B8%D1%8F). Существуют математические формулы, которые подбирают коэффициенты кривой по точкам, исходя из требований, например [многочлен Лагранжа](http://ru.wikipedia.org/wiki/%D0%98%D0%BD%D1%82%D0%B5%D1%80%D0%BF%D0%BE%D0%BB%D1%8F%D1%86%D0%B8%D0%BE%D0%BD%D0%BD%D1%8B%D0%B9_%D0%BC%D0%BD%D0%BE%D0%B3%D0%BE%D1%87%D0%BB%D0%B5%D0%BD_%D0%9B%D0%B0%D0%B3%D1%80%D0%B0%D0%BD%D0%B6%D0%B0). -Как правило, в компьютерной графике для интерполяции используют кубические кривые, соединённых гладким образом. Вместе они выглядят как одна кривая. Это называется [интерполяция сплайнами](http://ru.wikipedia.org/wiki/%D0%9A%D1%83%D0%B1%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D1%81%D0%BF%D0%BB%D0%B0%D0%B9%D0%BD). +Как правило, в компьютерной графике для интерполяции используют кубические кривые, соединённых гладким образом. Вместе они выглядят как одна кривая. Это называется [интерполяция сплайнами](http://ru.wikipedia.org/wiki/%D0%9A%D1%83%D0%B1%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D1%81%D0%BF%D0%BB%D0%B0%D0%B9%D0%BD). [/smart] + ## Итого -Кривые Безье задаются опорными точками. Мы рассмотрели два определения кривых: +Кривые Безье задаются опорными точками. + +Мы рассмотрели два определения кривых:
    1. Через математическую формулу.
    2. Через процесс построения де Кастельжо.
    3. @@ -197,6 +200,6 @@
      • В компьютерной графике, моделировании, в графических редакторах. Шрифты описываются с помощью кривых Безье.
      • -
      • В веб-разработке -- для графики на Canvas или в формате SVG. Кстати, все живые примеры выше написаны на SVG. Фактически, это один SVG-документ, к которому точки передаются параметрами. Вы можете открыть его в отдельном окне и посмотреть исходник: demo.svg.
      • -
      • В CSS-анимации, для задания траектории и скорости передвижения.
      • +
      • В веб-разработке -- для графики на Canvas или в формате SVG. Кстати, все живые примеры выше написаны на SVG. Фактически, это один SVG-документ, к которому точки передаются параметрами. Вы можете открыть его в отдельном окне и посмотреть исходник: [demo.svg](demo.svg?p=0,0,1,0.5,0,0.5,1,1&animate=1).
      • +
      • В CSS-анимации, для задания траектории или скорости передвижения.
      \ No newline at end of file diff --git a/5-animation/1-bezier/bezier-car.svg b/5-animation/1-bezier/bezier-car.svg new file mode 100644 index 00000000..58061706 --- /dev/null +++ b/5-animation/1-bezier/bezier-car.svg @@ -0,0 +1,32 @@ + + + + bezier-car.svg + Created with bin/sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/5-animation/1-bezier/bezier-letter.svg b/5-animation/1-bezier/bezier-letter.svg new file mode 100644 index 00000000..58e3fbfd --- /dev/null +++ b/5-animation/1-bezier/bezier-letter.svg @@ -0,0 +1,50 @@ + + + + bezier-letter.svg + Created with bin/sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/5-animation/1-bezier/bezier-vase.svg b/5-animation/1-bezier/bezier-vase.svg new file mode 100644 index 00000000..6030a745 --- /dev/null +++ b/5-animation/1-bezier/bezier-vase.svg @@ -0,0 +1,49 @@ + + + + bezier-vase.svg + Created with bin/sketchtool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/5-animation/1-bezier/bezier2.png b/5-animation/1-bezier/bezier2.png deleted file mode 100644 index 4e26747b..00000000 Binary files a/5-animation/1-bezier/bezier2.png and /dev/null differ diff --git a/5-animation/1-bezier/bezier2.svg b/5-animation/1-bezier/bezier2.svg new file mode 100644 index 00000000..0b0f99a6 --- /dev/null +++ b/5-animation/1-bezier/bezier2.svg @@ -0,0 +1,20 @@ + + + + bezier2.svg + Created with bin/sketchtool. + + + + + + + 1 + + + + 2 + + + + \ No newline at end of file diff --git a/5-animation/1-bezier/bezier3-draw1.png b/5-animation/1-bezier/bezier3-draw1.png deleted file mode 100644 index 10ce7f99..00000000 Binary files a/5-animation/1-bezier/bezier3-draw1.png and /dev/null differ diff --git a/5-animation/1-bezier/bezier3-draw1.svg b/5-animation/1-bezier/bezier3-draw1.svg new file mode 100644 index 00000000..9051e0ca --- /dev/null +++ b/5-animation/1-bezier/bezier3-draw1.svg @@ -0,0 +1,37 @@ + + + + bezier3-draw1.svg + Created with bin/sketchtool. + + + + + + + + 1 + + + + 3 + + + + + 2 + + + 0.25 + + + t = 0.25 + + + 0.25 + + + + + + \ No newline at end of file diff --git a/5-animation/1-bezier/bezier3-draw2.png b/5-animation/1-bezier/bezier3-draw2.png deleted file mode 100644 index b1226656..00000000 Binary files a/5-animation/1-bezier/bezier3-draw2.png and /dev/null differ diff --git a/5-animation/1-bezier/bezier3-draw2.svg b/5-animation/1-bezier/bezier3-draw2.svg new file mode 100644 index 00000000..dd762b32 --- /dev/null +++ b/5-animation/1-bezier/bezier3-draw2.svg @@ -0,0 +1,38 @@ + + + + bezier3-draw2.svg + Created with bin/sketchtool. + + + + + + + + + 1 + + + + 3 + + + + + + 2 + + + 0.5 + + + t = 0.5 + + + 0.5 + + + + + \ No newline at end of file diff --git a/5-animation/1-bezier/bezier3-e.png b/5-animation/1-bezier/bezier3-e.png deleted file mode 100644 index 7e0f8804..00000000 Binary files a/5-animation/1-bezier/bezier3-e.png and /dev/null differ diff --git a/5-animation/1-bezier/bezier3-e.svg b/5-animation/1-bezier/bezier3-e.svg new file mode 100644 index 00000000..79e31532 --- /dev/null +++ b/5-animation/1-bezier/bezier3-e.svg @@ -0,0 +1,25 @@ + + + + bezier3-e.svg + Created with bin/sketchtool. + + + + + + + + 1 + + + + 3 + + + + 3 + + + + \ No newline at end of file diff --git a/5-animation/1-bezier/bezier3.png b/5-animation/1-bezier/bezier3.png deleted file mode 100644 index 3991122c..00000000 Binary files a/5-animation/1-bezier/bezier3.png and /dev/null differ diff --git a/5-animation/1-bezier/bezier3.svg b/5-animation/1-bezier/bezier3.svg new file mode 100644 index 00000000..5ac329b5 --- /dev/null +++ b/5-animation/1-bezier/bezier3.svg @@ -0,0 +1,24 @@ + + + + bezier3.svg + Created with bin/sketchtool. + + + + + + + 1 + + + + 3 + + + + 3 + + + + \ No newline at end of file diff --git a/5-animation/1-bezier/bezier4-e.png b/5-animation/1-bezier/bezier4-e.png deleted file mode 100644 index a664b520..00000000 Binary files a/5-animation/1-bezier/bezier4-e.png and /dev/null differ diff --git a/5-animation/1-bezier/bezier4-e.svg b/5-animation/1-bezier/bezier4-e.svg new file mode 100644 index 00000000..562d06c6 --- /dev/null +++ b/5-animation/1-bezier/bezier4-e.svg @@ -0,0 +1,29 @@ + + + + bezier4-e.svg + Created with bin/sketchtool. + + + + + + + + + + + 1 + + + 2 + + + 3 + + + 4 + + + + \ No newline at end of file diff --git a/5-animation/1-bezier/bezier4.png b/5-animation/1-bezier/bezier4.png deleted file mode 100644 index a5ac0cfd..00000000 Binary files a/5-animation/1-bezier/bezier4.png and /dev/null differ diff --git a/5-animation/1-bezier/bezier4.svg b/5-animation/1-bezier/bezier4.svg new file mode 100644 index 00000000..f60ba3e5 --- /dev/null +++ b/5-animation/1-bezier/bezier4.svg @@ -0,0 +1,28 @@ + + + + bezier4.svg + Created with bin/sketchtool. + + + + + + + + + + 1 + + + 2 + + + 3 + + + 4 + + + + \ No newline at end of file diff --git a/5-animation/1-bezier/car.jpg b/5-animation/1-bezier/car.jpg deleted file mode 100644 index 8940f17a..00000000 Binary files a/5-animation/1-bezier/car.jpg and /dev/null differ diff --git a/5-animation/1-bezier/letter_m.png b/5-animation/1-bezier/letter_m.png deleted file mode 100644 index ba104a9a..00000000 Binary files a/5-animation/1-bezier/letter_m.png and /dev/null differ diff --git a/5-animation/1-bezier/pause.png b/5-animation/1-bezier/pause.png new file mode 100644 index 00000000..4d46cebb Binary files /dev/null and b/5-animation/1-bezier/pause.png differ diff --git a/5-animation/1-bezier/vase.png b/5-animation/1-bezier/vase.png deleted file mode 100644 index da8f06e9..00000000 Binary files a/5-animation/1-bezier/vase.png and /dev/null differ diff --git a/5-animation/2-css-transitions/1-animate-logo-css/solution.md b/5-animation/2-css-transitions/1-animate-logo-css/solution.md index 9f5ed693..ff52c5b0 100644 --- a/5-animation/2-css-transitions/1-animate-logo-css/solution.md +++ b/5-animation/2-css-transitions/1-animate-logo-css/solution.md @@ -1,28 +1,16 @@ -# Алгоритм - -Анимируйте одновременно свойства `left/top` и `width/height`. - -Чтобы в процессе анимации таблица сохраняла геометрию -- создайте на месте `IMG` временный `DIV` фиксированного размера и переместите `IMG` внутрь него. После анимации можно вернуть как было. - -Для начала анимации - добавьте класс изображению: +CSS-код для анимации одновременно `width` и `height`: ```css -.growing { - /* все свойства анимируются 3 секунды */ - -webkit-transition: all 3s; - -moz-transition: all 3s; - -o-transition: all 3s; - -ms-transition: all 3s; +/* исходный класс */ +#flyjet { + transition: all 3s; +} + +/* JS добавляет .growing *. +#flyjet.growing { + width: 400px; + height: 240px; } ``` -При этом, чтобы анимация началась, может понадобиться отложить установку класса и новых свойств через `setTimeout(.., 0)`. - -Для отлова конца анимации используйте событие `onTransitionEnd`. Оно сработает несколько раз, для каждого свойства, поэтому чтобы обработчик не вывел "OK" много раз -- можно обрабатывать окончание только при одном `event.propertyName`. - -# Похожая задача -Аналогичная задача, решённая средствами JS: [](/task/animate-logo). - -# Решение - -[edit src="solution" task/] \ No newline at end of file +Небольшая тонкость с окончанием анимации. Соответствующее событие `transitionend` сработает два раза -- по одному для каждого свойства. Поэтому, если не предпринять дополнительных шагов, сообщение из обработчика может быть выведено 2 раза. diff --git a/5-animation/2-css-transitions/1-animate-logo-css/solution.view/index.html b/5-animation/2-css-transitions/1-animate-logo-css/solution.view/index.html index 5190775c..3481cf54 100644 --- a/5-animation/2-css-transitions/1-animate-logo-css/solution.view/index.html +++ b/5-animation/2-css-transitions/1-animate-logo-css/solution.view/index.html @@ -2,38 +2,15 @@ - + + + -Кликните картинку для анимации. Расположение элементов при анимации не должно меняться! - - - - - - -
      Догнать -..и перегнать!
      + -В процессе анимации повторные нажатия на изображение игнорируются. - - - - - + \ No newline at end of file diff --git a/5-animation/2-css-transitions/1-animate-logo-css/task.md b/5-animation/2-css-transitions/1-animate-logo-css/task.md index d9fb2eaf..089f1b21 100644 --- a/5-animation/2-css-transitions/1-animate-logo-css/task.md +++ b/5-animation/2-css-transitions/1-animate-logo-css/task.md @@ -1,12 +1,16 @@ -# Анимировать лого (CSS) +# Анимировать самолёт (CSS) [importance 5] Реализуйте анимацию, как в демке ниже (клик на картинку): -[iframe src="solution" height=350] -Продолжительность анимации: 3 секунды. +[iframe src="solution" height=300] -Для анимации использовать CSS, по окончании вывести "ок". +
        +
      • Изображение растёт при клике с 40x24px до 400x240px .
      • +
      • Продолжительность анимации: 3 секунды.
      • +
      • По окончании вывести "Готово!".
      • +
      • Если в процессе анимации были дополнительные клики -- они не должны ничего "сломать".
      • +
      diff --git a/5-animation/2-css-transitions/2-animate-logo-bezier-css/bezier-up.svg b/5-animation/2-css-transitions/2-animate-logo-bezier-css/bezier-up.svg new file mode 100644 index 00000000..c6f18cc4 --- /dev/null +++ b/5-animation/2-css-transitions/2-animate-logo-bezier-css/bezier-up.svg @@ -0,0 +1,31 @@ + + + + bezier-up + Created with Sketch. + + + + + + + + + + + + + 1 + + + 2 + + + 3 + + + 4 + + + + \ No newline at end of file diff --git a/5-animation/2-css-transitions/2-animate-logo-bezier-css/solution.md b/5-animation/2-css-transitions/2-animate-logo-bezier-css/solution.md new file mode 100644 index 00000000..00c487de --- /dev/null +++ b/5-animation/2-css-transitions/2-animate-logo-bezier-css/solution.md @@ -0,0 +1,7 @@ +Для такой анимации необходимо подобрать правильную кривую Безье. + +Чтобы она выпрыгивала вверх, обе опорные точки можно вынести по `y>1`, например: `cubic-bezier(0.25, 1.5, 0.75, 1.5)` (промежуточные опорные точки имеют `y=1.5`). + +Её график: + + \ No newline at end of file diff --git a/5-animation/2-css-transitions/2-animate-logo-bezier-css/solution.view/index.html b/5-animation/2-css-transitions/2-animate-logo-bezier-css/solution.view/index.html new file mode 100644 index 00000000..ba3a92ab --- /dev/null +++ b/5-animation/2-css-transitions/2-animate-logo-bezier-css/solution.view/index.html @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + diff --git a/5-animation/2-css-transitions/2-animate-logo-bezier-css/task.md b/5-animation/2-css-transitions/2-animate-logo-bezier-css/task.md new file mode 100644 index 00000000..4ba2004b --- /dev/null +++ b/5-animation/2-css-transitions/2-animate-logo-bezier-css/task.md @@ -0,0 +1,10 @@ +# Анимировать самолёт с перелётом (CSS) + +[importance 5] + +Модифицируйте решение предыдущей задачи [](/task/animate-logo-css), чтобы в процессе анимации изображение выросло больше своего стандартного размера 400x240px ("выпрыгнуло"), а затем вернулось к нему. + +Должно получиться как здесь (клик на картинку) +[iframe src="solution" height=350] + +В качестве исходного документа возьмите решение предыдущей задачи. \ No newline at end of file diff --git a/5-animation/2-css-transitions/article.md b/5-animation/2-css-transitions/article.md index 330b14ec..699741f7 100644 --- a/5-animation/2-css-transitions/article.md +++ b/5-animation/2-css-transitions/article.md @@ -1,120 +1,213 @@ -# CSS-transitions +# CSS-анимации -Все современные браузеры, кроме IE9- поддерживают CSS transitions, которые позволяют реализовать анимацию средствами CSS, без привлечения JavaScript. +Все современные браузеры, кроме IE9- поддерживают CSS transitions и CSS animations, которые позволяют реализовать анимацию средствами CSS, без привлечения JavaScript. +Однако, как мы увидим далее, для более тонкого контроля анимации JavaScript вовсе не будет лишним. [cut] -## Анимация свойства [#css-transition] +## CSS transitions [#css-transition] -Идея проста. Вы указываете, что некоторое свойство будет анимироваться при помощи специальных CSS-правил. Далее, при изменении этого свойства, браузер сам обработает анимацию. +Идея проста. Мы указываем, что некоторое свойство будет анимироваться при помощи специальных CSS-правил. Далее, при изменении этого свойства, браузер сам обработает анимацию. -Например, CSS, представленный ниже, 2 секунды анимирует свойство `background-color`. +Например, CSS, представленный ниже, 3 секунды анимирует свойство `background-color`. ```css .animated { transition-property: background-color; - transition-duration: 2s; + transition-duration: 3s; } ``` -Теперь любое изменение фонового цвета будет анимироваться в течение 2-х секунд. +Теперь любое изменение фонового цвета будет анимироваться в течение 3х секунд. -У свойства `"transition"` есть и короткая запись: - -```css -.animated { - transition: background-color 2s; -} -``` - -## Полный синтаксис - -Свойства для CSS-анимаций: -
      -
      `transition-property`
      -
      Список свойств, которые будут анимироваться. Анимировать можно не все свойства, но [многие](http://www.w3.org/TR/css3-transitions/#animatable-properties-). Значение `all` означает "анимировать все свойства".
      -
      `transition-duration`
      -
      Продолжительность анимации. Если указано одно значение -- оно применится ко всем свойствам, можно указать несколько значений для разных `transition-property`.
      -
      `transition-timing-function`
      -
      Кривая Безье по 4-м точкам, используемая в качестве временной функциии. Их мы изучим [чуть позже](/bezier)
      -
      `transition-delay`
      -
      Указывает задержку от изменения свойства до начала CSS-анимации.
      -
      - -Свойство **`transition`** может содержать их все, в порядке: `property duration timing-function delay, ...`. - -## Пример - -Анимируем одновременно цвет и размер шрифта: +При клике на эту кнопку происходит анимация её фона: ```html - + + + - + + ``` -## Временнáя функция +Есть всего 5 свойств, задающих анимацию: +
        +
      • `transition-property`
      • +
      • `transition-duration`
      • +
      • `transition-timing-function`
      • +
      • `transition-delay`
      • +
      -В качестве временной функции можно выбрать любую [кривую Безье](/bezier), удовлетворяющую условиям: +Далее мы изучим их все, пока лишь заметим, что общее свойство `transition` может перечислять их все, в порядке: `property duration timing-function delay`, а также задавать анимацию нескольких свойств сразу. + +Например, при клике на эту кнопку анимируются одновременно цвет и размер шрифта: + +```html + + + + + + +``` + +Далее мы рассмотрим свойства анимации по отдельности. + +## transition-property + +Список свойств, которые будут анимироваться, например: `left`, `margin-left`, `height`, `color`. + +Анимировать можно не все свойства, но [многие](http://www.w3.org/TR/css3-transitions/#animatable-properties-). Значение `all` означает "анимировать все свойства". + +## transition-duration + +Продолжительность анимации, задаётся в формате [CSS time](http://www.w3.org/TR/css3-values/#time), то есть в секундах `s` или `ms`. + +## transition-delay + +Задержка до анимации. Например, если `transition-delay: 1s`, то анимация начнётся через 1 секунду после смены свойства. + +Возможны отрицательные значения, при этом анимация начнётся с середины. + +Например, вот анимация цифр от `0` до `9`: + +[codetabs src="digits"] + +Она осуществляется сменой `margin-left` у элемента с цифрами, примерно так: + +```css +#stripe.animate { + margin-left: -174px; + transition-property: margin-left; + transition-duration: 9s; +} +``` + +В примере выше JavaScript просто добавляет элементу класс -- и анимация стартует: + +```js +digit.classList.add('animate'); +``` + +Можно стартовать её "с середины", с нужной цифры, например соответствующей текущей секунде, при помощи отрицательного `transition-delay`. + +В примере ниже при клике на цифру она начнёт двигаться с текущей секунды: + +[codetabs src="digits-negative-delay"] + +В JavaScript это делается дополнительной строкой: + +```js +stripe.onclick = function() { + var sec = new Date().getSeconds() % 10; +*!* + // например, значение -3s начнёт анимацию с 3й секунды + stripe.style.transitionDelay = '-' + sec + 's'; +*/!* + stripe.classList.add('animate'); +}; +``` + +## transition-timing-function + +Временнáя функция, которая задаёт, как процесс анимации будет распределён во времени, например начнётся ли анимация медленно, чтобы потом ускориться или наоборот. + +Самое сложное, но при небольшом изучении -- вполне очевидное свойство. + +У него есть два основных вида значения: кривая Безье и по шагам. Начнём с первого. + +### Кривая Безье + +В качестве временной функции можно выбрать любую [кривую Безье](/bezier) с 4 опорными точками, удовлетворяющую условиям:
      1. Начальная точка `(0,0)`.
      2. Конечная точка `(1,1)`.
      3. -
      4. Для промежуточных точек значения `x` должны быть в интервале `0..1`.
      5. +
      6. Для промежуточных точек значения `x` должны быть в интервале `0..1`, `y` -- любыми.
      -Синтаксис для задания кривой Безье в CSS: `cubic-bezier(x2, y2, x3, y3)`. В нём указываются координаты второй и третьей точек, так как первая и последняя фиксированы. +Синтаксис для задания кривой Безье в CSS: `cubic-bezier(x2, y2, x3, y3)`. В нём указываются координаты только двух точек: второй и третьей, так как первая и последняя фиксированы. -Например, торможение можно описать кривой Безье: `cubic-bezier(0.0, 0.5, 0.5 ,1.0)`. +Она указывает, как быстро развивается процесс анимации во времени. + +
        i> +
      • По оси `x` идёт время: `0` -- начальный момент, `1` -- конец времени `transition-duration`.
      • +
      • По оси `y` -- завершённость процесса: `0` -- начальное значение анимируемого свойства, `1` -- конечное. + +Самый простой вариант -- это когда процесс развивается равномерно, "линейно" по времени. Это можно задать кривой Безье `cubic-bezier(0, 0, 1, 1)`. + +График этой "кривой" таков: + + + +...Как видно, это просто прямая. По мере того, как проходит время `x`, завершённость анимации `y` равномерно приближается от `0` к `1`. + +Поезд в примере ниже с постоянной скоростью "едет" слева направо, используя такую временную функцию: + +[codetabs src="train-linear"] + + +CSS для анимации: +```css +.train { + left: 0; + transition: left 5s cubic-bezier(0, 0, 1, 1); + /* JavaScript ставит значение left: 450px */ +} +``` + +Как нам показать, что поезд тормозит? + +Для этого используем кривую Безье: `cubic-bezier(0.0, 0.5, 0.5 ,1.0)`. График этой кривой: - + + +Как видно, процесс вначале развивается быстро -- кривая резко идёт вверх, а затем всё медленнее, медленнее. Вы можете увидеть эту временную функцию в действии, кликнув на поезд: -```html - - ``` - - +Существует несколько стандартных обозначений кривых: `linear`, `ease`, `ease-in`, `ease-out` и `ease-in-out`. +Значение `linear` -- это прямая, мы её уже видели. -Существуют и несколько стандартных кривых: `linear`, `ease`, `ease-in`, `ease-out` и `ease-in-out`. - -Значение `linear` -- это прямая, равномерное изменение. Оно используется по умолчанию. - -Остальные кривые являются короткой записью следующих `cubic-bezier`. В таблице ниже показано соответствие: +Остальные кривые являются короткой записью следующих `cubic-bezier`: - + @@ -126,71 +219,147 @@ - - - - + + + +
        `ease``ease`* `ease-in` `ease-out` `ease-in-out``(0.42, 0, 0.58, 1.0)`
        -Наиболее близкий стандартный вариант для примера с поездом -- `ease-out`: +* - По умолчанию, если никакой временной функции не указано, -- используется `ease`. +Значени + +**Кривая Безье может заставить анимацию "выпрыгивать" за пределы диапазона.** + +Допустимо указывать для кривой Безье как отрицательные `y`, так и сколь угодно большие. При этом кривая Безье будет также по `y` выскакивать за пределы диапазона `0..1`, представляющего собой начало-конец значения. + +В примере ниже CSS-код анимации таков: ```css .train { - -moz-transition: left 5s ease-out; - ... + left: 100px; + transition: left 5s cubic-bezier(.5, -1, .5, 2); + /* JavaScript поменяет left на 400px */ } ``` -## CSS-преобразования +Свойство `left` должно меняться от `100px` до `400px`. -Браузеры, которые поддерживают CSS-анимацию, поддерживают и [CSS-преобразования](https://developer.mozilla.org/en/CSS/Using_CSS_transforms). +Однако, если кликнуть на поезд, то мы увидим, что: +
          +
        • Он едет сначала назад, то есть `left` становится меньше `100px`.
        • +
        • Затем вперёд, причём выезжает за назначенные `400px`.
        • +
        • А затем опять назад -- до `400px`.
        • +
        -С их помощью можно сделать много красивых эффектов. Например, вращение: +[codetabs src="train-over"] -```js -//+ run - -document.body.style.MozTransition = "all 5s"; -document.body.style.MozTransform = "rotate(360deg)"; -document.body.style.WebkitTransition = "all 5s"; -document.body.style.WebkitTransform = "rotate(360deg)"; -document.body.style.OTransition = "all 5s"; -document.body.style.OTransform = "rotate(360deg)"; -document.body.style.MsTransition = "all 5s"; -document.body.style.MsTransform = "rotate(360deg)"; +Почему так происходит -- отлично видно, если взглянуть на кривую Безье с указанными опорными точками: -document.body.style.Transition = "all 5s"; -document.body.style.Transform = "rotate(360deg)"; + + +Мы вынесли координату `y` для второй опорной точки на 1 ниже нуля, а для третьей опорной точки -- на 1 выше единицы, поэтому и кривая вышла за границы "обычного" квадрата. Её значения по `y` вышли из стандартного диапазона `0..1`. + +Как мы помним, значению `y = 0` соответствует "нулевое" положение анимации, а `y = 1` -- конечное. Получается, что значения `y<0` двинули поезд назад, меньше исходного `left`, а значения `y>1` -- больше итогового `left`. + +Это, конечно, "мягкий" вариант. Если поставить значения `y` порядка `-99`, `99`, то поезд будет куда более сильно выпрыгивать за диапазон. + +Итак, кривая Безье позволяет задавать "плавное"" течение анимации. Подобрать кривую Безье вручную можно на сайте [](http://cubic-bezier.com/). + +### Шаги steps + +Временная функция `steps(количество шагов[, start/end])` позволяет разбить анимацию на чёткое количество шагов. + +Проще всего это увидеть на примере. Выше мы видели плавную анимацию цифр от `0` до `9` при помощи смены `margin-left` у элемента, содержащего `0123456789`. + +Чтобы цифры сдвигались не плавно, а шли чётко и раздельно, одна за другой -- мы разобьём анимацию на 9 шагов: +```css +#stripe.animate { + margin-left: -174px; + transition: margin-left 9s *!*steps(9, start)*/!*; +} ``` -Самое замечательное -- все эти эффекты используют графический ускоритель и почти не нагружают процессор. +В действии `step(9, start)`: -Все браузеры, кроме IE9- поддерживают это, ну а в IE может быть что-то через JavaScript или вообще без анимации. +[codetabs src="step"] + +Первый аргумент `steps` -- количество шагов, то есть изменение `margin-left` разделить на 9 частей, получается примерно по `19px`. На то же количество частей делится и временной интервал, то есть по `1s`. + +`start` -- означает, что при начале анимации нужно сразу применить первое изменение. Это проявляется тем, что при нажатии на цифру она меняется на `1` (первое изменение `margin-left`) мгновенно, а затем в начале каждой следующей секунды. + +То есть, процесс развивается так: +
          +
        • `0s` -- `-19px` (первое изменение в начале 1й секунды, сразу при нажатии)
        • +
        • `1s` -- `-38px`
        • +
        • ...
        • +
        • `8s` -- `-174px`
        • +
        • (на протяжении последней секунды видно окончательное значение).
        • +
        + +Альтернативное значение `end` означало бы, что изменения нужно применять не в начале, а в конце каждой секунды, то есть так: + +
          +
        • `0s` -- `0`
        • +
        • `1s` -- `-19px` (первое изменение в конце 1й секунды)
        • +
        • `2s` -- `-38px`
        • +
        • ...
        • +
        • `9s` -- `-174px`
        • +
        + +В действии `step(9, end)`: + +[codetabs src="step-end"] + +Также есть сокращённые значения: +
          +
        • `step-start` -- то же, что `steps(1, start)`, то есть завершить анимацию в 1 шаг сразу.
        • +
        • `step-end` -- то же, что `steps(1, end)`, то есть завершить анимацию в 1 шаг по истечении `transition-duration`.
        • +
        + +Такие значения востребованы редко, так как это даже и не анимация почти, но тоже бывают полезны. ## Событие transitionend -На конец CSS-анимации можно повесить обработчик. Его стандартное имя: `transitionend`, но браузерные префиксы требуются и тут. +На конец CSS-анимации можно повесить обработчик на событие `transitionend`. -Кликните на лодочку: +Это широко используется, чтобы после анимации сделать какое-то действие или объединить несколько анимаций в одну. + +Например, лодочка в примере ниже при клике начинает плавать туда-обратно, с каждым разом уплывая всё дальше вправо: [iframe src="boat" height=300 edit link] -Её анимация осуществляется функцией `go`, которая перезапускается по окончании (с переворотом через CSS). +Её анимация осуществляется функцией `go`, которая перезапускается по окончании, с переворотом через CSS: + ```js -... +boat.onclick = function() { + //... + var times = 1; + + function go() { + if (times % 2) { + // плывём вправо + boat.classList.remove('back'); + boat.style.marginLeft = 100 * times + 200 + 'px'; + } else { + // плывём влево + boat.classList.add('back'); + boat.style.marginLeft = 100 * times - 200 + 'px'; + } + + } + go(); - elem.addEventListener('transitionend', go); /* на будущее */ - elem.addEventListener('webkitTransitionEnd', go); - elem.addEventListener('mozTransitionEnd', go); - elem.addEventListener('oTransitionEnd', go); - elem.addEventListener('msTransitionEnd', go); -... + boat.addEventListener('transitionend', function() { + times++; + go(); + }); +}; ``` -Объект события `transitionend` также содержит свойства: +Объект события `transitionend` содержит специфические свойства:
        `propertyName`
        Свойство, анимация которого завершилась.
        @@ -200,17 +369,69 @@ document.body.style.Transform = "rotate(360deg)"; Свойство `propertyName` может быть полезно при одновременной анимации нескольких свойств. Каждое свойство даст своё событие, и можно решить, что с ним делать дальше. -## Ограничения и достоинства CSS-анимаций + +## CSS animations + +Более сложные анимации делаются объединением простых при помощи CSS-правила `@keyframes`. + +В нём задаётся "имя" анимации и правила: что, откуда и куда анимировать. Затем при помощи свойства `animation: имя параметры` эта анимация подключается к элементу, задаётся время анимации и дополнительные параметры, как её применять. + +Например: + +```html + + +
        + + +``` + +Этот стандарт пока в черновике, поэтому в Chrome, Safari, Opera нужен префикс `-webkit`. + +Статей про CSS animations достаточно много, посмотрите, например: + +
          +
        • [](http://css.yoksel.ru/css-animation/).
        • +
        + +## Итого + +CSS-анимации позволяют плавно или не очень менять одно или несколько свойств. + +Альтернатива им -- плавное изменение значений свойств через JavaScript, мы рассмотрим подробности далее. + +Ограничения и достоинства CSS-анимаций по сравнению с JavaScript: [compare] --Основное ограничение -- это то, что временная функция может быть задана только кривой Безье. Более сложные анимации, состоящие из нескольких кривых, реализуются при помощи [CSS animations](http://dev.w3.org/csswg/css3-animations/#animation-name-property) (стандарт пока не готов). --CSS-анимации касаются только свойств, а в JavaScript можно делать всё, что угодно. +-Временная функция может быть задана кривой Безье или через шаги. Более сложные анимации, состоящие из нескольких кривых, реализуются их комбинацией при помощи [CSS animations](http://www.w3.org/TR/css3-animations/), но JavaScript-функции всегда гибче. +-CSS-анимации касаются только свойств, а в JavaScript можно делать всё, что угодно, удалять элементы, создавать новые. -Отсутствует поддержка в IE9- -+Простые вещи делаются просто. Особенно удобно, если от отсутствия эффекта в IE проблем не возникнет. -+Гораздо "легче" для процессора, чем анимации JavaScript. Лучше используется графический ускоритель. Это очень важно для мобильных устройств. ++Простые вещи делаются просто. ++"Легче" для процессора, чем анимации JavaScript, лучше используется графический ускоритель. Это очень важно для мобильных устройств. [/compare] -[head] - - -[/head] \ No newline at end of file +Подавляющее большинство анимаций делается через CSS. + +При этом JavaScript запускает их начало -- как правило, добавлением класса, в котором задано новое свойство, и может отследить окончание через событие `transitionend`. diff --git a/5-animation/2-css-transitions/bezier-linear.svg b/5-animation/2-css-transitions/bezier-linear.svg new file mode 100644 index 00000000..0b0f99a6 --- /dev/null +++ b/5-animation/2-css-transitions/bezier-linear.svg @@ -0,0 +1,20 @@ + + + + bezier2.svg + Created with bin/sketchtool. + + + + + + + 1 + + + + 2 + + + + \ No newline at end of file diff --git a/5-animation/2-css-transitions/bezier-train-over.svg b/5-animation/2-css-transitions/bezier-train-over.svg new file mode 100644 index 00000000..696c5fc3 --- /dev/null +++ b/5-animation/2-css-transitions/bezier-train-over.svg @@ -0,0 +1,45 @@ + + + + bezier-train-over.svg + Created with bin/sketchtool. + + + + + + + (1,1) + + + (0,0) + + + (0,1) + + + (1,0) + + + + + + + 1 + + + + 2 + + + + + 4 + + + + 3 + + + + \ No newline at end of file diff --git a/5-animation/2-css-transitions/boat.view/index.html b/5-animation/2-css-transitions/boat.view/index.html index fd61a70e..4e4c3c4f 100644 --- a/5-animation/2-css-transitions/boat.view/index.html +++ b/5-animation/2-css-transitions/boat.view/index.html @@ -2,56 +2,36 @@ - + - + diff --git a/5-animation/2-css-transitions/boat.view/style.css b/5-animation/2-css-transitions/boat.view/style.css new file mode 100644 index 00000000..3e3b2b31 --- /dev/null +++ b/5-animation/2-css-transitions/boat.view/style.css @@ -0,0 +1,13 @@ + +#boat { + margin-left: 0; + cursor: pointer; + transition: margin-left 3s ease-in-out; +} + +/* переворот картинки через CSS */ +.back { + -webkit-transform: scaleX(-1); + transform: scaleX(-1); + filter: fliph; /* IE9- */ +} \ No newline at end of file diff --git a/5-animation/2-css-transitions/digits-negative-delay.view/index.html b/5-animation/2-css-transitions/digits-negative-delay.view/index.html new file mode 100644 index 00000000..02b1e240 --- /dev/null +++ b/5-animation/2-css-transitions/digits-negative-delay.view/index.html @@ -0,0 +1,13 @@ + + + + + + + + +
        0123456789
        + + + + \ No newline at end of file diff --git a/5-animation/2-css-transitions/digits-negative-delay.view/script.js b/5-animation/2-css-transitions/digits-negative-delay.view/script.js new file mode 100644 index 00000000..f2feda7f --- /dev/null +++ b/5-animation/2-css-transitions/digits-negative-delay.view/script.js @@ -0,0 +1,5 @@ +stripe.onclick = function() { + var sec = new Date().getSeconds() % 10; + stripe.style.transitionDelay = '-' + sec + 's'; + stripe.classList.add('animate'); +}; \ No newline at end of file diff --git a/5-animation/2-css-transitions/digits-negative-delay.view/style.css b/5-animation/2-css-transitions/digits-negative-delay.view/style.css new file mode 100644 index 00000000..c2af8112 --- /dev/null +++ b/5-animation/2-css-transitions/digits-negative-delay.view/style.css @@ -0,0 +1,13 @@ + +#digit { + width: .5em; + overflow: hidden; + font: 32px "Courier New", monospace; +} + +#stripe.animate { + margin-left: -174px; + transition-property: margin-left; + transition-duration: 9s; + transition-timing-function: linear; +} diff --git a/5-animation/2-css-transitions/digits.view/index.html b/5-animation/2-css-transitions/digits.view/index.html new file mode 100644 index 00000000..02b1e240 --- /dev/null +++ b/5-animation/2-css-transitions/digits.view/index.html @@ -0,0 +1,13 @@ + + + + + + + + +
        0123456789
        + + + + \ No newline at end of file diff --git a/5-animation/2-css-transitions/digits.view/script.js b/5-animation/2-css-transitions/digits.view/script.js new file mode 100644 index 00000000..faad8cf9 --- /dev/null +++ b/5-animation/2-css-transitions/digits.view/script.js @@ -0,0 +1,3 @@ +stripe.onclick = function() { + stripe.classList.add('animate'); +}; \ No newline at end of file diff --git a/5-animation/2-css-transitions/digits.view/style.css b/5-animation/2-css-transitions/digits.view/style.css new file mode 100644 index 00000000..c2af8112 --- /dev/null +++ b/5-animation/2-css-transitions/digits.view/style.css @@ -0,0 +1,13 @@ + +#digit { + width: .5em; + overflow: hidden; + font: 32px "Courier New", monospace; +} + +#stripe.animate { + margin-left: -174px; + transition-property: margin-left; + transition-duration: 9s; + transition-timing-function: linear; +} diff --git a/5-animation/2-css-transitions/ease-in-out.png b/5-animation/2-css-transitions/ease-in-out.png deleted file mode 100644 index 3581f658..00000000 Binary files a/5-animation/2-css-transitions/ease-in-out.png and /dev/null differ diff --git a/5-animation/2-css-transitions/ease-in-out.svg b/5-animation/2-css-transitions/ease-in-out.svg new file mode 100644 index 00000000..55c0dbee --- /dev/null +++ b/5-animation/2-css-transitions/ease-in-out.svg @@ -0,0 +1,30 @@ + + + + ease-in-out.svg + Created with bin/sketchtool. + + + + + + + + + + + + 1 + + + 2 + + + 3 + + + 4 + + + + \ No newline at end of file diff --git a/5-animation/2-css-transitions/ease-in.png b/5-animation/2-css-transitions/ease-in.png deleted file mode 100644 index 3556ba51..00000000 Binary files a/5-animation/2-css-transitions/ease-in.png and /dev/null differ diff --git a/5-animation/2-css-transitions/ease-in.svg b/5-animation/2-css-transitions/ease-in.svg new file mode 100644 index 00000000..45a2c4b5 --- /dev/null +++ b/5-animation/2-css-transitions/ease-in.svg @@ -0,0 +1,28 @@ + + + + ease-in + Created with Sketch. + + + + + + + + + + 1 + + + 2 + + + 3 + + + 4 + + + + \ No newline at end of file diff --git a/5-animation/2-css-transitions/ease-out.png b/5-animation/2-css-transitions/ease-out.png deleted file mode 100644 index 40957d23..00000000 Binary files a/5-animation/2-css-transitions/ease-out.png and /dev/null differ diff --git a/5-animation/2-css-transitions/ease-out.svg b/5-animation/2-css-transitions/ease-out.svg new file mode 100644 index 00000000..f570bbd4 --- /dev/null +++ b/5-animation/2-css-transitions/ease-out.svg @@ -0,0 +1,28 @@ + + + + ease-out + Created with Sketch. + + + + + + + + + + 1 + + + 2 + + + 3 + + + 4 + + + + \ No newline at end of file diff --git a/5-animation/2-css-transitions/ease.png b/5-animation/2-css-transitions/ease.png deleted file mode 100644 index c77a294a..00000000 Binary files a/5-animation/2-css-transitions/ease.png and /dev/null differ diff --git a/5-animation/2-css-transitions/ease.svg b/5-animation/2-css-transitions/ease.svg new file mode 100644 index 00000000..c683c720 --- /dev/null +++ b/5-animation/2-css-transitions/ease.svg @@ -0,0 +1,30 @@ + + + + ease + Created with Sketch. + + + + + + + + + + + + 1 + + + 2 + + + 3 + + + 4 + + + + \ No newline at end of file diff --git a/5-animation/2-css-transitions/step-end.view/index.html b/5-animation/2-css-transitions/step-end.view/index.html new file mode 100644 index 00000000..d07dc2dc --- /dev/null +++ b/5-animation/2-css-transitions/step-end.view/index.html @@ -0,0 +1,19 @@ + + + + + + + + +
        0123456789
        + + + + + + diff --git a/5-animation/2-css-transitions/step-end.view/style.css b/5-animation/2-css-transitions/step-end.view/style.css new file mode 100644 index 00000000..af0554f9 --- /dev/null +++ b/5-animation/2-css-transitions/step-end.view/style.css @@ -0,0 +1,13 @@ + +#digit { + width: .5em; + overflow: hidden; + font: 32px "Courier New", monospace; +} + +#stripe.animate { + margin-left: -174px; + transition-property: margin-left; + transition-duration: 9s; + transition-timing-function: steps(9, end); +} diff --git a/5-animation/2-css-transitions/step.view/index.html b/5-animation/2-css-transitions/step.view/index.html new file mode 100644 index 00000000..d07dc2dc --- /dev/null +++ b/5-animation/2-css-transitions/step.view/index.html @@ -0,0 +1,19 @@ + + + + + + + + +
        0123456789
        + + + + + + diff --git a/5-animation/2-css-transitions/step.view/style.css b/5-animation/2-css-transitions/step.view/style.css new file mode 100644 index 00000000..7a7ee410 --- /dev/null +++ b/5-animation/2-css-transitions/step.view/style.css @@ -0,0 +1,13 @@ + +#digit { + width: .5em; + overflow: hidden; + font: 32px "Courier New", monospace; +} + +#stripe.animate { + margin-left: -174px; + transition-property: margin-left; + transition-duration: 9s; + transition-timing-function: steps(9, start); +} diff --git a/5-animation/2-css-transitions/train-curve.png b/5-animation/2-css-transitions/train-curve.png deleted file mode 100644 index 985500a0..00000000 Binary files a/5-animation/2-css-transitions/train-curve.png and /dev/null differ diff --git a/5-animation/2-css-transitions/train-curve.svg b/5-animation/2-css-transitions/train-curve.svg new file mode 100644 index 00000000..76acd7e6 --- /dev/null +++ b/5-animation/2-css-transitions/train-curve.svg @@ -0,0 +1,30 @@ + + + + train-curve.svg + Created with bin/sketchtool. + + + + + + + + 1 + + + + 2 + + + + + 4 + + + + 3 + + + + \ No newline at end of file diff --git a/5-animation/2-css-transitions/train-linear.view/index.html b/5-animation/2-css-transitions/train-linear.view/index.html new file mode 100644 index 00000000..2a4236ee --- /dev/null +++ b/5-animation/2-css-transitions/train-linear.view/index.html @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/5-animation/2-css-transitions/train-linear.view/style.css b/5-animation/2-css-transitions/train-linear.view/style.css new file mode 100644 index 00000000..15ccbed8 --- /dev/null +++ b/5-animation/2-css-transitions/train-linear.view/style.css @@ -0,0 +1,9 @@ +.train { + position: relative; + cursor: pointer; + width: 177px; + height: 160px; + + left: 0; + transition: left 5s cubic-bezier(0, 0, 1, 1); +} \ No newline at end of file diff --git a/5-animation/2-css-transitions/train-over.view/index.html b/5-animation/2-css-transitions/train-over.view/index.html new file mode 100644 index 00000000..d94c00f3 --- /dev/null +++ b/5-animation/2-css-transitions/train-over.view/index.html @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/5-animation/2-css-transitions/train-over.view/style.css b/5-animation/2-css-transitions/train-over.view/style.css new file mode 100644 index 00000000..cf7590be --- /dev/null +++ b/5-animation/2-css-transitions/train-over.view/style.css @@ -0,0 +1,9 @@ +.train { + position: relative; + cursor: pointer; + width: 177px; + height: 160px; + + left: 100px; + transition: left 5s cubic-bezier(.5, -1, .5, 2); +} \ No newline at end of file diff --git a/5-animation/2-css-transitions/train.view/index.html b/5-animation/2-css-transitions/train.view/index.html new file mode 100644 index 00000000..2a4236ee --- /dev/null +++ b/5-animation/2-css-transitions/train.view/index.html @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/5-animation/2-css-transitions/train.view/style.css b/5-animation/2-css-transitions/train.view/style.css new file mode 100644 index 00000000..c3a9b3af --- /dev/null +++ b/5-animation/2-css-transitions/train.view/style.css @@ -0,0 +1,9 @@ +.train { + position: relative; + cursor: pointer; + width: 177px; + height: 160px; + + left: 0px; + transition: left 5s cubic-bezier(0.0,0.5,0.5,1.0); +} \ No newline at end of file diff --git a/5-animation/3-js-animation/3-animate-ball/solution.md b/5-animation/3-js-animation/1-animate-ball/solution.md similarity index 68% rename from 5-animation/3-js-animation/3-animate-ball/solution.md rename to 5-animation/3-js-animation/1-animate-ball/solution.md index 4a367c2b..666e00d3 100644 --- a/5-animation/3-js-animation/3-animate-ball/solution.md +++ b/5-animation/3-js-animation/1-animate-ball/solution.md @@ -7,21 +7,14 @@ Следующий код даст нам нужный результат: ```js -var img = document.getElementById('ball'); -var field = document.getElementById('field'); -img.onclick = function() { - var from = 0; - var to = field.clientHeight - img.clientHeight; - animate({ - delay: 20, - duration: 1000, -*!* - delta: makeEaseOut(bounce), - step: function(delta) { - img.style.top = to*delta + 'px' - } -*/!* - }); -} +var to = field.clientHeight - ball.clientHeight; + +animate({ + duration: 2000, + timing: makeEaseOut(bounce), + draw: function(progress) { + ball.style.top = to*progress + 'px' + } +}); ``` diff --git a/5-animation/3-js-animation/1-animate-ball/solution.view/index.html b/5-animation/3-js-animation/1-animate-ball/solution.view/index.html new file mode 100644 index 00000000..6301e8eb --- /dev/null +++ b/5-animation/3-js-animation/1-animate-ball/solution.view/index.html @@ -0,0 +1,50 @@ + + + + + + + + + +
        + +
        + + + + + + + diff --git a/5-animation/3-js-animation/1-animate-ball/solution.view/style.css b/5-animation/3-js-animation/1-animate-ball/solution.view/style.css new file mode 100644 index 00000000..2b21c418 --- /dev/null +++ b/5-animation/3-js-animation/1-animate-ball/solution.view/style.css @@ -0,0 +1,10 @@ +#field { + height: 200px; + border-bottom: 3px black groove; + position: relative; +} + +#ball { + position: absolute; + cursor: pointer; +} \ No newline at end of file diff --git a/5-animation/3-js-animation/4-animate-ball-hops/source.view/index.html b/5-animation/3-js-animation/1-animate-ball/source.view/index.html similarity index 57% rename from 5-animation/3-js-animation/4-animate-ball-hops/source.view/index.html rename to 5-animation/3-js-animation/1-animate-ball/source.view/index.html index 2ac136cd..2a04c036 100644 --- a/5-animation/3-js-animation/4-animate-ball-hops/source.view/index.html +++ b/5-animation/3-js-animation/1-animate-ball/source.view/index.html @@ -1,13 +1,8 @@ - - + + @@ -17,6 +12,5 @@ - diff --git a/5-animation/3-js-animation/1-animate-ball/source.view/style.css b/5-animation/3-js-animation/1-animate-ball/source.view/style.css new file mode 100644 index 00000000..2b21c418 --- /dev/null +++ b/5-animation/3-js-animation/1-animate-ball/source.view/style.css @@ -0,0 +1,10 @@ +#field { + height: 200px; + border-bottom: 3px black groove; + position: relative; +} + +#ball { + position: absolute; + cursor: pointer; +} \ No newline at end of file diff --git a/5-animation/3-js-animation/3-animate-ball/task.md b/5-animation/3-js-animation/1-animate-ball/task.md similarity index 67% rename from 5-animation/3-js-animation/3-animate-ball/task.md rename to 5-animation/3-js-animation/1-animate-ball/task.md index 3dc6c031..ed588116 100644 --- a/5-animation/3-js-animation/3-animate-ball/task.md +++ b/5-animation/3-js-animation/1-animate-ball/task.md @@ -3,8 +3,6 @@ [importance 5] Сделайте так, чтобы мяч подпрыгивал. Кликните по мячу, чтобы увидеть, как это должно выглядеть. + [iframe height=250 src="solution"] -В исходный документ включена функция [](#animate) и набор `delta`-функций. - - diff --git a/5-animation/3-js-animation/1-carousel-animated/solution.md b/5-animation/3-js-animation/1-carousel-animated/solution.md deleted file mode 100644 index c3e7992e..00000000 --- a/5-animation/3-js-animation/1-carousel-animated/solution.md +++ /dev/null @@ -1,2 +0,0 @@ -Проще всего -- использовать функцию [](#animate), а еще удобнее -- `animateProp`. - diff --git a/5-animation/3-js-animation/1-carousel-animated/solution.view/animate.js b/5-animation/3-js-animation/1-carousel-animated/solution.view/animate.js deleted file mode 100644 index 51bb38c6..00000000 --- a/5-animation/3-js-animation/1-carousel-animated/solution.view/animate.js +++ /dev/null @@ -1,92 +0,0 @@ -/** - Docs: http://learn.javascript.ru/tutorial/lib -*/ - -function animate(opts) { - - var start = new Date; - var delta = opts.delta || linear; - - var timer = setInterval(function() { - var progress = (new Date - start) / opts.duration; - - if (progress > 1) progress = 1; - - opts.step( delta(progress) ); - - if (progress == 1) { - clearInterval(timer); - opts.complete && opts.complete(); - } - }, opts.delay || 13); - - return timer; -} - -/** - Анимация стиля opts.prop у элемента opts.elem - от opts.from до opts.to продолжительностью opts.duration - в конце opts.complete - - Например: animateProp({ elem: ..., prop: 'height', start:0, end: 100, duration: 1000 }) -*/ -function animateProp(opts) { - var start = opts.start, end = opts.end, prop = opts.prop; - - opts.step = function(delta) { - var value = Math.round(start + (end - start)*delta); - opts.elem.style[prop] = value + 'px'; - } - return animate(opts); -} - -// ------------------ Delta ------------------ - -function elastic(progress) { - return Math.pow(2, 10 * (progress-1)) * Math.cos(20*Math.PI*1.5/3*progress); -} - -function linear(progress) { - return progress; -} - -function quad(progress) { - return Math.pow(progress, 2); -} - -function quint(progress) { - return Math.pow(progress, 5); -} - -function circ(progress) { - return 1 - Math.sin(Math.acos(progress)); -} - -function back(progress) { - return Math.pow(progress, 2) * ((1.5 + 1) * progress - 1.5); -} - - -function bounce(progress) { - for(var a = 0, b = 1, result; 1; a += b, b /= 2) { - if (progress >= (7 - 4 * a) / 11) { - return -Math.pow((11 - 6 * a - 11 * progress) / 4, 2) + Math.pow(b, 2); - } - } -} - -function makeEaseInOut(delta) { - return function(progress) { - if (progress < .5) - return delta(2*progress) / 2; - else - return (2 - delta(2*(1-progress))) / 2; - } -} - - -function makeEaseOut(delta) { - return function(progress) { - return 1 - delta(1 - progress); - } -} diff --git a/5-animation/3-js-animation/1-carousel-animated/solution.view/index.html b/5-animation/3-js-animation/1-carousel-animated/solution.view/index.html deleted file mode 100644 index 2a72c098..00000000 --- a/5-animation/3-js-animation/1-carousel-animated/solution.view/index.html +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - - - - - - - - - diff --git a/5-animation/3-js-animation/1-carousel-animated/solution.view/style.css b/5-animation/3-js-animation/1-carousel-animated/solution.view/style.css deleted file mode 100644 index d430e776..00000000 --- a/5-animation/3-js-animation/1-carousel-animated/solution.view/style.css +++ /dev/null @@ -1,52 +0,0 @@ -body { - padding: 10px -} - -.carousel { - position: relative; - width: 398px; - padding: 10px 40px; - border: 1px solid #CCC; - border-radius: 15px; - background: #eee; -} -.carousel img { - width: 130px; - height: 130px; - display: block; /* если не поставить block, в ряде браузеров будет inline -> лишнее пространтсво вокруг элементов */ -} -.carousel .arrow { - position: absolute; - top: 57px; - padding: 3px 8px 8px 9px; - background: #ddd; - border-radius: 15px; - font-size: 24px; - color: #444; - text-decoration: none; -} - -.carousel .arrow:hover { - background: #ccc; -} -.carousel .left-arrow { - left: 7px; -} -.carousel .right-arrow { - right: 7px; -} -.gallery { - width: 390px; - overflow: hidden; -} -.gallery ul { - height: 130px; - width: 9999px; - margin: 0; - padding: 0; - list-style: none; -} - -.gallery ul li { - float: left; -} diff --git a/5-animation/3-js-animation/1-carousel-animated/task.md b/5-animation/3-js-animation/1-carousel-animated/task.md deleted file mode 100644 index 0442a370..00000000 --- a/5-animation/3-js-animation/1-carousel-animated/task.md +++ /dev/null @@ -1,10 +0,0 @@ -# Анимируйте карусель - -[importance 5] - -Возьмите решение задачи [](/task/carousel) и сделайте передвижение карусели плавным, анимированым. - -Подключите для этого файл animate.js. - -Результат: -[iframe height=220 src="solution"] \ No newline at end of file diff --git a/5-animation/3-js-animation/2-animate-ball-hops/solution.md b/5-animation/3-js-animation/2-animate-ball-hops/solution.md new file mode 100644 index 00000000..f299c109 --- /dev/null +++ b/5-animation/3-js-animation/2-animate-ball-hops/solution.md @@ -0,0 +1,30 @@ +В задаче [](/task/animate-ball) создаётся подпрыгивающий мяч. Нам нужно всего лишь добавить еще одну анимацию для `elem.style.left`. + +Горизонтальная координата меняется по другому закону, нежели вертикальная. Она не "подпрыгивает", а постоянно увеличивается, постепенно сдвигая мяч вправо. + +Поэтому мы не можем добавить её в тот же `animate`, нужно делать отдельный. + +В качестве временной функции для перемещения вправо мы могли бы применить для неё `linear`, но тогда горизонтальное движение будет отставать от скачков мяча. Более красиво будет что-то типа `makeEaseOut(quad)`. + +Код: + +```js +var height = field.clientHeight - ball.clientHeight; +var width = 100; + +animate({ + duration: 2000, + timing: makeEaseOut(bounce), + draw: function(progress) { + ball.style.top = height*progress + 'px' + } +}); + +animate({ + duration: 2000, + timing: makeEaseOut(quad), + draw: function(progress) { + ball.style.left = width*progress + "px" + } +}); +``` diff --git a/5-animation/3-js-animation/2-animate-ball-hops/solution.view/index.html b/5-animation/3-js-animation/2-animate-ball-hops/solution.view/index.html new file mode 100644 index 00000000..62f98a6e --- /dev/null +++ b/5-animation/3-js-animation/2-animate-ball-hops/solution.view/index.html @@ -0,0 +1,59 @@ + + + + + + + + + +
        + +
        + + + + + + \ No newline at end of file diff --git a/5-animation/3-js-animation/2-animate-ball-hops/solution.view/style.css b/5-animation/3-js-animation/2-animate-ball-hops/solution.view/style.css new file mode 100644 index 00000000..2b21c418 --- /dev/null +++ b/5-animation/3-js-animation/2-animate-ball-hops/solution.view/style.css @@ -0,0 +1,10 @@ +#field { + height: 200px; + border-bottom: 3px black groove; + position: relative; +} + +#ball { + position: absolute; + cursor: pointer; +} \ No newline at end of file diff --git a/5-animation/3-js-animation/4-animate-ball-hops/task.md b/5-animation/3-js-animation/2-animate-ball-hops/task.md similarity index 71% rename from 5-animation/3-js-animation/4-animate-ball-hops/task.md rename to 5-animation/3-js-animation/2-animate-ball-hops/task.md index d2d85337..af3ef3bb 100644 --- a/5-animation/3-js-animation/4-animate-ball-hops/task.md +++ b/5-animation/3-js-animation/2-animate-ball-hops/task.md @@ -3,9 +3,9 @@ [importance 5] Заставьте мяч падать вправо. Кликните, чтобы увидеть в действии. + [iframe height=250 src="solution"] -Напишите код, который будет анимировать мяч. Дистанция вправо составляет `100px`. - -В исходный документ включена функция [](#animate) и набор `delta`-функций. +Напишите код, который будет анимировать мяч. Дистанция вправо составляет `100px`. +В качестве исходного кода возьмите решение предыдущей задачи [](/task/animate-ball). \ No newline at end of file diff --git a/5-animation/3-js-animation/2-animate-logo/solution.md b/5-animation/3-js-animation/2-animate-logo/solution.md deleted file mode 100644 index b4953291..00000000 --- a/5-animation/3-js-animation/2-animate-logo/solution.md +++ /dev/null @@ -1,5 +0,0 @@ -Анимируйте одновременно свойства `left/top` и `width/height`. - -Чтобы в процессе анимации таблица сохраняла геометрию -- создайте на месте `IMG` временный `DIV` фиксированного размера и переместите `IMG` внутрь него. После анимации можно вернуть как было. - -[edit src="solution"]Открыть решение в песочнице[/edit] \ No newline at end of file diff --git a/5-animation/3-js-animation/2-animate-logo/solution.view/animate.js b/5-animation/3-js-animation/2-animate-logo/solution.view/animate.js deleted file mode 100644 index 51bb38c6..00000000 --- a/5-animation/3-js-animation/2-animate-logo/solution.view/animate.js +++ /dev/null @@ -1,92 +0,0 @@ -/** - Docs: http://learn.javascript.ru/tutorial/lib -*/ - -function animate(opts) { - - var start = new Date; - var delta = opts.delta || linear; - - var timer = setInterval(function() { - var progress = (new Date - start) / opts.duration; - - if (progress > 1) progress = 1; - - opts.step( delta(progress) ); - - if (progress == 1) { - clearInterval(timer); - opts.complete && opts.complete(); - } - }, opts.delay || 13); - - return timer; -} - -/** - Анимация стиля opts.prop у элемента opts.elem - от opts.from до opts.to продолжительностью opts.duration - в конце opts.complete - - Например: animateProp({ elem: ..., prop: 'height', start:0, end: 100, duration: 1000 }) -*/ -function animateProp(opts) { - var start = opts.start, end = opts.end, prop = opts.prop; - - opts.step = function(delta) { - var value = Math.round(start + (end - start)*delta); - opts.elem.style[prop] = value + 'px'; - } - return animate(opts); -} - -// ------------------ Delta ------------------ - -function elastic(progress) { - return Math.pow(2, 10 * (progress-1)) * Math.cos(20*Math.PI*1.5/3*progress); -} - -function linear(progress) { - return progress; -} - -function quad(progress) { - return Math.pow(progress, 2); -} - -function quint(progress) { - return Math.pow(progress, 5); -} - -function circ(progress) { - return 1 - Math.sin(Math.acos(progress)); -} - -function back(progress) { - return Math.pow(progress, 2) * ((1.5 + 1) * progress - 1.5); -} - - -function bounce(progress) { - for(var a = 0, b = 1, result; 1; a += b, b /= 2) { - if (progress >= (7 - 4 * a) / 11) { - return -Math.pow((11 - 6 * a - 11 * progress) / 4, 2) + Math.pow(b, 2); - } - } -} - -function makeEaseInOut(delta) { - return function(progress) { - if (progress < .5) - return delta(2*progress) / 2; - else - return (2 - delta(2*(1-progress))) / 2; - } -} - - -function makeEaseOut(delta) { - return function(progress) { - return 1 - delta(1 - progress); - } -} diff --git a/5-animation/3-js-animation/2-animate-logo/solution.view/index.html b/5-animation/3-js-animation/2-animate-logo/solution.view/index.html deleted file mode 100644 index 4c7d55b0..00000000 --- a/5-animation/3-js-animation/2-animate-logo/solution.view/index.html +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - -Кликните картинку для анимации. Расположение элементов при анимации не должно меняться! - - - - - - - -
        Догнать -..и перегнать!
        - - - - - diff --git a/5-animation/3-js-animation/2-animate-logo/source.view/animate.js b/5-animation/3-js-animation/2-animate-logo/source.view/animate.js deleted file mode 100644 index 51bb38c6..00000000 --- a/5-animation/3-js-animation/2-animate-logo/source.view/animate.js +++ /dev/null @@ -1,92 +0,0 @@ -/** - Docs: http://learn.javascript.ru/tutorial/lib -*/ - -function animate(opts) { - - var start = new Date; - var delta = opts.delta || linear; - - var timer = setInterval(function() { - var progress = (new Date - start) / opts.duration; - - if (progress > 1) progress = 1; - - opts.step( delta(progress) ); - - if (progress == 1) { - clearInterval(timer); - opts.complete && opts.complete(); - } - }, opts.delay || 13); - - return timer; -} - -/** - Анимация стиля opts.prop у элемента opts.elem - от opts.from до opts.to продолжительностью opts.duration - в конце opts.complete - - Например: animateProp({ elem: ..., prop: 'height', start:0, end: 100, duration: 1000 }) -*/ -function animateProp(opts) { - var start = opts.start, end = opts.end, prop = opts.prop; - - opts.step = function(delta) { - var value = Math.round(start + (end - start)*delta); - opts.elem.style[prop] = value + 'px'; - } - return animate(opts); -} - -// ------------------ Delta ------------------ - -function elastic(progress) { - return Math.pow(2, 10 * (progress-1)) * Math.cos(20*Math.PI*1.5/3*progress); -} - -function linear(progress) { - return progress; -} - -function quad(progress) { - return Math.pow(progress, 2); -} - -function quint(progress) { - return Math.pow(progress, 5); -} - -function circ(progress) { - return 1 - Math.sin(Math.acos(progress)); -} - -function back(progress) { - return Math.pow(progress, 2) * ((1.5 + 1) * progress - 1.5); -} - - -function bounce(progress) { - for(var a = 0, b = 1, result; 1; a += b, b /= 2) { - if (progress >= (7 - 4 * a) / 11) { - return -Math.pow((11 - 6 * a - 11 * progress) / 4, 2) + Math.pow(b, 2); - } - } -} - -function makeEaseInOut(delta) { - return function(progress) { - if (progress < .5) - return delta(2*progress) / 2; - else - return (2 - delta(2*(1-progress))) / 2; - } -} - - -function makeEaseOut(delta) { - return function(progress) { - return 1 - delta(1 - progress); - } -} diff --git a/5-animation/3-js-animation/2-animate-logo/source.view/index.html b/5-animation/3-js-animation/2-animate-logo/source.view/index.html deleted file mode 100644 index ed2d2641..00000000 --- a/5-animation/3-js-animation/2-animate-logo/source.view/index.html +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - -Кликните картинку для анимации. Расположение элементов при анимации не должно меняться! - - - - - - -
        Догнать -..и перегнать!
        - -В процессе анимации повторные нажатия на изображение игнорируются. - - - - - - - diff --git a/5-animation/3-js-animation/2-animate-logo/task.md b/5-animation/3-js-animation/2-animate-logo/task.md deleted file mode 100644 index 2a6d011a..00000000 --- a/5-animation/3-js-animation/2-animate-logo/task.md +++ /dev/null @@ -1,12 +0,0 @@ -# Анимировать лого - -[importance 5] - -Реализуйте анимацию, как в демке ниже (клик на картинку): -[iframe src="solution" border=1] - -Продолжительность анимации: 3 секунды. - -В процессе анимации пусть повторные клики на изображение игнорируются. - - diff --git a/5-animation/3-js-animation/3-animate-ball/solution.view/animate.js b/5-animation/3-js-animation/3-animate-ball/solution.view/animate.js deleted file mode 100644 index 12ae14e3..00000000 --- a/5-animation/3-js-animation/3-animate-ball/solution.view/animate.js +++ /dev/null @@ -1,91 +0,0 @@ -/** - Docs: http://learn.javascript.ru/tutorial/lib -*/ - -function animate(opts) { - - var start = new Date; - var delta = opts.delta || linear; - - var timer = setInterval(function() { - var progress = (new Date - start) / opts.duration; - - if (progress > 1) progress = 1; - - opts.step( delta(progress) ); - - if (progress == 1) { - clearInterval(timer); - opts.complete && opts.complete(); - } - }, opts.delay || 13); - - return timer; -} - -/** - Анимация стиля opts.prop у элемента opts.elem - от opts.from до opts.to продолжительностью opts.duration - в конце opts.complete - - Например: animateProp({ elem: ..., prop: 'height', from:0, to: 100, duration: 1000 }) -*/ -function animateProp(opts) { - var start = opts.start, end = opts.end, prop = opts.prop; - - opts.step = function(delta) { - opts.elem.style[prop] = Math.round(start + (start - end)*delta) + 'px'; - } - return animate(opts); -} - -// ------------------ Delta ------------------ - -function elastic(progress) { - return Math.pow(2, 10 * (progress-1)) * Math.cos(20*Math.PI*1.5/3*progress) -} - -function linear(progress) { - return progress -} - -function quad(progress) { - return Math.pow(progress, 2) -} - -function quint(progress) { - return Math.pow(progress, 5) -} - -function circ(progress) { - return 1 - Math.sin(Math.acos(progress)) -} - -function back(progress) { - return Math.pow(progress, 2) * ((1.5 + 1) * progress - 1.5) -} - - -function bounce(progress) { - for(var a = 0, b = 1, result; 1; a += b, b /= 2) { - if (progress >= (7 - 4 * a) / 11) { - return -Math.pow((11 - 6 * a - 11 * progress) / 4, 2) + Math.pow(b, 2); - } - } -} - -function makeEaseInOut(delta) { - return function(progress) { - if (progress < .5) - return delta(2*progress) / 2 - else - return (2 - delta(2*(1-progress))) / 2 - } -} - - -function makeEaseOut(delta) { - return function(progress) { - return 1 - delta(1 - progress) - } -} diff --git a/5-animation/3-js-animation/3-animate-ball/solution.view/index.html b/5-animation/3-js-animation/3-animate-ball/solution.view/index.html deleted file mode 100644 index 4380b0b6..00000000 --- a/5-animation/3-js-animation/3-animate-ball/solution.view/index.html +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - -
        - -
        - - - - - - - diff --git a/5-animation/3-js-animation/3-animate-ball/source.view/animate.js b/5-animation/3-js-animation/3-animate-ball/source.view/animate.js deleted file mode 100644 index 12ae14e3..00000000 --- a/5-animation/3-js-animation/3-animate-ball/source.view/animate.js +++ /dev/null @@ -1,91 +0,0 @@ -/** - Docs: http://learn.javascript.ru/tutorial/lib -*/ - -function animate(opts) { - - var start = new Date; - var delta = opts.delta || linear; - - var timer = setInterval(function() { - var progress = (new Date - start) / opts.duration; - - if (progress > 1) progress = 1; - - opts.step( delta(progress) ); - - if (progress == 1) { - clearInterval(timer); - opts.complete && opts.complete(); - } - }, opts.delay || 13); - - return timer; -} - -/** - Анимация стиля opts.prop у элемента opts.elem - от opts.from до opts.to продолжительностью opts.duration - в конце opts.complete - - Например: animateProp({ elem: ..., prop: 'height', from:0, to: 100, duration: 1000 }) -*/ -function animateProp(opts) { - var start = opts.start, end = opts.end, prop = opts.prop; - - opts.step = function(delta) { - opts.elem.style[prop] = Math.round(start + (start - end)*delta) + 'px'; - } - return animate(opts); -} - -// ------------------ Delta ------------------ - -function elastic(progress) { - return Math.pow(2, 10 * (progress-1)) * Math.cos(20*Math.PI*1.5/3*progress) -} - -function linear(progress) { - return progress -} - -function quad(progress) { - return Math.pow(progress, 2) -} - -function quint(progress) { - return Math.pow(progress, 5) -} - -function circ(progress) { - return 1 - Math.sin(Math.acos(progress)) -} - -function back(progress) { - return Math.pow(progress, 2) * ((1.5 + 1) * progress - 1.5) -} - - -function bounce(progress) { - for(var a = 0, b = 1, result; 1; a += b, b /= 2) { - if (progress >= (7 - 4 * a) / 11) { - return -Math.pow((11 - 6 * a - 11 * progress) / 4, 2) + Math.pow(b, 2); - } - } -} - -function makeEaseInOut(delta) { - return function(progress) { - if (progress < .5) - return delta(2*progress) / 2 - else - return (2 - delta(2*(1-progress))) / 2 - } -} - - -function makeEaseOut(delta) { - return function(progress) { - return 1 - delta(1 - progress) - } -} diff --git a/5-animation/3-js-animation/3-animate-ball/source.view/index.html b/5-animation/3-js-animation/3-animate-ball/source.view/index.html deleted file mode 100644 index 2ac136cd..00000000 --- a/5-animation/3-js-animation/3-animate-ball/source.view/index.html +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - -
        - -
        - - - - - diff --git a/5-animation/3-js-animation/4-animate-ball-hops/solution.md b/5-animation/3-js-animation/4-animate-ball-hops/solution.md deleted file mode 100644 index 5e89693b..00000000 --- a/5-animation/3-js-animation/4-animate-ball-hops/solution.md +++ /dev/null @@ -1,35 +0,0 @@ -Посмотрите задачу [](/task/animate-ball). Там создаётся подпрыгивающий мяч. А для решения этой задачи нам нужно добавить еще одну анимацию для `elem.style.left`. - -Горизонтальная координата меняется по другому закону, нежели вертикальная. Она не "подпрыгивает", а постоянно увеличивается, постепенно сдвигая мяч вправо. - -Мы могли бы применить для неё `linear`, но тогда горизонтальное движение будет отставать от скачков мяча. Более красиво будет что-то типа `makeEaseOut(quad)`. - -Код: - -```js -img.onclick = function() { - - var height = document.getElementById('field').clientHeight - img.clientHeight - var width = 100 - - animate({ - delay: 20, - duration: 1000, - delta: makeEaseOut(bounce), - step: function(delta) { - img.style.top = height*delta + 'px' - } - }); - -*!* - animate({ - delay: 20, - duration: 1000, - delta: makeEaseOut(quad), - step: function(delta) { - img.style.left = width*delta + "px" - } - }); -*/!* -} -``` diff --git a/5-animation/3-js-animation/4-animate-ball-hops/solution.view/animate.js b/5-animation/3-js-animation/4-animate-ball-hops/solution.view/animate.js deleted file mode 100644 index 12ae14e3..00000000 --- a/5-animation/3-js-animation/4-animate-ball-hops/solution.view/animate.js +++ /dev/null @@ -1,91 +0,0 @@ -/** - Docs: http://learn.javascript.ru/tutorial/lib -*/ - -function animate(opts) { - - var start = new Date; - var delta = opts.delta || linear; - - var timer = setInterval(function() { - var progress = (new Date - start) / opts.duration; - - if (progress > 1) progress = 1; - - opts.step( delta(progress) ); - - if (progress == 1) { - clearInterval(timer); - opts.complete && opts.complete(); - } - }, opts.delay || 13); - - return timer; -} - -/** - Анимация стиля opts.prop у элемента opts.elem - от opts.from до opts.to продолжительностью opts.duration - в конце opts.complete - - Например: animateProp({ elem: ..., prop: 'height', from:0, to: 100, duration: 1000 }) -*/ -function animateProp(opts) { - var start = opts.start, end = opts.end, prop = opts.prop; - - opts.step = function(delta) { - opts.elem.style[prop] = Math.round(start + (start - end)*delta) + 'px'; - } - return animate(opts); -} - -// ------------------ Delta ------------------ - -function elastic(progress) { - return Math.pow(2, 10 * (progress-1)) * Math.cos(20*Math.PI*1.5/3*progress) -} - -function linear(progress) { - return progress -} - -function quad(progress) { - return Math.pow(progress, 2) -} - -function quint(progress) { - return Math.pow(progress, 5) -} - -function circ(progress) { - return 1 - Math.sin(Math.acos(progress)) -} - -function back(progress) { - return Math.pow(progress, 2) * ((1.5 + 1) * progress - 1.5) -} - - -function bounce(progress) { - for(var a = 0, b = 1, result; 1; a += b, b /= 2) { - if (progress >= (7 - 4 * a) / 11) { - return -Math.pow((11 - 6 * a - 11 * progress) / 4, 2) + Math.pow(b, 2); - } - } -} - -function makeEaseInOut(delta) { - return function(progress) { - if (progress < .5) - return delta(2*progress) / 2 - else - return (2 - delta(2*(1-progress))) / 2 - } -} - - -function makeEaseOut(delta) { - return function(progress) { - return 1 - delta(1 - progress) - } -} diff --git a/5-animation/3-js-animation/4-animate-ball-hops/solution.view/index.html b/5-animation/3-js-animation/4-animate-ball-hops/solution.view/index.html deleted file mode 100644 index 996d0810..00000000 --- a/5-animation/3-js-animation/4-animate-ball-hops/solution.view/index.html +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - -
        - -
        - - - - - - - diff --git a/5-animation/3-js-animation/4-animate-ball-hops/source.view/animate.js b/5-animation/3-js-animation/4-animate-ball-hops/source.view/animate.js deleted file mode 100644 index 12ae14e3..00000000 --- a/5-animation/3-js-animation/4-animate-ball-hops/source.view/animate.js +++ /dev/null @@ -1,91 +0,0 @@ -/** - Docs: http://learn.javascript.ru/tutorial/lib -*/ - -function animate(opts) { - - var start = new Date; - var delta = opts.delta || linear; - - var timer = setInterval(function() { - var progress = (new Date - start) / opts.duration; - - if (progress > 1) progress = 1; - - opts.step( delta(progress) ); - - if (progress == 1) { - clearInterval(timer); - opts.complete && opts.complete(); - } - }, opts.delay || 13); - - return timer; -} - -/** - Анимация стиля opts.prop у элемента opts.elem - от opts.from до opts.to продолжительностью opts.duration - в конце opts.complete - - Например: animateProp({ elem: ..., prop: 'height', from:0, to: 100, duration: 1000 }) -*/ -function animateProp(opts) { - var start = opts.start, end = opts.end, prop = opts.prop; - - opts.step = function(delta) { - opts.elem.style[prop] = Math.round(start + (start - end)*delta) + 'px'; - } - return animate(opts); -} - -// ------------------ Delta ------------------ - -function elastic(progress) { - return Math.pow(2, 10 * (progress-1)) * Math.cos(20*Math.PI*1.5/3*progress) -} - -function linear(progress) { - return progress -} - -function quad(progress) { - return Math.pow(progress, 2) -} - -function quint(progress) { - return Math.pow(progress, 5) -} - -function circ(progress) { - return 1 - Math.sin(Math.acos(progress)) -} - -function back(progress) { - return Math.pow(progress, 2) * ((1.5 + 1) * progress - 1.5) -} - - -function bounce(progress) { - for(var a = 0, b = 1, result; 1; a += b, b /= 2) { - if (progress >= (7 - 4 * a) / 11) { - return -Math.pow((11 - 6 * a - 11 * progress) / 4, 2) + Math.pow(b, 2); - } - } -} - -function makeEaseInOut(delta) { - return function(progress) { - if (progress < .5) - return delta(2*progress) / 2 - else - return (2 - delta(2*(1-progress))) / 2 - } -} - - -function makeEaseOut(delta) { - return function(progress) { - return 1 - delta(1 - progress) - } -} diff --git a/5-animation/3-js-animation/article.md b/5-animation/3-js-animation/article.md index 9df716e8..422e6133 100644 --- a/5-animation/3-js-animation/article.md +++ b/5-animation/3-js-animation/article.md @@ -1,256 +1,238 @@ # JS-Анимация -В этой главе мы рассмотрим устройство браузерной анимации. Она примерно одинаково реализована во всех фреймворках. +JavaScript-анимация применяется там, где не подходит CSS. -Понимание этого позволит разобраться в происходящем, если что-то вдруг не работает, а также написать сложную анимацию самому. +Например, по сложной траектории, с временной функцией, выходящей за рамки кривых Безье, на canvas. Иногда её используют для анимации в старых IE. -Анимация при помощи JavaScript и современная CSS-анимация дополняют друг друга. [cut] -## Основы анимации +## setInterval С точки зрения HTML/CSS, анимация -- это постепенное изменение стиля DOM-элемента. Например, увеличение координаты `style.left` от `0px` до `100px` сдвигает элемент. -Код, который производит изменение, вызывается таймером. Интервал таймера очень мал и поэтому анимация выглядит плавной. Это тот же принцип, что и в кино: для непрерывной анимации достаточно 24 или больше вызовов таймера в секунду. +Если увеличивать `left` от `0` до `100` при помощи `setInterval`, делая по 50 изменений в секунду, то это будет выглядеть как плавное перемещение. Тот же принцип, что и в кино: для непрерывной анимации достаточно 24 или больше вызовов `setInterval` в секунду. Псевдо-код для анимации выглядит так: ```js +var fps = 50; // 50 кадров в секунду var timer = setInterval(function() { - показать новый кадр - if (время вышло) clearInterval(timer); -}, 10) + if (время вышло) clearInterval(timer); + else немного увеличить left +}, 1000 / fps) ``` -Задержка между кадрами в данном случае составляет `10 ms`, что означает `100` кадров в секунду. +Более полный пример кода анимации: -В большинстве фреймворков, задержка по умолчанию составляет `10`-`15` мс. Меньшая задержка делает анимацию более плавной, но только в том случае, если браузер достаточно быстр, чтобы анимировать каждый шаг вовремя. +```js +var start = Date.now(); // сохранить время начала -Если анимация требует большого количества вычислений, то нагрузка процессора может доходить до 100% и вызывать ощутимые "тормоза" в работе браузера. В -таком случае, задержку можно увеличить. Например, 40мс дадут нам 25 кадров в секунду, что очень близко к кинематографическому стандарту в 24 кадра. +var timer = setInterval(function() { + // вычислить сколько времени прошло с начала анимации + var timePassed = Date.now() - start; -[smart header="`setInterval` вместо `setTimeout`"] -Мы используем `setInterval`, а не рекурсивный `setTimeout`, потому что нам нужен *один кадр за промежуток времени*, а не *фиксированная задержка между кадрами*. -В статье [](/setTimeout-setInterval) описана разница между `setInterval` и рекурсивным `setTimeout`. -[/smart] - - -### Пример - -Например, передвинем элемент путём изменения `element.style.left` от 0 до 100px. Изменение происходит на 1px каждые 10мс. - -```html - + // рисует состояние анимации, соответствующее времени timePassed + draw(timePassed); -
        -
        -
        +}, 20); + +// в то время как timePassed идёт от 0 до 2000 +// left принимает значения от 0 до 400px +function draw(timePassed) { + train.style.left = timePassed / 5 + 'px'; +} ``` Кликните для демонстрации: -[iframe height=60 src="move100" link] + +[codetabs height=200 src="move"] + +## requestAnimationFrame + +Если у нас не один такой `setInterval`, а несколько в разных местах кода, то браузеру нужно в те же 20мс работать со страницей уже несколько раз. А ведь кроме `setInterval` есть ещё другие действия, к примеру, прокрутка страницы, которую тоже надо нарисовать. + +Если все действия по перерисовке производить независимо, то будет выполняться много двойной работы. + +Гораздо выгоднее с точки зрения производительности -- сгруппировать все перерисовки в одну и запускать их централизованно, все вместе. + +Для этого в JavaScript-фреймворках, которые поддерживают анимацию, есть единый таймер: + +```js +setInterval(function() { + /* отрисовать все анимации */ +}, 20); +``` + +...Все анимации, которые запускает такой фреймворк, добавляются в общий список, и раз в 20мс единый таймер проверяет его, запускает текущие, удаляет завершившиеся. + +Современные браузеры, кроме IE9-, поддерживают стандарт [Animation timing](http://www.w3.org/TR/animation-timing/), который представляет собой дальнейший шаг в этом направлении. Он позволяет синхронизировать наши анимации со встроенными механизмами обновления страницы. То есть, сгруппированы будут не только наши, но и CSS-анимации и другие браузерные перерисовки. + +При этом графический ускоритель будет использован максимально эффективно, и исключена повторная обработка одних и тех же участков страницы. А значит -- меньше будет загрузка CPU, да и сама анимация станет более плавной. + +Для этого используется функция [requestAnimationFrame](http://www.w3.org/TR/animation-timing/#dom-windowanimationtiming-requestanimationframe). + +Синтаксис: +```js +var requestId = requestAnimationFrame(callback) +``` + +Такой вызов планирует запуск `callback` в ближайшее время, когда браузер сочтёт возможным осуществить анимацию. + +Если запланировать в `callback` какое-то рисование, то оно будет сгруппировано с другими `requestAnimationFrame` и с внутренними перерисовками браузера. + +Возвращаемое значение `requestId` служит для отмены запуска: +```js +// отменить запланированное выше выполнение callback +cancelAnimationFrame(requestId); +``` + +Функция `callback` получает один аргумент -- время, прошедшее с начала загрузки страницы, результат вызова [performance.now()](https://developer.mozilla.org/en-US/docs/Web/API/Performance/now). + +Как правило, запуск `callback` происходит очень скоро. Если у процессора большая загрузка или батарея у ноутбука почти разряжена -- то пореже. + +Если вы запустите этот код, то увидите промежутки между первыми 20 запусками `requestAnimationFrame`. Как правило, это 10-20мс, но бывает и больше и меньше. Это оптимальная частота анимации с точки зрения браузера. + +```html + + +``` -## Структура анимации +Функция анимации на основе `requestAnimationFrame`: + +```js +// Рисует функция draw +// Продолжительность анимации duration +function animate(draw, duration) { + var start = performance.now(); + + requestAnimationFrame(function animate(time) { + // определить, сколько прошло времени с начала анимации + var timePassed = time - start; + + // возможно небольшое превышение времени, в этом случае зафиксировать конец + if (timePassed > duration) timePassed = duration; + + // нарисовать состояние анимации в момент timePassed + draw(timePassed); + + // если время анимации не закончилось - запланировать ещё кадр + if (timePassed < duration) { + requestAnimationFrame(animate); + } + + }); +} +``` + +Использование для поезда: +```js +animate(function(timePassed) { + train.style.left = timePassed / 5 + 'px'; +}, 2000); +``` + +В действии: + +[codetabs src="move-raf"] + +## Структура анимации + +На основе `requestAnimationFrame` можно соорудить и гораздо более мощную, но в то же время простую функцию анимации. У анимации есть три основных параметра:
        -
        `delay`
        -
        Время между кадрами (в миллисекундах, т.е. 1/1000 секунды). Например, 10мс.
        `duration`
        -
        Общее время, которое должна длиться анимация, в мс. Например, 1000мс.
        -
        `step(progress)`
        -
        Функция **`step(progress)`** занимается отрисовкой состояния анимации, соответствующего времени `progress`.
        -
        +
        Общее время, которое должна длиться анимация, в мс. Например, `1000`.
        +
        `timing(timeFraction)`
        +
        Временная функция, которая, по аналогии с CSS-свойством `transition-timing-function`, будет по текущему времени вычислять состояние анимации. -Каждый кадр выполняется, сколько времени прошло: `progress = (now-start)/duration`. Значение `progress` меняется от `0` в начале анимации до `1` в конце. Так как вычисления с дробными числами не всегда точны, то в конце оно может быть даже немного больше 1. В этом случае мы уменьшаем его до 1 и завершаем анимацию. +Она получает на вход непрерывно возрастающее число `timeFraction` -- от `0` до `1`, где `0` означает самое начало анимации, а `1` -- её конец. -Создадим функцию `animate`, которая получает объект со свойствами `delay, duration, step` и выполняет анимацию. +Её результатом должно быть значение завершённости анимации, которому в CSS transitions на кривых Безье соответствует координата `y`. +Также по аналогии с `transition-timing-function` должны соблюдаться условия: +
          +
        • timing(0) = 0
        • +
        • timing(1) = 1
        • +
        + +...То есть, анимация начинается в точке `(0,0)` -- нулевое время и нулевой прогресс и заканчивается в `(1, 1)` -- прошло полное время, и процесс завершён. + +Например, функция-прямая означает равномерное развитие процесса: ```js -function animate(opts) { - - var start = new Date; // сохранить время начала - - var timer = setInterval(function() { - - // вычислить сколько времени прошло - var progress = (new Date - start) / opts.duration; - if (progress > 1) progress = 1; - - // отрисовать анимацию - opts.step(progress); - - if (progress == 1) clearInterval(timer); // конец :) - - }, opts.delay || 10); // по умолчанию кадр каждые 10мс - +function linear(timeFraction) { + return timeFraction; } ``` - -### Пример - - -Анимируем ширину элемента `width` от `0` до `100%`, используя нашу функцию: - -```js -function stretch(elem) { - animate({ - duration: 1000, // время на анимацию 1000 мс - step: function(progress) { - elem.style.width = progress*100 + '%'; - } - }); -} -``` - -Кликните для демонстрации: -[iframe height=60 src="width" link] - - -Функция `step` может получать дополнительные параметры анимации из `opts` (через `this`) или через замыкание. - -Следующий пример использует параметр `to` из замыкания для анимации бегунка: - -```js -function move(elem) { - var to = 500; - - animate({ - duration: 1000, - step: function(progress) { - // progress меняется от 0 до 1, left от 0px до 500px - elem.style.left = to*progress + "px"; - } - }); - -} -``` - -Кликните для демонстрации: -[iframe height=60 src="move" link] - -## Временная функция delta - -В сложных анимациях свойства изменяются по определённому закону. Зачастую, он гораздо сложнее, чем простое равномерное возрастание/убывание. - -Для того, чтобы можно было задать более хитрые виды анимации, в алгоритм добавляется дополнительная функция `delta(progress)`, которая вычисляет текущее состояние анимации от 0 до 1, а `step` использует её значение вместо `progress`. - -В `animate` изменится всего одна строчка. Было: - -```js -... -opts.step(progress); -... -``` - -Станет: - -```js -... -opts.step( opts.delta(progress) ); -... -``` - - - -```js -//+ hide="Раскрыть код animate с delta" -function animate(opts) { - - var start = new Date; - - var timer = setInterval(function() { - - var progress = (new Date - start) / opts.duration; - if (progress > 1) progress = 1; - - opts.step( opts.delta(progress) ); - - if (progress == 1) clearInterval(timer); - - }, opts.delay || 10); - -} -``` - -Такое небольшое изменение добавляет много гибкости. Функция `step` занимается всего лишь отрисовкой текущего состояния анимации, а само состояние по времени определяется в `delta`. - -Разные значения `delta` заставляют скорость анимации, ускорение и другие параметры вести себя абсолютно по-разному. - -Рассмотрим примеры анимации движения с использованием различных `delta`. - -### Линейная delta - -Самая простая функция `delta` -- это та, которая просто возвращает `progress`. - -```js -function linear(progress) { - return progress; -} -``` - -То есть, как будто никакой `delta` нет. Состояние анимации (которое при передвижении отображается как координата `left`) зависит от времени линейно. - - -**График:** +Её график: -**По горизонтали - `progress`, а по вертикали - `delta(progress)`.** +Как видно, её график полностью совпадает с `transition-timing-function: linear`, и эффект абсолютно такой же. -Пример: +Есть и другие, более интересные варианты, мы рассмотрим их чуть позже. -```html -
        -
        -
        -``` +
        +
        `draw(progress)`
        +
        Функция, которая получает состояние завершённости анимации и рисует его. Значению `progress=0` соответствует начальная точка анимации, `progress=1` -- конечная. -Здесь и далее функция `move` будет такой: +Именно эта функция и осуществляет, собственно, анимацию. +Например, может двигать элемент: ```js -function move(elem, delta, duration) { - var to = 500; - - animate({ - delay: 10, - duration: duration || 1000, - delta: delta, - step: function(delta) { - elem.style.left = to*delta + "px" - } - }); - +function draw(progress) { + train.style.left = progress + 'px'; } ``` -То есть, она будет перемещать бегунок, изменяя `left` по закону `delta`, за `duration` мс (по умолчанию 1000мс). +Возможны любые варианты, анимировать можно что угодно и как угодно. +
        +
        -Кликните для демонстрации линейной `delta`: -
        -
        -
        +Анимируем ширину элемента `width` от `0` до `100%`, используя нашу функцию. + +Кликните для демонстрации: +[codetabs height=60 src="width"] + +Код для запуска анимации: + +```js +animate({ + duration: 1000, + timing: function(timeFraction) { + return timeFraction; + }, + draw: function(progress) { + elem.style.width = progress * 100 + '%'; + } +}); +``` + +## Временные функции + +Выше мы видели самую простую, линейную временную функцию. + +Рассмотрим примеры анимации движения с использованием различных `timing`. ### В степени n -Вот еще один простой случай. `delta` - это `progress` в `n-й` степени . Частные случаи - квадратичная, кубическая функции и т.д. +Вот еще один простой случай -- `progress` в степени `n`. Частные случаи - квадратичная, кубическая функции и т.д. Для квадратичной функции: @@ -265,42 +247,30 @@ function quad(progress) { Пример для квадратичной функции (клик для просмотра): -
        -
        -
        +[iframe height=40 src="quad" link] Увеличение степени влияет на ускорение. Например, график для 5-й степени: -И пример: -
        -
        -
        - -**Функция `delta` описывает развитие анимации в зависимости от времени.** - -В примере выше -- сначала медленно: время идёт (ось X), а состояние анимации почти не меняется (ось Y), а потом всё быстрее и быстрее. Другие графики зададут иное поведение. - +В действии: +[iframe height=40 src="quint" link] ### Дуга Функция: ```js -function circ(progress) { - return 1 - Math.sin(Math.acos(progress)) +function circ(timeFraction) { + return 1 - Math.sin(Math.acos(timeFraction)) } ``` **График:** + -Пример: -
        -
        -
        - +[iframe height=40 src="circ" link] ### Back: стреляем из лука @@ -312,8 +282,8 @@ function circ(progress) { Её код: ```js -function back(progress, x) { - return Math.pow(progress, 2) * ((x + 1) * progress - x) +function back(x, timeFraction) { + return Math.pow(timeFraction, 2) * ((x + 1) * timeFraction - x) } ``` @@ -322,12 +292,11 @@ function back(progress, x) { Пример для `x = 1.5`: -
        -
        -
        -### Отскок +[iframe height=40 src="back" link] + +### Отскок bounce Представьте, что мы отпускаем мяч, он падает на пол, несколько раз отскакивает и останавливается. @@ -336,10 +305,10 @@ function back(progress, x) { Эта функция немного сложнее предыдущих и использует специальные коэффициенты: ```js -function bounce(progress) { +function bounce(timeFraction) { for (var a = 0, b = 1, result; 1; a += b, b /= 2) { - if (progress >= (7 - 4 * a) / 11) { - return -Math.pow((11 - 6 * a - 11 * progress) / 4, 2) + Math.pow(b, 2) + if (timeFraction >= (7 - 4 * a) / 11) { + return -Math.pow((11 - 6 * a - 11 * timeFraction) / 4, 2) + Math.pow(b, 2) } } } @@ -348,18 +317,16 @@ function bounce(progress) { Код взят из MooTools.FX.Transitions. Конечно же, есть и другие реализации `bounce`. Пример: -
        -
        -
        +[iframe height=40 src="bounce" link] ### Упругая анимация Эта функция зависит от дополнительного параметра `x`, который определяет начальный диапазон. ```js -function elastic(progress, x) { - return Math.pow(2, 10 * (progress-1)) * Math.cos(20*Math.PI*x/3*progress) +function elastic(x, timeFraction) { + return Math.pow(2, 10 * (timeFraction-1)) * Math.cos(20*Math.PI*x/3*timeFraction) } ``` @@ -367,331 +334,175 @@ function elastic(progress, x) { Пример для `x=1.5`: -
        -
        -
        + +[iframe height=40 src="elastic" link] -## Реверсивные функции (easeIn, easeOut, easeInOut) +## Реверсивные функции ease* + +Итак, у нас есть коллекция временных функций. -Обычно, JavaScript-фреймворк предоставляет несколько `delta`-функций. Их прямое использование называется "easeIn". **Иногда нужно показать анимацию в обратном режиме. Преобразование функции, которое даёт такой эффект, называется "easeOut"**. - ### easeOut -В режиме "easeOut", значение delta вычисляется так: -`deltaEaseOut(progress) = 1 - delta(1 - progress)` +В режиме "easeOut", значение timing вычисляется по формуле: `timingEaseOut(timeFraction) = 1 - timing(1 - timeFraction)` Например, функция `bounce` в режиме "easeOut": ```js -function bounce(progress) { +// обычный вариант +function bounce(timeFraction) { for (var a = 0, b = 1, result; 1; a += b, b /= 2) { - if (progress >= (7 - 4 * a) / 11) { - return -Math.pow((11 - 6 * a - 11 * progress) / 4, 2) + Math.pow(b, 2); + if (timeFraction >= (7 - 4 * a) / 11) { + return -Math.pow((11 - 6 * a - 11 * timeFraction) / 4, 2) + Math.pow(b, 2); } } } -*!* -function makeEaseOut(delta) { // преобразовать delta - return function(progress) { - return 1 - delta(1 - progress); +// преобразователь в easeOut +function makeEaseOut(timing) { + return function(timeFraction) { + return 1 - timing(1 - timeFraction); } } -*/!* -*!*var bounceEaseOut = makeEaseOut(bounce);*/!* +*!* +var bounceEaseOut = makeEaseOut(bounce); +*/!* ``` -Кликните для демонстрации: -
        -
        -
        +Полный пример -- откок в `bounceEaseOut` теперь не в начале, а в конце (и это куда красивее): -Давайте посмотрим, как преобразование `easeOut` изменяет поведение функции: +[codetabs src="bounce-easeout"] + +На этом графике видно, как преобразование `easeOut` изменяет поведение функции: -Красным цветом обозначен easeIn, а зеленым - easeOut. +Если есть анимационный эффект, такой как подпрыгивание -- он будет показан в конце, а не в начале (или наоборот, в начале, а не в конце). + +Красным цветом обозначен обычный вариант, а синим -- `easeOut`.
          -
        • Обычно анимируемый объект сначала медленно скачет внизу, а затем, в конце, резко достигает верха..
        • -
        • А после easeOut он сначала прыгает наверх, а затем медленно скачет внизу.
        • +
        • Обычно анимируемый объект сначала медленно скачет внизу, а затем, в конце, резко достигает верха..
        • +
        • А после `easeOut` -- он сначала прыгает наверх, а затем медленно скачет внизу.
        -**При `easeOut` анимация развивается в обратном временном порядке.** - -Если есть анимационный эффект, такой как подпрыгивание -- он будет показан в конце, а не в начале (или наоборот, в начале, а не в конце). ### easeInOut -А еще можно сделать так, чтобы показать эффект *и* в начале *и* в конце анимации. Соответствующее преобразование называется "easeInOut". +А еще можно сделать так, чтобы показать эффект *и в начале и в конце* анимации. Соответствующее преобразование называется "easeInOut". Его код выглядит так: ```js -if (progress <= 0.5) { // первая половина анимации) - return delta(2 * progress) / 2; +if (timeFraction <= 0.5) { // первая половина анимации) + return timing(2 * timeFraction) / 2; } else { // вторая половина - return (2 - delta(2 * (1 - progress))) / 2; + return (2 - timing(2 * (1 - timeFraction))) / 2; } ``` -Например, `easeInOut` для `bounce`: -
        -
        -
        - -*У этого примера длительность составляет 3 секунды для того, что бы хватило времени для обоих эффектов(начального и конечного).* - -Код, который трансформирует `delta`: +Код, который трансформирует `timing`: ```js -function makeEaseInOut(delta) { - return function(progress) { - if (progress < .5) - return delta(2*progress) / 2; +function makeEaseInOut(timing) { + return function(timeFraction) { + if (timeFraction < .5) + return timing(2*timeFraction) / 2; else - return (2 - delta(2*(1-progress))) / 2; + return (2 - timing(2*(1-timeFraction))) / 2; } } bounceEaseInOut = makeEaseInOut(bounce); ``` +Пример с `bounceEaseInOut`: + +[codetabs src="bounce-easeinout"] + + Трансформация "easeInOut" объединяет в себе два графика в один: `easeIn` для первой половины анимации и `easeOut` -- для второй. -Например, давайте посмотрим эффект `easeOut`/`easeInOut` на примере функции `circ`: +Это отлично видно, если посмотреть графики `easeIn`, `easeOut` и `easeInOut` на примере функции `circ`: -Как видно, график первой половины анимации представляет собой уменьшенный "easeIn", а второй -- уменьшенный "easeOut". В результате, анимация начинается и заканчивается одинаковым эффектом. - - -### Графопостроитель - -Для наглядной демонстрации в действии различных delta, как нормальных(easeIn), так и измененных(easeOut,easeInOut), я подготовил графопостроитель. - - -Открыть в новом окне. - -Выберите функцию и нажмите `Рисовать!` -
          -
        • easeIn - базовое поведение: медленная анимация в начале, с постепенным ускорением.
        • -
        • easeOut - поведение, обратное easeIn: быстрая анимация на старте, а затем все медленней и медленней.
        • -
        • easeInOut - слияние обоих поведений. Анимация разделяется на две части. Первая часть - это `easeIn`, а вторая - `easeOut`.
        • +
        • Красным цветом обозначен обычный вариант функции `circ`.
        • +
        • Зелёным -- `easeOut`.
        • +
        • Синим -- `easeInOut`.
        -Для примера, попробуйте "bounce". +Как видно, график первой половины анимации представляет собой уменьшенный "easeIn", а второй -- уменьшенный "easeOut". В результате, анимация начинается и заканчивается одинаковым эффектом. [summary] -Процесс анимации полностью в ваших руках благодаря `delta`. Вы можете сделать ее настолько реалистичной, насколько захотите. +Процесс анимации полностью в ваших руках благодаря `timing`. Её можно сделать настолько реалистичной, насколько захочется. -И кстати. Если вы когда-нибудь изучали математику... Некоторые вещи все же бывают полезны в жизни :) Можно продумать и сделать красиво. - -Впрочем, исходя из практики, можно сказать, что варианты `delta`, описанные выше, покрывают 95% потребностей в анимации. +Впрочем, исходя из практики, можно сказать, что варианты `timing`, описанные выше, покрывают 95% потребностей в анимации. [/summary] ## Сложные варианты step -Анимировать можно все, что угодно. Вместо движения, как во всех предыдущих примерах, вы можете изменять прозрачность, ширину, высоту, цвет... Все, о чем вы можете подумать! - -Достаточно лишь написать соответствующий `step`. - -### Подсветка цветом - -Функция `highlight`, представленная ниже, анимирует изменение цвета. - -```js -function highlight(elem) { - var from = [255,0,0], to = [255,255,255] - animate({ - delay: 10, - duration: 1000, - delta: linear, - step: function(delta) { - elem.style.backgroundColor = 'rgb(' + - Math.max(Math.min(parseInt((delta * (to[0]-from[0])) + from[0], 10), 255), 0) + ',' + - Math.max(Math.min(parseInt((delta * (to[1]-from[1])) + from[1], 10), 255), 0) + ',' + - Math.max(Math.min(parseInt((delta * (to[2]-from[2])) + from[2], 10), 255), 0) + ')' - } - }) -} -``` - -
        Кликните по этой надписи, чтобы увидеть функцию в действии
        - -А теперь тоже самое, но `delta = makeEaseOut(bounce)`: - -
        Кликните по этой надписи, чтобы увидеть функцию в действии
        +Анимировать можно все, что угодно. Вместо движения, как во всех предыдущих примерах, можно изменять любые CSS свойства... И не только! +Достаточно лишь написать соответствующий `draw`. ### Набор текста -Вы можете создавать интересные анимации, как, например, набор текста в "скачущем" режиме: +Можно, к примеру, анимировать набор текста в "скачущем" режиме: -[pre] - +[codetabs src="text"] - -[/pre] -Исходный код: +## Итого + +Анимация выполняется путём вызовов `requestAnimationFrame`. Для поддержки IE9- желательно подключить полифилл, который будет внутри использовать `setTimeout`. Это будет всё равно лучше, чем независимые `setInterval`. + +Реализация анимации -- очень простая и вместе с тем гибкая: ```js -function animateText(textArea) { - var text = textArea.value - var to = text.length, from = 0 - - animate({ - delay: 20, - duration: 5000, - delta: bounce, - step: function(delta) { - var result = (to-from) * delta + from - textArea.value = text.substr(0, Math.ceil(result)) +function animate(options) { + + var start = performance.now(); + + requestAnimationFrame(function animate(time) { + // timeFraction от 0 до 1 + var timeFraction = (time - start) / options.duration; + if (timeFraction > 1) timeFraction = 1; + + // текущее состояние анимации + var progress = options.timing(timeFraction) + + options.draw(progress); + + if (timeFraction < 1) { + requestAnimationFrame(animate); } - }) -} -``` -## Итого [#animate] - - -Анимация выполняется путём использования `setInterval` с маленькой задержкой, порядка 10-50мс. При каждом запуске происходит отрисовка очередного кадра. - -Анимационная функция, немного расширенная: - -```js -function animate(opts) { - - var start = new Date; - var delta = opts.delta || linear; - - var timer = setInterval(function() { - - var progress = (new Date - start) / opts.duration; - if (progress > 1) progress = 1; - - opts.step( delta(progress) ); - - if (progress == 1) { - clearInterval(timer); - opts.complete && opts.complete(); - } - }, opts.delay || 13); - - return timer; + }); } ``` Основные параметры:
          -
        • `delay` - задержка между кадрами, по умолчанию 13мс.
        • -
        • `duration` - длительность анимации в мс.
        • -
        • `delta` - функция, которая определяет состояние анимации каждый кадр. Получает часть времени от 0 до 1, возвращает завершенность анимации от 0 до 1. По умолчанию `linear`.
        • -
        • `step` - функция, которая отрисовывает состояние анимации от 0 до 1.
        • -
        • `complete` - функция для вызова после завершенности анимации.
        • -
        • Вызов `animate` возвращает таймер, чтобы анимацию можно было отменить.
        • +
        • `duration` -- длительность анимации в мс.
        • +
        • `timing` -- функция, которая определяет состояние анимации каждый кадр. Получает часть времени от 0 до 1, возвращает завершенность анимации от 0 до 1. +
        • +
        • `draw` -- функция, которая отрисовывает состояние анимации от 0 до 1.
        -Функцию `delta` можно модифицировать, используя трансформации `easeOut/easeInOut`: +Эту функцию можно улучшить, например добавить коллбэк `complete` для вызова в конце анимации. -```js -function makeEaseInOut(delta) { - return function(progress) { - if (progress < .5) return delta(2*progress) / 2; - else return (2 - delta(2*(1-progress))); / 2; - } -} +Мы рассмотрели ряд примеров для `timing` и трансформации `easeOut`, `easeInOut`, которые позволяют их разнообразить. В отличие от CSS мы не ограничены кривыми Безье, можно реализовать всё, что угодно. -function makeEaseOut(delta) { - return function(progress) { - return 1 - delta(1 - progress); - } -} -``` +Это же относится и к функции `draw`. -На основе этой общей анимационной функции можно делать и более специализированные, например `animateProp`, которая анимирует свойство `opts.elem[opts.prop]` от `opts.start px` до `opts.end px` : - -```js -//+ run -function animateProp(opts) { - var start = opts.start, end = opts.end, prop = opts.prop; - - opts.step = function(delta) { - opts.elem.style[prop] = Math.round(start + (end - start)*delta) + 'px'; - } - return animate(opts); -} - -// Использование: -animateProp({ - elem: document.body, - prop: "width", - start: 0, - duration: 2000, - end: document.body.clientWidth -}); -``` - -Можно добавить еще варианты `delta`, `step`, создать общий фреймворк для анимации с единым таймером и т.п. Собственно, это и делают библиотеки типа jQuery. - - -### Советы по оптимизации - -
        -
        Большое количество таймеров сильно нагружают процессор.
        -
        Если вы хотите запустить несколько анимаций одновременно, например, показать много падающих снежинок, то управляйте ими с помощью одного таймера. - -Дело в том, что каждый таймер вызывает перерисовку. Поэтому браузер работает гораздо эффективней, если для всех анимаций приходится делать одну объединенную перерисовку вместо нескольких. - -Фреймворки обычно используют один `setInterval` и запускают все кадры в заданном интервале. -
        -
        Помогайте браузеру в отрисовке
        -
        Браузер управляет отрисовкой дерева и элементы зависят друг от друга. - -Если анимируемый элемент лежит глубоко в DOM, то другие элементы зависят от его размеров и позиции. Даже если анимация не касается их, браузер все равно делает лишние расчёты. - -Для того, чтобы анимация меньше расходовала ресурсы процессора(и была плавнее), не анимируйте элемент, находящийся глубоко в DOM. - -Вместо этого: -
          -
        1. Для начала, удалите анимируемый элемент из DOM и прикрепите его непосредственно к `BODY`. Вам, возможно придется использовать `position: absolute` и выставить координаты.
        2. -
        3. Анимируйте элемент.
        4. -
        5. Верните его обратно в DOM. -
        - -Эта хитрость поможет выполнять сложные анимации и при этом экономить ресурсы процессора. -
        -
        - -Там, где это возможно, стоит использовать CSS-анимацию, особенно на смартфонах и планшетах, где процессор слабоват и JavaScript работает не быстро. - -[head] - - - - -[/head] \ No newline at end of file +Такая реализация анимации имеет три основных области применения: +
          +
        • Нестандартные задачи и требования, не укладывающиеся в рамки CSS.
        • +
        • Поддержка IE9-.
        • +
        • Графика, рисование на canvas.
        • +
        diff --git a/5-animation/3-js-animation/back.view/index.html b/5-animation/3-js-animation/back.view/index.html new file mode 100644 index 00000000..7cf7341c --- /dev/null +++ b/5-animation/3-js-animation/back.view/index.html @@ -0,0 +1,33 @@ + + + + + + + + + + +
        +
        +
        + + + + + + \ No newline at end of file diff --git a/5-animation/3-js-animation/back.view/style.css b/5-animation/3-js-animation/back.view/style.css new file mode 100644 index 00000000..d4c5ff32 --- /dev/null +++ b/5-animation/3-js-animation/back.view/style.css @@ -0,0 +1,14 @@ + +#brick { + width: 40px; + height: 20px; + background: #EE6B47; + position: relative; + cursor: pointer; +} + +#path { + outline: 1px solid #E8C48E; + width: 540px; + height: 20px; +} \ No newline at end of file diff --git a/5-animation/3-js-animation/bezier-linear.svg b/5-animation/3-js-animation/bezier-linear.svg new file mode 100644 index 00000000..0b0f99a6 --- /dev/null +++ b/5-animation/3-js-animation/bezier-linear.svg @@ -0,0 +1,20 @@ + + + + bezier2.svg + Created with bin/sketchtool. + + + + + + + 1 + + + + 2 + + + + \ No newline at end of file diff --git a/5-animation/3-js-animation/bounce-easeinout.view/index.html b/5-animation/3-js-animation/bounce-easeinout.view/index.html new file mode 100644 index 00000000..56edad9f --- /dev/null +++ b/5-animation/3-js-animation/bounce-easeinout.view/index.html @@ -0,0 +1,50 @@ + + + + + + + + + + +
        +
        +
        + + + + + + \ No newline at end of file diff --git a/5-animation/3-js-animation/bounce-easeinout.view/style.css b/5-animation/3-js-animation/bounce-easeinout.view/style.css new file mode 100644 index 00000000..d4c5ff32 --- /dev/null +++ b/5-animation/3-js-animation/bounce-easeinout.view/style.css @@ -0,0 +1,14 @@ + +#brick { + width: 40px; + height: 20px; + background: #EE6B47; + position: relative; + cursor: pointer; +} + +#path { + outline: 1px solid #E8C48E; + width: 540px; + height: 20px; +} \ No newline at end of file diff --git a/5-animation/3-js-animation/bounce-easeout.view/index.html b/5-animation/3-js-animation/bounce-easeout.view/index.html new file mode 100644 index 00000000..bb858d63 --- /dev/null +++ b/5-animation/3-js-animation/bounce-easeout.view/index.html @@ -0,0 +1,46 @@ + + + + + + + + + + +
        +
        +
        + + + + + + \ No newline at end of file diff --git a/5-animation/3-js-animation/bounce-easeout.view/style.css b/5-animation/3-js-animation/bounce-easeout.view/style.css new file mode 100644 index 00000000..d4c5ff32 --- /dev/null +++ b/5-animation/3-js-animation/bounce-easeout.view/style.css @@ -0,0 +1,14 @@ + +#brick { + width: 40px; + height: 20px; + background: #EE6B47; + position: relative; + cursor: pointer; +} + +#path { + outline: 1px solid #E8C48E; + width: 540px; + height: 20px; +} \ No newline at end of file diff --git a/5-animation/3-js-animation/bounce.view/index.html b/5-animation/3-js-animation/bounce.view/index.html new file mode 100644 index 00000000..a59a0cdd --- /dev/null +++ b/5-animation/3-js-animation/bounce.view/index.html @@ -0,0 +1,37 @@ + + + + + + + + + + +
        +
        +
        + + + + + + \ No newline at end of file diff --git a/5-animation/3-js-animation/bounce.view/style.css b/5-animation/3-js-animation/bounce.view/style.css new file mode 100644 index 00000000..d4c5ff32 --- /dev/null +++ b/5-animation/3-js-animation/bounce.view/style.css @@ -0,0 +1,14 @@ + +#brick { + width: 40px; + height: 20px; + background: #EE6B47; + position: relative; + cursor: pointer; +} + +#path { + outline: 1px solid #E8C48E; + width: 540px; + height: 20px; +} \ No newline at end of file diff --git a/5-animation/3-js-animation/circ.view/index.html b/5-animation/3-js-animation/circ.view/index.html new file mode 100644 index 00000000..c7641d1b --- /dev/null +++ b/5-animation/3-js-animation/circ.view/index.html @@ -0,0 +1,33 @@ + + + + + + + + + + +
        +
        +
        + + + + + + \ No newline at end of file diff --git a/5-animation/3-js-animation/circ.view/style.css b/5-animation/3-js-animation/circ.view/style.css new file mode 100644 index 00000000..d4c5ff32 --- /dev/null +++ b/5-animation/3-js-animation/circ.view/style.css @@ -0,0 +1,14 @@ + +#brick { + width: 40px; + height: 20px; + background: #EE6B47; + position: relative; + cursor: pointer; +} + +#path { + outline: 1px solid #E8C48E; + width: 540px; + height: 20px; +} \ No newline at end of file diff --git a/5-animation/3-js-animation/elastic.view/index.html b/5-animation/3-js-animation/elastic.view/index.html new file mode 100644 index 00000000..7a05a5de --- /dev/null +++ b/5-animation/3-js-animation/elastic.view/index.html @@ -0,0 +1,33 @@ + + + + + + + + + + +
        +
        +
        + + + + + + \ No newline at end of file diff --git a/5-animation/3-js-animation/elastic.view/style.css b/5-animation/3-js-animation/elastic.view/style.css new file mode 100644 index 00000000..d4c5ff32 --- /dev/null +++ b/5-animation/3-js-animation/elastic.view/style.css @@ -0,0 +1,14 @@ + +#brick { + width: 40px; + height: 20px; + background: #EE6B47; + position: relative; + cursor: pointer; +} + +#path { + outline: 1px solid #E8C48E; + width: 540px; + height: 20px; +} \ No newline at end of file diff --git a/5-animation/3-js-animation/move-raf.view/index.html b/5-animation/3-js-animation/move-raf.view/index.html new file mode 100644 index 00000000..8b61b8d8 --- /dev/null +++ b/5-animation/3-js-animation/move-raf.view/index.html @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + diff --git a/5-animation/3-js-animation/move.view/index.html b/5-animation/3-js-animation/move.view/index.html index ec6f3e03..2df013bb 100644 --- a/5-animation/3-js-animation/move.view/index.html +++ b/5-animation/3-js-animation/move.view/index.html @@ -1,13 +1,35 @@ - - + -
        -
        -
        + + + + + + diff --git a/5-animation/3-js-animation/move100.view/index.html b/5-animation/3-js-animation/move100.view/index.html deleted file mode 100644 index fb33ca3e..00000000 --- a/5-animation/3-js-animation/move100.view/index.html +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - -
        -
        -
        - - - diff --git a/5-animation/3-js-animation/quad.view/index.html b/5-animation/3-js-animation/quad.view/index.html new file mode 100644 index 00000000..d667a71a --- /dev/null +++ b/5-animation/3-js-animation/quad.view/index.html @@ -0,0 +1,33 @@ + + + + + + + + + + +
        +
        +
        + + + + + + \ No newline at end of file diff --git a/5-animation/3-js-animation/quad.view/style.css b/5-animation/3-js-animation/quad.view/style.css new file mode 100644 index 00000000..d4c5ff32 --- /dev/null +++ b/5-animation/3-js-animation/quad.view/style.css @@ -0,0 +1,14 @@ + +#brick { + width: 40px; + height: 20px; + background: #EE6B47; + position: relative; + cursor: pointer; +} + +#path { + outline: 1px solid #E8C48E; + width: 540px; + height: 20px; +} \ No newline at end of file diff --git a/5-animation/3-js-animation/quint.view/index.html b/5-animation/3-js-animation/quint.view/index.html new file mode 100644 index 00000000..c3d00097 --- /dev/null +++ b/5-animation/3-js-animation/quint.view/index.html @@ -0,0 +1,33 @@ + + + + + + + + + + +
        +
        +
        + + + + + + \ No newline at end of file diff --git a/5-animation/3-js-animation/quint.view/style.css b/5-animation/3-js-animation/quint.view/style.css new file mode 100644 index 00000000..d4c5ff32 --- /dev/null +++ b/5-animation/3-js-animation/quint.view/style.css @@ -0,0 +1,14 @@ + +#brick { + width: 40px; + height: 20px; + background: #EE6B47; + position: relative; + cursor: pointer; +} + +#path { + outline: 1px solid #E8C48E; + width: 540px; + height: 20px; +} \ No newline at end of file diff --git a/5-animation/3-js-animation/text.view/index.html b/5-animation/3-js-animation/text.view/index.html new file mode 100644 index 00000000..ca366ae4 --- /dev/null +++ b/5-animation/3-js-animation/text.view/index.html @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/5-animation/3-js-animation/text.view/style.css b/5-animation/3-js-animation/text.view/style.css new file mode 100644 index 00000000..0e92aa9f --- /dev/null +++ b/5-animation/3-js-animation/text.view/style.css @@ -0,0 +1,11 @@ + +textarea { + display: block; + border: 1px solid #BBB; + color: #444; + font-size: 110%; +} + +button { + margin-top: 10px; +} diff --git a/5-animation/3-js-animation/width.view/animate.js b/5-animation/3-js-animation/width.view/animate.js new file mode 100644 index 00000000..d2ecc454 --- /dev/null +++ b/5-animation/3-js-animation/width.view/animate.js @@ -0,0 +1,20 @@ +function animate(options) { + + var start = performance.now(); + + requestAnimationFrame(function animate(time) { + // timeFraction от 0 до 1 + var timeFraction = (time - start) / options.duration; + if (timeFraction > 1) timeFraction = 1; + + // текущее состояние анимации + var progress = options.timing(timeFraction) + + options.draw(progress); + + if (timeFraction < 1) { + requestAnimationFrame(animate); + } + + }); +} \ No newline at end of file diff --git a/5-animation/3-js-animation/width.view/index.html b/5-animation/3-js-animation/width.view/index.html index 68385955..40e17b70 100644 --- a/5-animation/3-js-animation/width.view/index.html +++ b/5-animation/3-js-animation/width.view/index.html @@ -1,13 +1,36 @@ - - + + + -
        -
        -
        + + + + + + diff --git a/5-animation/index.md b/5-animation/index.md index 51699284..72043f5d 100644 --- a/5-animation/index.md +++ b/5-animation/index.md @@ -1,3 +1,3 @@ -# Анимация [todo] +# Анимация CSS анимации. Контроль над ними из JavaScript. Анимации на чистом JavaScript. \ No newline at end of file diff --git a/figures.sketch b/figures.sketch index 598bf343..8a068553 100644 Binary files a/figures.sketch and b/figures.sketch differ