animation

This commit is contained in:
Ilya Kantor 2017-03-22 00:04:08 +03:00
parent 97c8f22bbb
commit 3285348787
129 changed files with 732 additions and 693 deletions

View file

@ -93,7 +93,7 @@ When the pattern contains `pattern:\b`, it tests that the position in string fit
- String end, and the last string character is `\w`. - String end, and the last string character is `\w`.
- Inside the string: from one side is `\w`, from the other side -- not `\w`. - Inside the string: from one side is `\w`, from the other side -- not `\w`.
For instance, in the string `subject:Hello, Java!` the following positions fit `\b`: For instance, in the string `subject:Hello, Java!` the following positions match `\b`:
![](hello-java-boundaries.png) ![](hello-java-boundaries.png)

View file

@ -0,0 +1,183 @@
# Bezier curve
Bezier curves are used in computer graphics to draw shapes, for CSS animation and in many other places.
They are actually a very simple thing, worth to study once and then feel comfortable in the world of vector graphics and advanced animations.
[cut]
## Control points
A [bezier curve](https://en.wikipedia.org/wiki/B%C3%A9zier_curve) is defined by control points.
There may be 2, 3, 4 or more.
For instance, two points curve:
![](bezier2.png)
Three points curve:
![](bezier3.png)
Four points curve:
![](bezier4.png)
If you look closely at these curves, you can immediately notice:
1. **Points are not always on curve.** That's perfectly normal, later we'll see how the curve is built.
2. **Curve order equals the number of points minus one**.
For two points we have a linear curve (that's a straight line), for three points -- quadratic curve (parabolic), for three points -- cubic curve.
3. **A curve is always inside the [convex hull](https://en.wikipedia.org/wiki/Convex_hull) of control points:**
![](bezier4-e.png) ![](bezier3-e.png)
Because of that last property, in computer graphics it's possible to optimize intersection tests. If convex hulls do not intersect, then curves do not either.
The main value of Bezier curves for drawing -- by moving the points the curve is changing *in intuitively obvious way*.
Try to move points using a mouse in the example below:
[iframe src="demo.svg?nocpath=1&p=0,0,0.5,0,0.5,1,1,1" height=370]
**As you can notice, the curve stretches along the tangential lines 1 -> 2 и 3 -> 4.**
After some practice it becomes obvious how to place points to get the needed curve. And by connecting several curves we can get practically anything.
Here are some examples:
![](bezier-car.png) ![](bezier-letter.png) ![](bezier-vase.png)
## Maths
A Bezier curve can be described using a mathematical formula.
As we'll see soon -- there's no need to know it. But for completeness -- here you go.
We have coordinates of control points <code>P<sub>i</sub></code>: the first control point has coordinates <code>P<sub>1</sub> = (x<sub>1</sub>, y<sub>1</sub>)</code>, the second: <code>P<sub>2</sub> = (x<sub>2</sub>, y<sub>2</sub>)</code>, and so on.
Then the curve coordinates are described by the equation that depends on the parameter `t` from the segment `[0,1]`.
- For two points:
<code>P = (1-t)P<sub>1</sub> + tP<sub>2</sub></code>
- For three points:
<code>P = (1t)<sup>2</sup>P<sub>1</sub> + 2(1t)tP<sub>2</sub> + t<sup>2</sup>P<sub>3</sub></code>
- For four points:
<code>P = (1t)<sup>3</sup>P<sub>1</sub> + 3(1t)<sup>2</sup>tP<sub>2</sub> +3(1t)t<sup>2</sup>P<sub>3</sub> + t<sup>3</sup>P<sub>4</sub></code>
These are vector equations.
We can rewrite them coordinate-by-coordinate, for instance the 3-point variant:
- <code>x = (1t)<sup>2</sup>x<sub>1</sub> + 2(1t)tx<sub>2</sub> + t<sup>2</sup>x<sub>3</sub></code>
- <code>y = (1t)<sup>2</sup>y<sub>1</sub> + 2(1t)ty<sub>2</sub> + t<sup>2</sup>y<sub>3</sub></code>
Instead of <code>x<sub>1</sub>, y<sub>1</sub>, x<sub>2</sub>, y<sub>2</sub>, x<sub>3</sub>, y<sub>3</sub></code> we should put coordinates of 3 control points, and `t` runs from `0` to `1`. The set of values `(x,y)` for each `t` forms the curve.
That's probably too scientific, not very obvious why curves look like that, and how they depend on control points.
Another drawing algorithm may be easier to understand.
## De Casteljau's algorithm
[De Casteljau's algorithm](https://en.wikipedia.org/wiki/De_Casteljau%27s_algorithm) is identical to the mathematical definition of the curve, but visually shows how it is built.
Let's explain it on the 3-points example.
Here's the demo. Points can be moved by the mouse. Press the "play" button to run it.
[iframe src="demo.svg?p=0,0,0.5,1,1,0&animate=1" height=370]
**De Casteljau's algorithm of building the 3-point bezier curve:**
1. Draw control points. In the demo above they are labeled: `1`, `2`, `3`.
2. Build segments between control points 1 -> 2 -> 3. In the demo above they are <span style="color:#825E28">brown</span>.
3. The parameter `t` moves from `0` to `1`. In the example above the step `0.05` is used: the loop goes over `0, 0.05, 0.1, 0.15, ... 0.95, 1`.
For each of these values of `t`:
- On each <span style="color:#825E28">brown</span> segment we take a point located on the distance proportional to `t` from its beginning. As there are two segments, we have two points.
For instance, for `t=0` -- both points will be at the beginning of segments, and for `t=0.25` -- on the 25% of segment length from the beginning, for `t=0.5` -- 50%(the middle), for `t=1` -- in the end of segments.
- Connect the points. On the picture below the connecting segment is painted <span style="color:#167490">blue</span>.
| For `t=0.25` | For `t=0.5` |
| ------------------------ | ---------------------- |
| ![](bezier3-draw1.png) | ![](bezier3-draw2.png) |
4. On the <span style="color:#167490">new</span> segment we take a point on the distance proportional to `t`. That is, for `t=0.25` (the left picture) we have a point at the end of the left quarter of the segment, and for `t=0.5` (the right picture) -- in the middle of the segment. On pictures above that point is <span style="color:red">red</span>.
5. As `t` runs from `0` to `1`, every value of `t` adds a point to the curve. The set of such points forms the Bezier curve. It's red and parabolic on the pictures above.
That was a process for 3 points. But the same is for 4 points.
The demo for 4 points (points can be moved by mouse):
[iframe src="demo.svg?p=0,0,0.5,0,0.5,1,1,1&animate=1" height=370]
The algorithm:
- Control points are connected by segments: 1 -> 2, 2 -> 3, 3 -> 4. We have 3 <span style="color:#825E28">brown</span> segments.
- For each `t` in the interval from `0` to `1`:
- We take points on these segments on the distance proportional to `t` from the beginning. These points are connected, so that we have two <span style="color:#0A0">green segments</span>.
- On these segments we take points proportional to `t`. We get one <span style="color:#167490">blue segment</span>.
- On the blue segment we take a point proportional to `t`. On the example above it's <span style="color:red">red</span>.
- These points together form the curve.
The algorithm is recursive. For each `t` from `0` to `1` we have first N segments, then take points of them and connect -- N-1 segments and so on till we have one point. These make the curve.
Press the "play" button in the example above to see it in action.
Move examples of curves:
[iframe src="demo.svg?p=0,0,0,0.75,0.25,1,1,1&animate=1" height=370]
With other points:
[iframe src="demo.svg?p=0,0,1,0.5,0,0.5,1,1&animate=1" height=370]
Loop form:
[iframe src="demo.svg?p=0,0,1,0.5,0,1,0.5,0&animate=1" height=370]
Not smooth Bezier curve:
[iframe src="demo.svg?p=0,0,1,1,0,1,1,0&animate=1" height=370]
As the algorithm is recursive, we can build Bezier curves of any order: using 5, 6 or more control points. But on practice they are less useful. Usually we take 2-3 points, and for complex lines glue several curves together. That's simpler for support and in calculations.
```smart header="How to draw a curve *through* given points?"
We use control points for a Bezier curve. As we can see, they are not on the curve. Or, to be precise, the first and the last one do belong to curve, but others don't.
Sometimes we have another task: to draw a curve *through several points*, so that all of them are on a single smooth curve. That task is called [interpolation](https://en.wikipedia.org/wiki/Interpolation), and here we don't cover it.
There are mathematical formulas for such curves, for instance [Lagrange polynomial](https://en.wikipedia.org/wiki/Lagrange_polynomial).
In computer graphics [spline interpolation](https://en.wikipedia.org/wiki/Spline_interpolation) is often used to build smooth curves that connect many points.
```
## Summary
Bezier curves are defined by their control points.
We saw two definitions of Bezier curves:
1. Using a mathematical formulas.
2. Using a drawing process: De Casteljau's algorithm
Good properties of Bezier curves:
- We can draw smooth lines with a mouse by moving around control points.
- Complex shapes can be made of several Bezier curves.
Usage:
- In computer graphics, modeling, vector graphic editors. Fonts are described by Bezier curves.
- In web development -- for graphics on Canvas and in the SVG format. By the way, "live" examples above are written in SVG. They are actually a single SVG document that is given different points as parameters. You can open it in a separate window and see the source: [demo.svg](demo.svg?p=0,0,1,0.5,0,0.5,1,1&animate=1).
- In CSS animation to describe the path and speed of animation.

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

View file

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Before After
Before After

View file

@ -1,181 +0,0 @@
# Bezier curves
Bezier curves are used in computer graphics ...
Кривые Безье используются в компьютерной графике для рисования плавных изгибов, в CSS-анимации и много где ещё.
Несмотря на "умное" название -- это очень простая штука.
В принципе, можно создавать анимацию и без знания кривых Безье, но стоит один раз изучить эту тему хотя бы для того, чтобы в дальнейшем с комфортом пользоваться этим замечательным инструментом. Тем более что в мире векторной графики и продвинутых анимаций без них никак.
[cut]
## Виды кривых Безье
[Кривая Безье](http://ru.wikipedia.org/wiki/%D0%9A%D1%80%D0%B8%D0%B2%D0%B0%D1%8F_%D0%91%D0%B5%D0%B7%D1%8C%D0%B5) задаётся опорными точками.
Их может быть две, три, четыре или больше. Например:
По двум точкам:
![](bezier2.png)
По трём точкам:
![](bezier3.png)
По четырём точкам:
![](bezier4.png)
Если вы посмотрите внимательно на эти кривые, то "на глазок" заметите:
1. **Точки не всегда на кривой.** Это совершенно нормально, как именно строится кривая мы рассмотрим чуть позже.
2. **Степень кривой равна числу точек минус один**.
Для двух точек -- это линейная кривая (т.е. прямая), для трёх точек -- квадратическая кривая (парабола), для четырёх -- кубическая.
3. **Кривая всегда находится внутри [выпуклой оболочки](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), образованной опорными точками:**
![](bezier4-e.png) ![](bezier3-e.png)
Благодаря последнему свойству в компьютерной графике можно оптимизировать проверку пересечений двух кривых. Если их выпуклые оболочки не пересекаются, то и кривые тоже не пересекутся.
Основная ценность кривых Безье для рисования -- в том, что, двигая точки, кривую можно менять, причём кривая при этом *меняется интуитивно понятным образом*.
Попробуйте двигать точки мышью в примере ниже:
[iframe src="demo.svg?nocpath=1&p=0,0,0.5,0,0.5,1,1,1" height=370]
**Как можно заметить, кривая натянута по касательным 1 -> 2 и 3 -> 4.**
После небольшой практики становится понятно, как расположить точки, чтобы получить нужную форму. А, соединяя несколько кривых, можно получить практически что угодно.
Вот некоторые примеры:
![](bezier-car.png) ![](bezier-letter.png) ![](bezier-vase.png)
## Математика
У кривых Безье есть математическая формула.
Как мы увидим далее, для пользования кривыми Безье знать её нет особенной необходимости, но для полноты картины -- вот она.
**Координаты кривой описываются в зависимости от параметра `t⋲[0,1]`**
- Для двух точек:
<code>P = (1-t)P<sub>1</sub> + tP<sub>2</sub></code>
- Для трёх точек:
<code>P = (1t)<sup>2</sup>P<sub>1</sub> + 2(1t)tP<sub>2</sub> + t<sup>2</sup>P<sub>3</sub></code>
- Для четырёх точек:
<code>P = (1t)<sup>3</sup>P<sub>1</sub> + 3(1t)<sup>2</sup>tP<sub>2</sub> +3(1t)t<sup>2</sup>P<sub>3</sub> + t<sup>3</sup>P<sub>4</sub></code>
Вместо <code>P<sub>i</sub></code> нужно подставить координаты i-й опорной точки <code>(x<sub>i</sub>, y<sub>i</sub>)</code>.
Эти уравнения векторные, то есть для каждой из координат:
- <code>x = (1t)<sup>2</sup>x<sub>1</sub> + 2(1t)tx<sub>2</sub> + t<sup>2</sup>x<sub>3</sub></code>
- <code>y = (1t)<sup>2</sup>y<sub>1</sub> + 2(1t)ty<sub>2</sub> + t<sup>2</sup>y<sub>3</sub></code>
Вместо <code>x<sub>1</sub>, y<sub>1</sub>, x<sub>2</sub>, y<sub>2</sub>, x<sub>3</sub>, y<sub>3</sub></code> подставляются координаты трёх опорных точек, и в то время как `t` пробегает множество от `0` до `1`, соответствующие значения `(x, y)` как раз и образуют кривую.
Впрочем, это чересчур наукообразно, не очень понятно, почему кривые именно такие, и как зависят от опорных точек. С этим нам поможет разобраться другой, более наглядный алгоритм.
## Рисование "де Кастельжо"
[Метод де Кастельжо](http://ru.wikipedia.org/wiki/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%B4%D0%B5_%D0%9A%D0%B0%D1%81%D1%82%D0%B5%D0%BB%D1%8C%D0%B6%D0%BE) идентичен математическому определению кривой и наглядно показывает, как она строится.
Посмотрим его на примере трёх точек (точки можно двигать). Нажатие на кнопку "play" запустит демонстрацию.
[iframe src="demo.svg?p=0,0,0.5,1,1,0&animate=1" height=370]
**Алгоритм построения кривой по "методу де Кастельжо":**
1. Рисуем опорные точки. В примере выше это `1`, `2`, `3`.
2. Строятся отрезки между опорными точками 1 -> 2 -> 3. На рисунке выше они <span style="color:#825E28">коричневые</span>.
3. Параметр `t` пробегает значения от `0` до `1`. В примере выше использован шаг `0.05`, т.е. в цикле `0, 0.05, 0.1, 0.15, ... 0.95, 1`.
Для каждого из этих значений `t`:
- На каждом из <span style="color:#825E28">коричневых</span> отрезков берётся точка, находящаяся от начала на расстоянии от 0 до `t` пропорционально длине. Так как коричневых отрезков -- два, то и точек две штуки.
Например, при `t=0` -- точки будут в начале, при `t=0.25` -- на расстоянии в 25% от начала отрезка, при `t=0.5` -- 50%(на середине), при `t=1` -- в конце отрезков.
- Эти точки соединяются. На рисунке ниже соединяющий их отрезок изображён <span style="color:blue">синим</span>.
| При `t=0.25` | При `t=0.5` |
| ------------------------ | ---------------------- |
| ![](bezier3-draw1.png) | ![](bezier3-draw2.png) |
4. На <span style="color:blue">получившемся</span> отрезке берётся точка на расстоянии, соответствующем `t`. То есть, для `t=0.25` (первый рисунок) получаем точку в конце первой четверти отрезка, для `t=0.5` (второй рисунок) -- в середине отрезка. На рисунках выше эта точка отмечена <span style="color:red">красным</span>.
5. По мере того как `t` пробегает последовательность от `0` до `1`, каждое значение `t` добавляет к красной кривой точку. **Совокупность таких точек для всех значений `t` образуют кривую Безье.**
Это был процесс для построения по трём точкам. Но то же самое происходит и с четырьмя точками.
Демо для четырёх точек (точки можно двигать):
[iframe src="demo.svg?p=0,0,0.5,0,0.5,1,1,1&animate=1" height=370]
Алгоритм:
- Точки по порядку соединяются отрезками: 1 -> 2, 2 -> 3, 3 -> 4. Получается три коричневых отрезка.
- На отрезках берутся точки, соответствующие текущему `t`, соединяются. Получается два <span style="color:#0A0">зелёных отрезка</span>.
- На этих отрезках берутся точки, соответствующие текущему `t`, соединяются. Получается один <span style="color:blue">синий отрезок</span>.
- На синем отрезке берётся точка, соответствующая текущему `t`. При запуске примера выше она <span style="color:red">красная</span>.
- Эти точки описывают кривую.
Этот алгоритм рекурсивен. Для каждого `t` из интервала от `0` до `1` по этому правилу, соединяя точки на соответствующем расстоянии, из 4 отрезков делается 3, затем из 3 так же делается 2, затем из 2 отрезков -- точка, описывающая кривую для данного значения `t`.
Нажмите на кнопку "play" в примере выше, чтобы увидеть это в действии.
Ещё примеры кривых:
[iframe src="demo.svg?p=0,0,0,0.75,0.25,1,1,1&animate=1" height=370]
С другими точками:
[iframe src="demo.svg?p=0,0,1,0.5,0,0.5,1,1&animate=1" height=370]
Петелька:
[iframe src="demo.svg?p=0,0,1,0.5,0,1,0.5,0&animate=1" height=370]
Пример негладкой кривой Безье:
[iframe src="demo.svg?p=0,0,1,1,0,1,1,0&animate=1" height=370]
Так как алгоритм рекурсивен, то аналогичным образом могут быть построены кривые Безье и более высокого порядка: по пяти точкам, шести и так далее. Однако на практике они менее полезны. Обычно используются 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%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).
```
## Итого
Кривые Безье задаются опорными точками.
Мы рассмотрели два определения кривых:
1. Через математическую формулу.
2. Через процесс построения де Кастельжо.
Их удобство в том, что:
- Можно легко нарисовать плавные линии вручную, передвигая точки мышкой.
- Более сложные изгибы и линии можно составить, если соединить несколько кривых Безье.
Применение:
- В компьютерной графике, моделировании, в графических редакторах. Шрифты описываются с помощью кривых Безье.
- В веб-разработке -- для графики на Canvas или в формате SVG. Кстати, все живые примеры выше написаны на SVG. Фактически, это один SVG-документ, к которому точки передаются параметрами. Вы можете открыть его в отдельном окне и посмотреть исходник: [demo.svg](demo.svg?p=0,0,1,0.5,0,0.5,1,1&animate=1).
- В CSS-анимации, для задания траектории или скорости передвижения.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View file

@ -0,0 +1,422 @@
# CSS-animations
CSS animations allow to do simple animations without JavaScript at all.
JavaScript can be used to control CSS animation and make them even better with a little of code.
[cut]
## CSS transitions [#css-transition]
The idea of CSS transitions is simple. We describe a property and how its changes should be animated. When the property changes, the browser paints the animation.
That is: all we need is to change the property. And the fluent transition is made by the browser.
For instance, the CSS below animates changes of `background-color` for 3 seconds:
```css
.animated {
transition-property: background-color;
transition-duration: 3s;
}
```
Now if an element has `.animated` class, any change of `background-color` is animated during 3 seconds.
Click the button below to animate the background:
```html run autorun height=60
<button id="color">Click me</button>
<style>
#color {
transition-property: background-color;
transition-duration: 3s;
}
</style>
<script>
color.onclick = function() {
this.style.backgroundColor = 'red';
};
</script>
```
There are 5 properties to describe CSS transitions:
- `transition-property`
- `transition-duration`
- `transition-timing-function`
- `transition-delay`
We'll cover them in a moment, for now let's note that the common `transition` property allows to declare them together in the order: `property duration timing-function delay`, and also animate multiple properties at once.
For instance, this button animates both `color` and `font-size`:
```html run height=80 autorun no-beautify
<button id="growing">Click me</button>
<style>
#growing {
*!*
transition: font-size 3s, color 2s;
*/!*
}
</style>
<script>
growing.onclick = function() {
this.style.fontSize = '36px';
this.style.color = 'red';
};
</script>
```
Now let's cover animation properties one by one.
## transition-property
In `transition-property` we write a list of property to animate, for instance: `left`, `margin-left`, `height`, `color`.
Not all properties can be animated, but [many of them](http://www.w3.org/TR/css3-transitions/#animatable-properties-). The value `all` means "animate all properties".
## transition-duration
In `transition-duration` we can specify how long the animation should take. The time should be in [CSS time format](http://www.w3.org/TR/css3-values/#time): in seconds `s` or milliseconds `ms`.
## transition-delay
In `transition-delay` we can specify the delay *before* the animation. For instance, if `transition-delay: 1s`, then animation starts after 1 second after the change.
Negative values are also possible. Then the animation starts from the middle. For instance, if `transition-duration` is `2s`, and the delay is `-1s`, then the animation takes 1 second and starts from the half.
Here's the animation shifts numbers from `0` to `9` using CSS `translate` property:
[codetabs src="digits"]
The `transform` property is animated like this:
```css
#stripe.animate {
transform: translate(-90%);
transition-property: transform;
transition-duration: 9s;
}
```
In the example above JavaScript adds the class `.animate` to the element -- and the animation starts:
```js
stripe.classList.add('animate');
```
We can also start it "from the middle", from the exact number, e.g. corresponding to the current second, using the negative `transition-delay`.
Here if you click the digit -- it starts the animation from the current second:
[codetabs src="digits-negative-delay"]
JavaScript does it by an extra line:
```js
stripe.onclick = function() {
let sec = new Date().getSeconds() % 10;
*!*
// for instance, -3s here starts the animation from the 3rd second
stripe.style.transitionDelay = '-' + sec + 's';
*/!*
stripe.classList.add('animate');
};
```
## transition-timing-function
Timing function describes how the animation process is distributed along the time. Will it start slowly and then go fast or vise versa.
That's the most complicated property from the first sight. But it becomes very simple if we devote a bit time to it.
That property accepts two kinds of values: a Bezier curve or steps. Let's start from the curve, as it's used more often.
### Bezier curve
The timing function can be set as a [Bezier curve](/bezier-curve) with 4 control points that satisfies the conditions:
1. First control point: `(0,0)`.
2. Last control point: `(1,1)`.
3. For intermediate points values of `x` must be in the interval `0..1`, `y` can be anything.
The syntax for a Bezier curve in CSS: `cubic-bezier(x2, y2, x3, y3)`. Here we need to specify only 2nd and 3rd control points, because the 1st one is fixed to `(0,0)` and the 4th one is `(1,1)`.
The timing function describes how fast the animation process goes in time.
- The `x` axis is the time: `0` -- the starting moment, `1` -- the last moment of `transition-duration`.
- The `y` axis specifies the completion of the process: `0` -- the starting value of the property, `1` -- the final value.
The simplest variant is when the animation goes uniformly, with the same linear speed. That can be specified by the curve `cubic-bezier(0, 0, 1, 1)`.
Here's how that curve looks:
![](bezier-linear.png)
...As we can see, it's just a straight line. As the time (`x`) passes, the completion (`y`) of the animation steadily goes from `0` to `1`.
The train in the example below goes from left to right with the permanent speed (click it):
[codetabs src="train-linear"]
The CSS `transition` is bazed on that curve:
```css
.train {
left: 0;
transition: left 5s cubic-bezier(0, 0, 1, 1);
/* JavaScript sets left to 450px */
}
```
...And how can we show a train slowing down?
We can use another Bezier curve: `cubic-bezier(0.0, 0.5, 0.5 ,1.0)`.
The graph:
![](train-curve.png)
As we can see, the process starts fast: the curve soars up high, and then slower and slower.
Here's the timing function in action (click the train):
[codetabs src="train"]
CSS:
```css
.train {
left: 0;
transition: left 5s cubic-bezier(0, .5, .5, 1);
/* JavaScript sets left to 450px */
}
```
There are several built-in curves: `linear`, `ease`, `ease-in`, `ease-out` and `ease-in-out`.
The `linear` is a shorthand for `cubic-bezier(0, 0, 1, 1)` -- a straight line, we saw it already.
Other names are shorthands for the following `cubic-bezier`:
| <code>ease</code><sup>*</sup> | <code>ease-in</code> | <code>ease-out</code> | <code>ease-in-out</code> |
|-------------------------------|----------------------|-----------------------|--------------------------|
| <code>(0.25, 0.1, 0.25, 1.0)</code> | <code>(0.42, 0, 1.0, 1.0)</code> | <code>(0, 0, 0.58, 1.0)</code> | <code>(0.42, 0, 0.58, 1.0)</code> |
| ![ease, figure](ease.png) | ![ease-in, figure](ease-in.png) | ![ease-out, figure](ease-out.png) | ![ease-in-out, figure](ease-in-out.png) |
`*` -- by default, if there's no timing function, `ease` is used.
So we could use `ease-out` for our slowing down train:
```css
.train {
left: 0;
transition: left 5s ease-out;
/* transition: left 5s cubic-bezier(0, .5, .5, 1); */
}
```
But it looks a bit differently.
**A Bezier curve can make the animation "jump out" of its range.**
The control points on the curve can have any `y` coordinates: even negative or huge. Then the Bezier curve would also jump very low or high, making the animation go beyound its normal range.
In the example below the animation code is:
```css
.train {
left: 100px;
transition: left 5s cubic-bezier(.5, -1, .5, 2);
/* JavaScript sets left to 400px */
}
```
The property `left` should animate from `100px` to `400px`.
But if you click the train, you'll see that:
- First, the train goes *back*: `left` becomes less than `100px`.
- Then it goes forward, a little bit farther than `400px`.
- And then back again -- to `400px`.
[codetabs src="train-over"]
Why it happens -- pretty obvious if we look at the graph of the given Bezier curve:
![](bezier-train-over.png)
We moved the `y` coordinate of the 2nd point below zero, and for the 3rd point we made put it over `1`, so the curve goes out of the "regular" quadrant. The `y` is out of the "standard" range `0..1`.
As we know, `y` measures "the completion of the animation process". The value `y = 0` corresponds to the starting property value and `y = 1` -- the ending value. So values `y<0` move the property lower than the starting `left` and `y>1` -- over the final `left`.
That's a "soft" variant for sure. If we put `y` values like `-99` and `99` then the train would jump out of the range much more.
But how to make the Bezier curve for a specific task? There are many tools. For instance, we can do it on the site <http://cubic-bezier.com/>.
### Steps
Timing function `steps(number of steps[, start/end])` allows to split animation into steps.
Let's see that in an example with digits. We'll make the digits change not in a smooth, but in a discrete way.
For that we split the animation into 9 steps:
```css
#stripe.animate {
transform: translate(-90%);
transition: transform 9s *!*steps(9, start)*/!*;
}
```
In action `step(9, start)`:
[codetabs src="step"]
The first argument of `steps` is the number of steps. The transform will be split into 9 parts (10% each). The time interval is divided as well: 9 seconds split into 1 second intervals.
The second argument is one of two words: `start` or `end`.
The `start` means that in the beginning of animation we need to do make the first step immediately.
We can observe that during the animation: when we click on the digit it changes to `1` (the first step) immediately, and then changes in the beginning of the next second.
The process is progressing like this:
- `0s` -- `-10%` (first change in the beginning of the 1st second, immediately)
- `1s` -- `-20%`
- ...
- `8s` -- `-80%`
- (the last second shows the final value).
The alternative value `end` would mean that the change should be applied not in the beginning, but at the end of each second.
So the process would go like this:
- `0s` -- `0`
- `1s` -- `-10%` (first change at the end of the 1st second)
- `2s` -- `-20%`
- ...
- `9s` -- `-90%`
In action `step(9, end)`:
[codetabs src="step-end"]
There are also shorthand values:
- `step-start` -- is the same as `steps(1, start)`. That is, the animation starts immediately and takes 1 step. So it starts and finishes immediately, as if there were no animation.
- `step-end` -- the same as `steps(1, end)`: make the animation in a single step at the end of `transition-duration`.
These values are rarely used, because that's not really animation, but rather a single-step change.
## Event transitionend
When the CSS animation finishes the `transitionend` event triggers.
It is widely used to do an action after the animation is done. Also we can join animations.
For instance, the ship in the example below starts to swim there and back on click, each time farther and farther to the right:
[iframe src="boat" height=300 edit link]
The animation is initiated by the function `go` that re-runs each time when the transition finishes and flips the direction:
```js
boat.onclick = function() {
//...
let times = 1;
function go() {
if (times % 2) {
// swim to the right
boat.classList.remove('back');
boat.style.marginLeft = 100 * times + 200 + 'px';
} else {
// swim to the left
boat.classList.add('back');
boat.style.marginLeft = 100 * times - 200 + 'px';
}
}
go();
boat.addEventListener('transitionend', function() {
times++;
go();
});
};
```
The event object for `transitionend` has few specific properties:
`event.propertyName`
: The property that has finished animating. Can be good if we animate multiple properties simultaneously.
`event.elapsedTime`
: The time (in secods) that the animation took, without `transition-delay`.
## Keyframes
We can join multiple simple animations together using the `@keyframes` CSS rule.
It specifies the "name" of the animation and rules: what, when and where to animate. Then using the `animation` property we attach the animation to the element and specify additional parameters for it.
Here's an example with explanations:
```html run height=60 autorun="no-epub" no-beautify
<div class="progress"></div>
<style>
*!*
@keyframes go-left-right { /* give it a name: "go-left-right" */
from { left: 0px; } /* animate from left: 0px */
to { left: calc(100% - 50px); } /* animate to left: 100%-50px */
}
*/!*
.progress {
*!*
animation: go-left-right 3s infinite alternate;
/* apply the animation "go-left-right" to the element
duration 3 seconds
number of times: infinite
alternate direction every time
*/
*/!*
position: relative;
border: 2px solid green;
width: 50px;
height: 20px;
background: lime;
}
</style>
```
There are many articles about `@keyframes` and a [detailed specification](https://drafts.csswg.org/css-animations/), so we won't go into more details here.
Probably you won't need it often, unless everything is in the constant move on your sites.
## Summary
CSS animations allow to smoothly (or not) animate changes to one or multiple CSS properties.
They are good for most animation tasks. We're also able to use JavaScript for animations, the next chapter is devoted to that.
Limitations of CSS animations compared to JavaScript animations:
```compare
- JavaScript animations are more flexible. They can implement any animation logic, like "explosion" of an element. We can create new elements in JavaScript for purposes of animation.
+ Simple things done simple.
+ Fast and easy for CPU.
```
The vast majority of animations uses the described CSS properties. And maybe the `transitionend` event to bind some JavaScript after it.
But in the next chapter we'll do some JavaScript animations to cover more complex cases.

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View file

@ -8,9 +8,9 @@
<body> <body>
<div id="digit"><span id="stripe">0123456789</span></div> Click below to animate:
<div id="digit"><div id="stripe">0123456789</div></div>
<script src="script.js"></script> <script src="script.js"></script>
</body> </body>
</html> </html>

View file

@ -0,0 +1,17 @@
#digit {
width: .5em;
overflow: hidden;
font: 32px monospace;
cursor: pointer;
}
#stripe {
display: inline-block
}
#stripe.animate {
transform: translate(-90%);
transition-property: transform;
transition-duration: 9s;
transition-timing-function: linear;
}

View file

@ -8,7 +8,9 @@
<body> <body>
<div id="digit"><span id="stripe">0123456789</span></div> Click below to animate:
<div id="digit"><div id="stripe">0123456789</div></div>
<script src="script.js"></script> <script src="script.js"></script>
</body> </body>

View file

@ -0,0 +1,17 @@
#digit {
width: .5em;
overflow: hidden;
font: 32px monospace;
cursor: pointer;
}
#stripe {
display: inline-block
}
#stripe.animate {
transform: translate(-90%);
transition-property: transform;
transition-duration: 9s;
transition-timing-function: linear;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

View file

@ -8,7 +8,9 @@
<body> <body>
<div id="digit"><span id="stripe">0123456789</span></div> Click below to animate:
<div id="digit"><div id="stripe">0123456789</div></div>
<script> <script>
digit.onclick = function() { digit.onclick = function() {

View file

@ -0,0 +1,17 @@
#digit {
width: .5em;
overflow: hidden;
font: 32px monospace;
cursor: pointer;
}
#stripe {
display: inline-block
}
#stripe.animate {
transform: translate(-90%);
transition-property: transform;
transition-duration: 9s;
transition-timing-function: steps(9, end);
}

View file

@ -8,7 +8,9 @@
<body> <body>
<div id="digit"><span id="stripe">0123456789</span></div> Click below to animate:
<div id="digit"><div id="stripe">0123456789</div></div>
<script> <script>
digit.onclick = function() { digit.onclick = function() {

View file

@ -0,0 +1,17 @@
#digit {
width: .5em;
overflow: hidden;
font: 32px monospace;
cursor: pointer;
}
#stripe {
display: inline-block
}
#stripe.animate {
transform: translate(-90%);
transition-property: transform;
transition-duration: 9s;
transition-timing-function: steps(9, start);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View file

@ -1,421 +0,0 @@
# CSS-анимации
Все современные браузеры, кроме IE9- поддерживают <a href="http://www.w3.org/TR/css3-transitions/">CSS transitions</a> и <a href="http://www.w3.org/TR/css3-animations/">CSS animations</a>, которые позволяют реализовать анимацию средствами CSS, без привлечения JavaScript.
Однако, как мы увидим далее, для более тонкого контроля анимации JavaScript вовсе не будет лишним.
[cut]
## CSS transitions [#css-transition]
Идея проста. Мы указываем, что некоторое свойство будет анимироваться при помощи специальных CSS-правил. Далее, при изменении этого свойства, браузер сам обработает анимацию.
Например, CSS, представленный ниже, 3 секунды анимирует свойство `background-color`.
```css
.animated {
transition-property: background-color;
transition-duration: 3s;
}
```
Теперь любое изменение фонового цвета будет анимироваться в течение 3х секунд.
При клике на эту кнопку происходит анимация её фона:
```html run autorun height=60
<button id="color">Кликни меня</button>
<style>
#color {
transition-property: background-color;
transition-duration: 3s;
}
</style>
<script>
color.onclick = function() {
this.style.backgroundColor = 'red';
}
</script>
```
Есть всего 5 свойств, задающих анимацию:
- `transition-property`
- `transition-duration`
- `transition-timing-function`
- `transition-delay`
Далее мы изучим их все, пока лишь заметим, что общее свойство `transition` может перечислять их все, в порядке: `property duration timing-function delay`, а также задавать анимацию нескольких свойств сразу.
Например, при клике на эту кнопку анимируются одновременно цвет и размер шрифта:
```html run height=80 autorun no-beautify
<button id="growing">Кликни меня</button>
<style>
#growing {
*!*
transition: font-size 3s, color 2s;
*/!*
}
</style>
<script>
growing.onclick = function() {
this.style.fontSize='36px';
this.style.color='red';
}
</script>
```
Далее мы рассмотрим свойства анимации по отдельности.
## 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() {
let 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. Для промежуточных точек значения `x` должны быть в интервале `0..1`, `y` -- любыми.
Синтаксис для задания кривой Безье в CSS: `cubic-bezier(x2, y2, x3, y3)`. В нём указываются координаты только двух точек: второй и третьей, так как первая и последняя фиксированы.
Она указывает, как быстро развивается процесс анимации во времени.
- По оси `x` идёт время: `0` -- начальный момент, `1` -- конец времени `transition-duration`.
- По оси `y` -- завершённость процесса: `0` -- начальное значение анимируемого свойства, `1` -- конечное.
Самый простой вариант -- это когда процесс развивается равномерно, "линейно" по времени. Это можно задать кривой Безье `cubic-bezier(0, 0, 1, 1)`.
График этой "кривой" таков:
![](bezier-linear.png)
...Как видно, это просто прямая. По мере того, как проходит время `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)`.
График этой кривой:
![](train-curve.png)
Как видно, процесс вначале развивается быстро -- кривая резко идёт вверх, а затем всё медленнее, медленнее.
Вы можете увидеть эту временную функцию в действии, кликнув на поезд:
[codetabs src="train"]
CSS для анимации:
```css
.train {
left: 0;
transition: left 5s cubic-bezier(0, .5, .5, 1);
/* JavaScript ставит значение left: 450px */
}
```
Существует несколько стандартных обозначений кривых: `linear`, `ease`, `ease-in`, `ease-out` и `ease-in-out`.
Значение `linear` -- это прямая, мы её уже видели.
Остальные кривые являются короткой записью следующих `cubic-bezier`:
| <code>ease</code><sup>*</sup> | <code>ease-in</code> | <code>ease-out</code> | <code>ease-in-out</code> |
|-------------------------------|----------------------|-----------------------|--------------------------|
| <code>(0.25, 0.1, 0.25, 1.0)</code> | <code>(0.42, 0, 1.0, 1.0)</code> | <code>(0, 0, 0.58, 1.0)</code> | <code>(0.42, 0, 0.58, 1.0)</code> |
| ![ease, figure](ease.png) | ![ease-in, figure](ease-in.png) | ![ease-out, figure](ease-out.png) | ![ease-in-out, figure](ease-in-out.png) |
`*` -- По умолчанию, если никакой временной функции не указано, используется `ease`.
**Кривая Безье может заставить анимацию "выпрыгивать" за пределы диапазона.**
Допустимо указывать для кривой Безье как отрицательные `y`, так и сколь угодно большие. При этом кривая Безье будет также по `y` выскакивать за пределы диапазона `0..1`, представляющего собой начало-конец значения.
В примере ниже CSS-код анимации таков:
```css
.train {
left: 100px;
transition: left 5s cubic-bezier(.5, -1, .5, 2);
/* JavaScript поменяет left на 400px */
}
```
Свойство `left` должно меняться от `100px` до `400px`.
Однако, если кликнуть на поезд, то мы увидим, что:
- Он едет сначала назад, то есть `left` становится меньше `100px`.
- Затем вперёд, причём выезжает за назначенные `400px`.
- А затем опять назад -- до `400px`.
[codetabs src="train-over"]
Почему так происходит -- отлично видно, если взглянуть на кривую Безье с указанными опорными точками:
![](bezier-train-over.png)
Мы вынесли координату `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)`:
[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`.
Это широко используется, чтобы после анимации сделать какое-то действие или объединить несколько анимаций в одну.
Например, лодочка в примере ниже при клике начинает плавать туда-обратно, с каждым разом уплывая всё дальше вправо:
[iframe src="boat" height=300 edit link]
Её анимация осуществляется функцией `go`, которая перезапускается по окончании, с переворотом через CSS:
```js
boat.onclick = function() {
//...
let 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();
boat.addEventListener('transitionend', function() {
times++;
go();
});
};
```
Объект события `transitionend` содержит специфические свойства:
`propertyName`
: Свойство, анимация которого завершилась.
`elapsedTime`
: Время (в секундах), которое заняла анимация, без учета `transition-delay`.
Свойство `propertyName` может быть полезно при одновременной анимации нескольких свойств. Каждое свойство даст своё событие, и можно решить, что с ним делать дальше.
## CSS animations
Более сложные анимации делаются объединением простых при помощи CSS-правила `@keyframes`.
В нём задаётся "имя" анимации и правила: что, откуда и куда анимировать. Затем при помощи свойства `animation: имя параметры` эта анимация подключается к элементу, задаётся время анимации и дополнительные параметры, как её применять.
Пример с пояснениями в комментарии:
```html run height=60 autorun="no-epub" no-beautify
<div class="progress"></div>
<style>
*!*
/* Современные браузеры, кроме Chrome, Opera, Safari */
@keyframes go-left-right { /* назовём анимацию: "go-left-right" */
from {
left: 0px; /* от: left: 0px */
}
to {
left: calc(100% - 50px); /* до: left: 100%-50px */
}
}
*/!*
/* Префикс для Chrome, Opera, Safari */
@-webkit-keyframes go-left-right {
from {
left: 0px;
}
to {
left: calc(100% - 50px);
}
}
.progress {
*!*
/* применить анимацию go-left-right */
/* продолжительность 3s */
/* количество раз: бесконечное (infinite) */
/* менять направление анимации каждый раз (alternate) */
animation: go-left-right 3s infinite alternate;
-webkit-animation: go-left-right 3s infinite alternate;
*/!*
position: relative;
border: 2px solid green;
width: 50px;
height: 20px;
background: lime;
}
</style>
```
Этот стандарт пока в черновике, поэтому в Chrome, Safari, Opera нужен префикс `-webkit`.
Статей про CSS animations достаточно много, посмотрите, например:
- [Статья про CSS Animation](http://css.yoksel.ru/css-animation/).
- [Пример бесконечной подпрыгивающей анимации на CSS Animation и кривых Безье](https://albiebrown.github.io/gravify/).
## Итого
CSS-анимации позволяют плавно или не очень менять одно или несколько свойств.
Альтернатива им -- плавное изменение значений свойств через JavaScript, мы рассмотрим подробности далее.
Ограничения и достоинства CSS-анимаций по сравнению с JavaScript:
```compare
- Временная функция может быть задана кривой Безье или через шаги. Более сложные анимации, состоящие из нескольких кривых, реализуются их комбинацией при помощи [CSS animations](http://www.w3.org/TR/css3-animations/), но JavaScript-функции всегда гибче.
- CSS-анимации касаются только свойств, а в JavaScript можно делать всё, что угодно, удалять элементы, создавать новые.
- Отсутствует поддержка в IE9-
+ Простые вещи делаются просто.
+ "Легче" для процессора, чем анимации JavaScript, лучше используется графический ускоритель. Это очень важно для мобильных устройств.
```
Подавляющее большинство анимаций делается через CSS.
При этом JavaScript запускает их начало -- как правило, добавлением класса, в котором задано новое свойство, и может отследить окончание через событие `transitionend`.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

View file

@ -1,12 +0,0 @@
#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;
}

View file

@ -1,12 +0,0 @@
#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;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Some files were not shown because too many files have changed in this diff Show more