Introduction to the
Bessel curves are applied in many places in front-end work. For example, in the recent annual drawing, bezier curves were used for the acceleration at the start of the roll and the slow stop at the winning number at the end of the roll. So I’m going to introduce you to bezier curves and simple applications. Its general parameter formula is:
The curve starts at P0 and ends at Pn, and there are n minus 2 points in between that control the curve. According to the different number of control points, different special curve formulas are obtained.
In practice, quadratic Bessel curve and cubic Bessel curve are commonly used. A Bezier curve, it’s a straight line. The following are the introductions and applications of several common Bessel curves.
Common curve
The linear equation
n=1
, the number of control points:0
, only two points, the beginning and the end, you get a straight line.
Quadratic formula
n=2
, the number of control points:1
Cubic formula
n=3
, the number of control points:2
formula
For example, the quadratic formula is converted to the following function, where P0 is the starting point, P2 is the ending point, and P1 is the control point. We get a quadratic function of t from P0 to P2, the range of t [0, 1].
function QuadraticBezier(P0, P1, P2){
return (t) = > (1 - t) * (1 - t) * P0 + 2 * t * (1 - t) * P1 + t * t * P2;
}
Copy the code
application
The application of SVG
In SVG, quadratic Bezier curves can be drawn using q or q (q refers to relative position, q refers to absolute position). For SVG, the critical code
// Compute X and Y respectively
const getX = new QuadraticBezier(100.250.400);
const getY = new QuadraticBezier(350.50.350);
const point = document.getElementById("point"); // Points that move dynamically with t
let t = 0; // The initial value is 0 and increments to 1
function setXY () {
point.style.top = getY(t) + "px";
point.style.left = getX(t) + "px";
if (t >= 1) {
cancelAnimationFrame(frame);
} else {
t = t + 0.005; frame = requestAnimationFrame(setXY); }}let frame = requestAnimationFrame(setXY);
Copy the code
With the increase of t, the top and left of point are calculated respectively, and the following animation can be obtained:
The apiquadraticCurveTo, bezierCurveTo in canvas are similar to drawing SVG.
CSS animation-timing-function
Animation-timing-function specifies the speed curve of the CSS animation. Several values are preset: Linear, ease, ease-in, ease-out, ease-in-out, cubic- Bezier () is used for flexible control of customized speed. You can modify the numbers in this example to see what happens.
Used in JS
The normal range of the value received by cubic bezier is [0, 1], that is, in the cubic Bezier curve, the default starting point P0(0, 0) and the end point P3(1, 1), the formula can be derived
const CubicBezier = P0 * (1-t)^3 + 3 * P1 * t * (1-t)^2 + 3 * P2 * t^2 * (1-t) + P3 * t^3
/ / make P0 (0, 0), P3 (1, 1), P1 (x1, y1), P2 (x2, y2), plug in
x = 3 * x1 * t * (1-t)^2 + 3 * x2 * t^2 * (1-t) + t^3;
= (3 * x1 - 3 * x2 + 1) * t^3 + (3 * x2 - 6 * x1) * t^2 + 3 * x1 * t;
y = (3 * y1 - 3 * y2 + 1) * t^3 + (3 * y2 - 6 * x1) * t^2 + 3* y1 * t; namelyfunction CubicBezier(x1, y1, x2, y2) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
CubicBezier.prototype.sampleCurveX = function(t){
const x1 = this.x1, x2 = this.x2;
return (3 * x1 - 3 * x2 + 1) * t^3 + (3 * x2 - 6 * x1) * t^2 + 3 * x1 * t;
}
CubicBezier.prototype.sampleCurveY = function(t){
const y1 = this.y1, y2 = this.y2;
return (3 * y1 - 3 * y2 + 1) * t^3 + (3 * y2 - 6 * x1) * t^2 + 3 * y1 * t;
}
Copy the code
The value of time t can be calculated by combining the functions of X and Y:
CubicBezier.prototype.solve = function(t){
return this.sampleCurveY(this.sampleCurveX(t))
}
Copy the code
Simple combination at this time, the error is large. As can be seen from the example of drawing SVG, the functions sampleCurveX and sampleCurveY calculate the coordinates x and y at time T and can draw the cubic Bessel curve. However, when applied to the change of attribute values, the tangent line at time t on the curve represents the speed of the change of attribute values at time T, which is not directly related to the coordinates x and y at time T. So you have to reprocess the function. Newton iteration method:
CubicBezier.prototype.solve = function(x){
if (x === 0 || x === 1) { // Do not calculate t 0 and t 1
return this.sampleCurveY(x);
}
let t = x
for (let i = 0; i < 8; i++) { // Do 8 iterations
const g = this.sampleCurveX(t) - x
if (Math.abs(g) < this.epsilon) { // Check error to acceptable range, e.g. This. epsilon = 1e-7;
return this.sampleCurveY(t)
}
const d = 3 * (3 * x1 - 3 * x2 + 1) * t^2 + 2 * (3 * x2 - 6 * x1) * t + 3 * x1; // The derivative with respect to x
if (Math.abs(d) < 1e-6) { // If the gradient is too low, Newton's iterative method cannot achieve higher accuracy
break
}
t = t - g / d;
}
return this.sampleCurveY(t) // Find y for the resulting approximate t
}
Copy the code
Within the acceptable range of error, there is a slight gap between the iterative effect and CSS animation. The test results are as follows:
Applications in slot machines
In the annual lottery page, three Bezier curves are also used, such as: when rolling, the rolling speed should be accelerated from 0 to the specified speed maxSpeed, need to pass in: initial speed 0, maximum speed maxSpeed, current time, the time needed to accelerate to maxSpeed and control point. The following functions can be obtained:
function initCubicBezier(startTime, totalTime, startValue, targetValue, controlArr){
let cubic = newCubicBezier(... controlArr);function getValue(time){
let progress = (time - startTime) / totalTime;
if (progress >= 1) {
progress = 1
}
const value = cubic.solve(progress);
return value * (targetValue - startValue) + startValue;
}
return getValue;
}
Copy the code
-
Start rolling
- Create a function that gets the speed of the current time
getSpeed
- According to the
top
Value offsets the image to achieve the effect of movement top = top + speed
, modifyspeed
The value of is changed incrementally to achieve the effect of acceleration- When the speed does not reach the maximum speed,
getSpeed
Get a new speed - Repeat 2-4, the main code is as follows
- Create a function that gets the speed of the current time
function start() {
let top = 0; // The current image moving position
let speed = 0; // Initial speed
const targetSpeed = 100; // Maximum speed
const totalTime = 3000; // Accelerate to maximum speed at 3000 ms
const getSpeed = initCubicBezier(date.now(), totalTime, speed, targetSpeed, [. 5.. 5.. 5.. 5]);
function run () {
if (speed < targetSpeed) {
speed = getSpeed(Date.now());
}
top = top + speed;
drawImage(top); // Redraw the image
requestAnimationFrame(run);
}
requestAnimationFrame(run);
}
Copy the code
-
End of the scroll
- Need to end according to
top
Computes the number currently displayedshowNum
- According to the winning numbers
targetNum
andshowNum
Calculate the value when you stop scrollingtargetTop
- Create the top function that gets the time
getTop
- Calculate the offset of the picture at the moment
top
- judge
top < targetTop
“, indicating that the winning number has not been rolled, continue to roll - judge
top >= targetTop
When, stop scrolling.
- Need to end according to
function end(targetNum) {
const targetTop = getTargetTop(top, targetNum); // According to the leading position, and the winning number, get the stop position
const getTop = initCubicBezier(date.now(), 3000, top, targetTop, [. 39.61..74..99.]);
function run () {
if (top >= targetTop) {
cancelAnimationFrame(frame);
} else {
top = getTop(Date.now());
drawImage(top); // Draw a new imageframe = requestAnimationFrame(run); }}let frame = requestAnimationFrame(run);
}
Copy the code
The final effect is as follows:
annotation
Because the MP4 format can not be directly referenced, so the animation in the article are converted to GIF format, the effect is much worse, the original video address please check the corresponding link. The picture is stored in Github. If you can’t open it, please check the corresponding link. If you have any suggestions about video storage, please contact me. Thank you