This article introduces the implementation code of Canvas text to particle effect and shares it with everyone. I hope it will be helpful to everyone. The details are as follows:
Drawing text through particles feels interesting, and matching the movement of particles will make the effect even cooler. This article introduces how to draw text through particles in canvas.
Implementation principleGenerally speaking, it is very simple to make the effect of turning text into particle display. The principle of implementation is to use two canvases. One is the A canvas that the user cannot see, which is used to draw text; the other is the one that the user can see. The B canvas obtained is used to generate particles based on the text data of A. Visual representation is as shown in the figure:
Create an off-screen canvasHTML only needs to be placed on the main canvas:
<!-- HTML structure--><html><head> ...</head><body> <canvas id=stage></canvas></body></html>
Then create an off-screen canvas and draw text:
const WIDTH = window.innerWidth; const HEIGHT = window.innerHeight; const offscreenCanvas = document.createElement('canvas'); const offscreenCtx = offscreenCanvas.getContext('2d'); offscreenCanvas.width = WIDTH; offscreenCanvas.height = HEIGHT; offscreenCtx.font = '100px PingFang SC';offscreenCtx.textAlign = 'center';offscreenCtx.baseline = 'middle';offscreenCtx.fillText('Hello', WIDTH / 2, HEIGHT / 2);Nothing happens on the page at this time, but you can actually imagine it on the off-screen canvas. It should be as shown in the figure:
Core method getImageData Using the canvas's getImageData method, you can obtain an ImageData object, which is used to describe the pixel data in the specified area of the canvas. In other words, we can get the position and color of each pixel of the Hello text, and we can generate particles at the specified location. The final effect is that the particles are pieced together into text.
To obtain pixel information, you need to use the data attribute of the ImageData object, which spreads the rgba values of all pixels into an array. Each pixel has four rgba values. The number of this array is 像素点数量* 4 .
Suppose I select a 3 * 4 area, then there are a total of 12 pixels, each pixel has four rgba values, so the data array will have 12 * 4 = 48 elements.
If you print out the data, you can see the rgba of these pixels arranged from left to right and top to bottom.
Of course, the area we want to obtain must contain text, so we should obtain the entire off-screen canvas area:
const imgData = offscreenCtx.getImageData(0, 0, WIDTH, HEIGHT).data;Generate particles
After getting the ImageData, by traversing the data array, you can determine which points in the off-screen canvas are colored (in the middle of the text) and which points are colorless (not on the text), and put those colored pixels Write down the position, then generate particles on the main canvas, and you're good to go.
First create a particle class:
class Particle { constructor (options = {}) { const { x = 0, y = 0, color = '#fff', radius = 5} = options; this.radius = radius; this.x = x; this.y = y; this.color = color; } draw (ctx) { ctx.beginPath(); ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI, false); ctx.fillStyle = this.color; ctx.fill(); ctx.closePath(); }}Traversing the data, we can determine whether the pixel is in the text based on the transparency, that is, whether the fourth element in rgba is not 0.
const particles = [];const skip = 4;for (var y = 0; y < HEIGHT; y += skip) { for (var x = 0; x < WIDTH; x += skip) { var opacityIndex = (x + y * WIDTH) * 4 + 3; if (imgData[opacityIndex] > 0) { particles.push(new Particle({ x, y, radius: 1, color: '#2EA9DF' })); } }} We use particles array to store all particles. The function of skip here is the step size of the traversal. If we scan pixel by pixel, the particles that will eventually piece together the text will be very dense. Increase this value and the final particles will be generated. will be sparser.
Finally, create the main canvas and draw it:
const canvas = document.querySelector('#stage');canvas.width = WIDTH;canvas.height = HEIGHT;const ctx = canvas.getContext('2d');for (const particle of particles) { particle.draw(ctx );}The effect is as follows:
For the complete code, see 01-basic-text-to-particles
Add effectsAfter understanding the implementation principle, the rest is actually to add some animation effects to the particles. First, you can give the particles some random displacement to avoid looking too neat.
const particles = [];const skip = 4;for (var y = 0; y < HEIGHT; y += skip) { for (var x = 0; x < WIDTH; x += skip) { var opacityIndex = (x + y * WIDTH) * 4 + 3; if (imgData[opacityIndex] > 0) { // Add random displacement when creating particles particles.push(new Particle({ x: x + Math.random() * 6 - 3, y: y + Math.random() * 6 - 3, radius: 1, color: '#2EA9DF' })); } }}The effect is as follows:
If you want to achieve a larger effect, such as:
How to implement this? First, you need to randomly generate the size of the particles. This only requires randomizing the radius when creating the particles. In addition, if you want the particle radius to change dynamically, you need to distinguish the particle's rendering radius and initial radius, and use requestAnimationFrame for animation rendering:
class Particle { constructor (options = {}) { const { x = 0, y = 0, color = '#fff', radius = 5} = options; this.radius = radius; // ... this.dynamicRadius = radius; // Add dynamicRadius property} draw (ctx) { // ... ctx.arc(this.x, this.y, this.dynamicRadius, 0, 2 * Math.PI, false); // Replace with dynamicRadius // ... } update () { // TODO }}requestAnimationFrame(function loop() { requestAnimationFrame(loop); ctx.fillStyle = '#fff'; ctx.fillRect(0, 0, WIDTH, HEIGHT ); for (const particle of particles) { particle.update(); particle.draw(ctx); }}); Then the key lies in how to implement the update method of the particles. Suppose we want the particle radius to change smoothly and cyclically from 1 to 5. It is easy to think of trigonometric functions, such as:
The horizontal axis should be related to time. You can maintain a variable and add it every time you call update. You can also use the timestamp to calculate it simply. An example of the update method is as follows:
update () { this.dynamicRadius = 3 + 2 * Math.sin(new Date() / 1000 % 1000 * this.radius);}For the complete code, see 02-text-to-particles-with-size-changing
The above is the entire content of this article. I hope it will be helpful to everyone’s study. I also hope everyone will support VeVb Wulin Network.