This commit is contained in:
Ilya Kantor 2018-07-05 15:34:47 +03:00
parent 3340634eb5
commit fb99525ba8
5 changed files with 65 additions and 37 deletions

View file

@ -33,7 +33,7 @@ For two points we have a linear curve (that's a straight line), for three 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. So checking for the convex hulls intersection first can give a very fast "no intersection" result. Checking the intersection or convex hulls is much easier, because they are rectangles, triangles and so on (see the picture above), much simpler figures than the curve. 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. So checking for the convex hulls intersection first can give a very fast "no intersection" result. Checking the intersection or convex hulls is much easier, because they are rectangles, triangles and so on (see the picture above), much simpler figures than the curve.
The main value of Bezier curves for drawing -- by moving the points the curve is changing *in intuitively obvious way*. **The main value of Bezier curves for drawing -- by moving the points the curve is changing *in intuitively obvious way*.**
Try to move control points using a mouse in the example below: Try to move control points using a mouse in the example below:
@ -56,7 +56,7 @@ First let's see the 3-points example.
Here's the demo, and the explanation follow. Here's the demo, and the explanation follow.
Points can be moved by the mouse. Press the "play" button to run it. Control points (1,2 and 3) 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] [iframe src="demo.svg?p=0,0,0.5,1,1,0&animate=1" height=370]
@ -117,12 +117,11 @@ A curve that looks like `y=1/t`:
[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,0,0.75,0.25,1,1,1&animate=1" height=370]
Zig-zag control points also work fine:
With zig-zag control points:
[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,0.5,1,1&animate=1" height=370]
Loop form: Making a loop is possible:
[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,0.5,0,1,0.5,0&animate=1" height=370]
@ -130,16 +129,19 @@ A non-smooth Bezier curve (yeah, that's possible too):
[iframe src="demo.svg?p=0,0,1,1,0,1,1,0&animate=1" height=370] [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 in practice many points are less useful. Usually we take 2-3 points, and for complex lines glue several curves together. That's simpler to develop and calculate. ```online
If there's anything unclear in the algorithm description, then live examples above show how
the curve is built.
```
As the algorithm is recursive, we can build Bezier curves of any order, that is: using 5, 6 or more control points. But in practice many points are less useful. Usually we take 2-3 points, and for complex lines glue several curves together. That's simpler to develop and calculate.
```smart header="How to draw a curve *through* given points?" ```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 ones do belong to curve, but others don't. We use control points for a Bezier curve. As we can see, they are not on the curve, except the first and the last ones.
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. 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). 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.
In computer graphics [spline interpolation](https://en.wikipedia.org/wiki/Spline_interpolation) is often used to build smooth curves that connect many points.
``` ```
@ -147,17 +149,17 @@ In computer graphics [spline interpolation](https://en.wikipedia.org/wiki/Spline
A Bezier curve can be described using a mathematical formula. A Bezier curve can be described using a mathematical formula.
As we saw -- there's actually no need to know it. But for completeness -- here it is. As we saw -- there's actually no need to know it, most people just draw the curve by moving points with a mouse. But if you're into maths -- here it is.
Given the 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, the curve coordinates are described by the equation that depends on the parameter `t` from the segment `[0,1]`. Given the 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, the curve coordinates are described by the equation that depends on the parameter `t` from the segment `[0,1]`.
- The formula for a 2-points curve: - The formula for a 2-points curve:
<code>P = (1-t)P<sub>1</sub> + tP<sub>2</sub></code> <code>P = (1-t)P<sub>1</sub> + tP<sub>2</sub></code>
- For three points: - For 3 control 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> <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: - For 4 control 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> <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>

View file

@ -40,7 +40,7 @@ Click the button below to animate the background:
</script> </script>
``` ```
There are 5 properties to describe CSS transitions: There are 4 properties to describe CSS transitions:
- `transition-property` - `transition-property`
- `transition-duration` - `transition-duration`
@ -260,9 +260,15 @@ But how to make the Bezier curve for a specific task? There are many tools. For
Timing function `steps(number of steps[, start/end])` allows to split animation into 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. Let's see that in an example with digits.
For that we split the animation into 9 steps: Here's a list of digits, without any animations, just as a source:
[codetabs src="step-list"]
We'll make the digits appear in a discrete way by making the part of the list outside of the red "window" invisible and shifting the list to the left with each step.
There will be 9 steps, a step-move for each digit:
```css ```css
#stripe.animate { #stripe.animate {
@ -271,11 +277,11 @@ For that we split the animation into 9 steps:
} }
``` ```
In action `step(9, start)`: In action:
[codetabs src="step"] [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 first argument of `steps(9, start)` is the number of steps. The transform will be split into 9 parts (10% each). The time interval is automatically divided into 9 parts as well, so `transition: 9s` gives us 9 seconds for the whole animation 1 second per digit.
The second argument is one of two words: `start` or `end`. The second argument is one of two words: `start` or `end`.
@ -301,7 +307,7 @@ So the process would go like this:
- ... - ...
- `9s` -- `-90%` - `9s` -- `-90%`
In action `step(9, end)`: Here's `step(9, end)` in action (note the pause between the first digit change):
[codetabs src="step-end"] [codetabs src="step-end"]

View file

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="digit"><div id="stripe">0123456789</div></div>
</body>
</html>

View file

@ -0,0 +1,9 @@
#digit {
border: 1px solid red;
width: 1.2em;
}
#stripe {
display: inline-block;
font: 32px monospace;
}

View file

@ -4,20 +4,19 @@ JavaScript animations can handle things that CSS can't.
For instance, moving along a complex path, with a timing function different from Bezier curves, or an animation on a canvas. For instance, moving along a complex path, with a timing function different from Bezier curves, or an animation on a canvas.
## setInterval ## Using setInterval
From the HTML/CSS point of view, an animation is a gradual change of the style property. For instance, changing `style.left` from `0px` to `100px` moves the element. An animation can be implemented as a sequence of frames -- usually small changes to HTML/CSS properties.
And if we increase it in `setInterval`, by making 50 small changes per second, then it looks smooth. That's the same principle as in the cinema: 24 or more frames per second is enough to make it look smooth. For instance, changing `style.left` from `0px` to `100px` moves the element. And if we increase it in `setInterval`, changing by `2px` with a tiny delay, like 50 times per second, then it looks smooth. That's the same principle as in the cinema: 24 or more frames per second is enough to make it look smooth.
The pseudo-code can look like this: The pseudo-code can look like this:
```js ```js
let delay = 1000 / 50; // in 1 second 50 frames
let timer = setInterval(function() { let timer = setInterval(function() {
if (animation complete) clearInterval(timer); if (animation complete) clearInterval(timer);
else increase style.left else increase style.left by 2px
}, delay) }, 20); // change by 2px every 20ms, about 50 frames per second
``` ```
More complete example of the animation: More complete example of the animation:
@ -50,15 +49,13 @@ Click for the demo:
[codetabs height=200 src="move"] [codetabs height=200 src="move"]
## requestAnimationFrame ## Using requestAnimationFrame
Let's imagine we have several animations running simultaneously. Let's imagine we have several animations running simultaneously.
If we run them separately, each one with its own `setInterval(..., 20)`, then the browser would have to repaint much more often than every `20ms`. If we run them separately, then even though each one has `setInterval(..., 20)`, then the browser would have to repaint much more often than every `20ms`.
Each `setInterval` triggers once per `20ms`, but they are independent, so we have several independent runs within `20ms`. That's because they have different starting time, so "every 20ms" differs between different animations. The intervals are not alignned. So we'll have several independent runs within `20ms`.
These several independent redraws should be grouped together, to make it easier for the browser.
In other words, this: In other words, this:
@ -70,19 +67,19 @@ setInterval(function() {
}, 20) }, 20)
``` ```
...Is lighter than this: ...Is lighter than three independent calls:
```js ```js
setInterval(animate1, 20); setInterval(animate1, 20); // independent animations
setInterval(animate2, 20); setInterval(animate2, 20); // in different places of the script
setInterval(animate3, 20); setInterval(animate3, 20);
``` ```
There's one more thing to keep in mind. Sometimes when CPU is overloaded, or there are other reasons to redraw less often. For instance, if the browser tab is hidden, then there's totally no point in drawing. These several independent redraws should be grouped together, to make the redraw easier for the browser (and hence smoother for people).
There's a standard [Animation timing](http://www.w3.org/TR/animation-timing/) that provides the function `requestAnimationFrame`. There's one more thing to keep in mind. Sometimes when CPU is overloaded, or there are other reasons to redraw less often (like when the browser tab is hidden), so we really shouldn't run it every `20ms`.
It addresses all these issues and even more. But how do we know about that in JavaScript? There's a specification [Animation timing](http://www.w3.org/TR/animation-timing/) that provides the function `requestAnimationFrame`. It addresses all these issues and even more.
The syntax: The syntax:
```js ```js
@ -416,7 +413,7 @@ Here's the animated "bouncing" text typing:
## Summary ## Summary
JavaScript animation should be implemented via `requestAnimationFrame`. That built-in method allows to setup a callback function to run when the browser will be preparing a repaint. Usually that's very soon, but the exact time depends on the browser. For animations that CSS can't handle well, or those that need tight control, JavaScript can help. JavaScript animations should be implemented via `requestAnimationFrame`. That built-in method allows to setup a callback function to run when the browser will be preparing a repaint. Usually that's very soon, but the exact time depends on the browser.
When a page is in the background, there are no repaints at all, so the callback won't run: the animation will be suspended and won't consume resources. That's great. When a page is in the background, there are no repaints at all, so the callback won't run: the animation will be suspended and won't consume resources. That's great.