Welcome to pay attention to the public number before the end of the line, open the front salvation road.

The background,

Recently, I am working on the special effects requirement of a balloon pendant. On this occasion, I would like to share with you how to construct a lifelike balloon by using canvas and corresponding mathematical knowledge.

Second, the implementation

Before the implementation of this seemingly round balloon, first understand its implementation ideas, mainly divided into the following parts:

  1. Implement the sphere part;
  2. Realize the balloon opening part;
  3. Realize the line part of the balloon;
  4. Color fill;
  5. Realize animation;

2.1 Sphere part implementation

For the sphere part of such a balloon, do you have any good ideas for implementation? I’m sure there are a lot of different ways to do this, but WHEN I saw one of the big guys, I realized how nice it is to use four cubic Bezier curves to achieve this effect. To understand the rest of the code, let’s look at how cubic Bezier curves work. (Note: quote from a big guy on CSDN, very well written, below quote here)

In the figure above, P0 is the starting point, P3 is the ending point, and P1 and P2 are the control points. The final curve formula is as follows:

B (t) = (1 – t) ^ 3 * P0 (1 – t) ^ 2 + 3 t * P1 + 3 t ^ 2 * (1 – t) P2 + t ^ 3 p3, t ∈ [0, 1]

Three renderings and formulas of Bessel curves have been listed above, but how does this hook into our balloon? Here are a few pictures to understand:

As shown above, this is the idea of realizing the whole balloon sphere, as explained below:

  1. In Figure A, the starting point is P1, the ending point is P2, and the control points are C1 and C2. When the two control points overlap, the effect drawn is not very like A part of the balloon. At this point, the appearance of the balloon should be changed by changing the control points.
  2. Change the control points C1 and C2, and the y value in C1 remains unchanged while the X value decreases. In C2, the x value remains unchanged, but the y value is increased (pay attention to the coordinate direction in canvas). After the change, the effect of picture B is obtained, which is similar to the appearance of a balloon.
  3. Then follow this method to achieve the appearance of the entire balloon sphere part.
function draw() {
    const canvas = document.getElementById('canvas');
    const ctx = canvas.getContext('2d');

    ctx.translate(250, 250);
    drawCoordiante(ctx);
    ctx.save();
    ctx.beginPath();
    ctx.moveTo(0, -80);
    ctx.bezierCurveTo(45, -80, 80, -45, 80, 0);
    ctx.bezierCurveTo(80, 85, 45, 120, 0, 120);
    ctx.bezierCurveTo(-45, 120, -80, 85, -80, 0);
    ctx.bezierCurveTo(-80, -45, -45, -80, 0, -80);
    ctx.stroke();
    ctx.restore();
}

function drawCoordiante(ctx) {
    ctx.beginPath();
    ctx.moveTo(-120, 0);
    ctx.lineTo(120, 0);
    ctx.moveTo(0, -120);
    ctx.lineTo(0, 120);
    ctx.closePath();
    ctx.stroke();
}
Copy the code

2.2 Implementation of the opening part

The opening section can be simplified to a triangle as follows:

function draw() { const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); ... ctx.save(); ctx.beginPath(); ctx.moveTo(0, 120); ctx.lineTo(-5, 130); ctx.lineTo(5, 130); ctx.closePath(); ctx.stroke(); ctx.restore(); }Copy the code

2.3 Line part realization

Line implementation is relatively simple, with a straight line implementation

function draw() { const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); ... ctx.save(); ctx.beginPath(); ctx.moveTo(0, 120); ctx.lineTo(0, 300); ctx.stroke(); ctx.restore(); }Copy the code

2.4 Filling

The balloon part is filled with a circular gradient effect, which is more beautiful than the solid color.

function draw() { const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); ctx.fillStyle = getBalloonGradient(ctx, 0, 0, 80, 210); ... } function getBalloonGradient(ctx, x, y, r, hue) { const grd = ctx.createRadialGradient(x, y, 0, x, y, r); grd.addColorStop(0, 'hsla(' + hue + ', 100%, 65%, .95)'); Hsla GRD. AddColorStop (0.4, '(' + +' hue, 100%, 45%, the 85) '); grd.addColorStop(1, 'hsla(' + hue + ', 100%, 25%, .80)'); return grd; }Copy the code

2.5 Animation effect and overall code

The above process has drawn a static part of the balloon, and all you need to do is loop through the requestAnimationFrame function. The overall code is directly thrown below to facilitate students to observe the effect of debugging, as shown below:

let posX = 225; let posY = 300; let points = getPoints(); draw(); function draw() { const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); ctx.clearRect(0, 0, canvas.width, canvas.height); if (posY < -200) { posY = 300; PosX += 300 * (math.random () -0.5); points = getPoints(); } else { posY -= 2; } ctx.save(); ctx.translate(posX, posY); drawBalloon(ctx, points); ctx.restore(); window.requestAnimationFrame(draw); } function drawBalloon(ctx, points) { ctx.scale(points.scale, points.scale); ctx.save(); ctx.fillStyle = getBalloonGradient(ctx, 0, 0, points.R, points.hue); // Draw the sphere part ctx.moveto (points.p1.x, points.p1.y); ctx.bezierCurveTo(points.pC1to2A.x, points.pC1to2A.y, points.pC1to2B.x, points.pC1to2B.y, points.p2.x, points.p2.y); ctx.bezierCurveTo(points.pC2to3A.x, points.pC2to3A.y, points.pC2to3B.x, points.pC2to3B.y, points.p3.x, points.p3.y); ctx.bezierCurveTo(points.pC3to4A.x, points.pC3to4A.y, points.pC3to4B.x, points.pC3to4B.y, points.p4.x, points.p4.y); ctx.bezierCurveTo(points.pC4to1A.x, points.pC4to1A.y, points.pC4to1B.x, points.pC4to1B.y, points.p1.x, points.p1.y); // Draw the button part ctx.moveto (points.p3.x, points.p3.y); ctx.lineTo(points.knowA.x, points.knowA.y); ctx.lineTo(points.knowB.x, points.knowB.y); ctx.fill(); ctx.restore(); // Draw the line part ctx.save(); ctx.strokeStyle = '#000000'; ctx.lineWidth = 1; ctx.beginPath(); ctx.moveTo(points.p3.x, points.p3.y); ctx.lineTo(points.lineEnd.x, points.lineEnd.y); ctx.stroke(); ctx.restore(); } function getPoints() { const offset = 35; Return {scale: 0.3 + math.random () / 2, hue: math.random () * 255, R: 80, p1: {x: 0, y: -80}, pC1to2A: {x: 80 - offset, y: -80 }, pC1to2B: { x: 80, y: -80 + offset }, p2: { x: 80, y: 0 }, pC2to3A: { x: 80, y: 120 - offset }, pC2to3B: { x: 80 - offset, y: 120 }, p3: { x: 0, y: 120 }, pC3to4A: { x: -80 + offset, y: 120 }, pC3to4B: { x: -80, y: 120 - offset }, p4: { x: -80, y: 0 }, pC4to1A: { x: -80, y: -80 + offset }, pC4to1B: { x: -80 + offset, y: -80 }, knowA: { x: -5, y: 130 }, knowB: { x: 5, y: 130 }, lineEnd: { x: 0, y: 250 } }; } function getBalloonGradient(ctx, x, y, r, hue) { const grd = ctx.createRadialGradient(x, y, 0, x, y, r); grd.addColorStop(0, 'hsla(' + hue + ', 100%, 65%, .95)'); Hsla GRD. AddColorStop (0.4, '(' + +' hue, 100%, 45%, the 85) '); grd.addColorStop(1, 'hsla(' + hue + ', 100%, 25%, .80)'); return grd; }Copy the code

Iii. Related articles

Let’s say all that’s left is the Canvas tag

Canvas from entry to pig head

1. If you think this article is good, share and like it so that more people can see it

2. Welcome to pay attention to the front end line and surface of the public account, and open the front salvation road.