animation
|
@ -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`:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
183
5-animation/1-bezier-curve/article.md
Normal 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:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Three points curve:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Four points curve:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
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:**
|
||||||
|
|
||||||
|
 
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
  
|
||||||
|
|
||||||
|
## 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 = (1−t)<sup>2</sup>P<sub>1</sub> + 2(1−t)tP<sub>2</sub> + t<sup>2</sup>P<sub>3</sub></code>
|
||||||
|
- For four points:
|
||||||
|
|
||||||
|
<code>P = (1−t)<sup>3</sup>P<sub>1</sub> + 3(1−t)<sup>2</sup>tP<sub>2</sub> +3(1−t)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 = (1−t)<sup>2</sup>x<sub>1</sub> + 2(1−t)tx<sub>2</sub> + t<sup>2</sup>x<sub>3</sub></code>
|
||||||
|
- <code>y = (1−t)<sup>2</sup>y<sub>1</sub> + 2(1−t)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` |
|
||||||
|
| ------------------------ | ---------------------- |
|
||||||
|
|  |  |
|
||||||
|
|
||||||
|
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.
|
BIN
5-animation/1-bezier-curve/bezier-car.png
Normal file
After Width: | Height: | Size: 6.6 KiB |
BIN
5-animation/1-bezier-curve/bezier-car@2x.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
5-animation/1-bezier-curve/bezier-letter.png
Normal file
After Width: | Height: | Size: 7.3 KiB |
BIN
5-animation/1-bezier-curve/bezier-letter@2x.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
5-animation/1-bezier-curve/bezier-vase.png
Normal file
After Width: | Height: | Size: 7.2 KiB |
BIN
5-animation/1-bezier-curve/bezier-vase@2x.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
5-animation/1-bezier-curve/bezier2.png
Normal file
After Width: | Height: | Size: 3 KiB |
BIN
5-animation/1-bezier-curve/bezier2@2x.png
Normal file
After Width: | Height: | Size: 7.1 KiB |
BIN
5-animation/1-bezier-curve/bezier3-draw1.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
5-animation/1-bezier-curve/bezier3-draw1@2x.png
Normal file
After Width: | Height: | Size: 51 KiB |
BIN
5-animation/1-bezier-curve/bezier3-draw2.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
5-animation/1-bezier-curve/bezier3-draw2@2x.png
Normal file
After Width: | Height: | Size: 42 KiB |
BIN
5-animation/1-bezier-curve/bezier3-e.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
5-animation/1-bezier-curve/bezier3-e@2x.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
5-animation/1-bezier-curve/bezier3.png
Normal file
After Width: | Height: | Size: 3.8 KiB |
BIN
5-animation/1-bezier-curve/bezier3@2x.png
Normal file
After Width: | Height: | Size: 8.6 KiB |
BIN
5-animation/1-bezier-curve/bezier4-e.png
Normal file
After Width: | Height: | Size: 6.7 KiB |
BIN
5-animation/1-bezier-curve/bezier4-e@2x.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
5-animation/1-bezier-curve/bezier4.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
5-animation/1-bezier-curve/bezier4@2x.png
Normal file
After Width: | Height: | Size: 9.8 KiB |
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 7.5 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
@ -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) задаётся опорными точками.
|
|
||||||
|
|
||||||
Их может быть две, три, четыре или больше. Например:
|
|
||||||
|
|
||||||
По двум точкам:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
По трём точкам:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
По четырём точкам:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Если вы посмотрите внимательно на эти кривые, то "на глазок" заметите:
|
|
||||||
|
|
||||||
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), образованной опорными точками:**
|
|
||||||
|
|
||||||
 
|
|
||||||
|
|
||||||
Благодаря последнему свойству в компьютерной графике можно оптимизировать проверку пересечений двух кривых. Если их выпуклые оболочки не пересекаются, то и кривые тоже не пересекутся.
|
|
||||||
|
|
||||||
Основная ценность кривых Безье для рисования -- в том, что, двигая точки, кривую можно менять, причём кривая при этом *меняется интуитивно понятным образом*.
|
|
||||||
|
|
||||||
Попробуйте двигать точки мышью в примере ниже:
|
|
||||||
|
|
||||||
[iframe src="demo.svg?nocpath=1&p=0,0,0.5,0,0.5,1,1,1" height=370]
|
|
||||||
|
|
||||||
**Как можно заметить, кривая натянута по касательным 1 -> 2 и 3 -> 4.**
|
|
||||||
|
|
||||||
После небольшой практики становится понятно, как расположить точки, чтобы получить нужную форму. А, соединяя несколько кривых, можно получить практически что угодно.
|
|
||||||
|
|
||||||
Вот некоторые примеры:
|
|
||||||
|
|
||||||
  
|
|
||||||
|
|
||||||
## Математика
|
|
||||||
|
|
||||||
У кривых Безье есть математическая формула.
|
|
||||||
|
|
||||||
Как мы увидим далее, для пользования кривыми Безье знать её нет особенной необходимости, но для полноты картины -- вот она.
|
|
||||||
|
|
||||||
**Координаты кривой описываются в зависимости от параметра `t⋲[0,1]`**
|
|
||||||
|
|
||||||
- Для двух точек:
|
|
||||||
|
|
||||||
<code>P = (1-t)P<sub>1</sub> + tP<sub>2</sub></code>
|
|
||||||
- Для трёх точек:
|
|
||||||
|
|
||||||
<code>P = (1−t)<sup>2</sup>P<sub>1</sub> + 2(1−t)tP<sub>2</sub> + t<sup>2</sup>P<sub>3</sub></code>
|
|
||||||
- Для четырёх точек:
|
|
||||||
|
|
||||||
<code>P = (1−t)<sup>3</sup>P<sub>1</sub> + 3(1−t)<sup>2</sup>tP<sub>2</sub> +3(1−t)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 = (1−t)<sup>2</sup>x<sub>1</sub> + 2(1−t)tx<sub>2</sub> + t<sup>2</sup>x<sub>3</sub></code>
|
|
||||||
- <code>y = (1−t)<sup>2</sup>y<sub>1</sub> + 2(1−t)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` |
|
|
||||||
| ------------------------ | ---------------------- |
|
|
||||||
|  |  |
|
|
||||||
|
|
||||||
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-анимации, для задания траектории или скорости передвижения.
|
|
Before Width: | Height: | Size: 6.8 KiB |
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 5.9 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 7 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 8.1 KiB |
Before Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 6.2 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 6.4 KiB |
After Width: | Height: | Size: 16 KiB |
422
5-animation/2-css-animations/article.md
Normal 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:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
...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:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
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> |
|
||||||
|
|  |  |  |  |
|
||||||
|
|
||||||
|
`*` -- 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:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
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.
|
BIN
5-animation/2-css-animations/bezier-linear.png
Normal file
After Width: | Height: | Size: 3 KiB |
BIN
5-animation/2-css-animations/bezier-linear@2x.png
Normal file
After Width: | Height: | Size: 7 KiB |
BIN
5-animation/2-css-animations/bezier-train-over.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
5-animation/2-css-animations/bezier-train-over@2x.png
Normal file
After Width: | Height: | Size: 26 KiB |
|
@ -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>
|
|
@ -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;
|
||||||
|
}
|
|
@ -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>
|
17
5-animation/2-css-animations/digits.view/style.css
Normal 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;
|
||||||
|
}
|
BIN
5-animation/2-css-animations/ease-in-out.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
5-animation/2-css-animations/ease-in-out@2x.png
Normal file
After Width: | Height: | Size: 9.8 KiB |
BIN
5-animation/2-css-animations/ease-in.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
5-animation/2-css-animations/ease-in@2x.png
Normal file
After Width: | Height: | Size: 9.9 KiB |
BIN
5-animation/2-css-animations/ease-out.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
5-animation/2-css-animations/ease-out@2x.png
Normal file
After Width: | Height: | Size: 9.6 KiB |
BIN
5-animation/2-css-animations/ease.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
5-animation/2-css-animations/ease@2x.png
Normal file
After Width: | Height: | Size: 9.8 KiB |
|
@ -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() {
|
17
5-animation/2-css-animations/step-end.view/style.css
Normal 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);
|
||||||
|
}
|
|
@ -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() {
|
17
5-animation/2-css-animations/step.view/style.css
Normal 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);
|
||||||
|
}
|
BIN
5-animation/2-css-animations/train-curve.png
Normal file
After Width: | Height: | Size: 4.1 KiB |
BIN
5-animation/2-css-animations/train-curve@2x.png
Normal file
After Width: | Height: | Size: 9.6 KiB |
Before Width: | Height: | Size: 6 KiB |
Before Width: | Height: | Size: 15 KiB |
|
@ -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)`.
|
|
||||||
|
|
||||||
График этой "кривой" таков:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
...Как видно, это просто прямая. По мере того, как проходит время `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)`.
|
|
||||||
|
|
||||||
График этой кривой:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Как видно, процесс вначале развивается быстро -- кривая резко идёт вверх, а затем всё медленнее, медленнее.
|
|
||||||
|
|
||||||
Вы можете увидеть эту временную функцию в действии, кликнув на поезд:
|
|
||||||
|
|
||||||
[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`.
|
|
||||||
|
|
||||||
**Кривая Безье может заставить анимацию "выпрыгивать" за пределы диапазона.**
|
|
||||||
|
|
||||||
Допустимо указывать для кривой Безье как отрицательные `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"]
|
|
||||||
|
|
||||||
Почему так происходит -- отлично видно, если взглянуть на кривую Безье с указанными опорными точками:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Мы вынесли координату `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`.
|
|
Before Width: | Height: | Size: 3 KiB |
Before Width: | Height: | Size: 7.2 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 25 KiB |
|
@ -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;
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
Before Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 4.6 KiB |