Today, I would like to talk about a relaxing topic — “Immersing beauty in the sea of notes with Canvas”. The effect is as follows:

First, preliminary preparation

To achieve this effect, prepare as follows:

  1. A picture of a coveted beauty;
  2. Easy to use canvas;
  3. Prepare some musical symbols;
  4. Prepare the editor, because the content is very inclined to combat, while knocking to see the effect is better.

Second, concrete implementation

Beauty picture is only used as a background, so there is no more to talk about, directly set the background property can be, the following is mainly about the realization of note animation.

2.1 Note Symbol

Note symbols can be expressed in words, there is no need to draw them one by one, I collected some of them, as shown in the following table: [Ah, I didn’t know at that time, and I was thinking about how to draw them. Finally, after a wave of magic Baidu, I realized that it was ok to write words directly, and there was no obstacle to display]

Note symbols meaning
quaver
Two eighth notes
Sixteenth note
Drop mark
L mark
𝄞 Music symbol G clef
Music Nature sign
𓏢 The harp

2.2 Encapsulates the class for drawing text

Now that note symbols can be represented by words, the complicated things become simple, directly encapsulating a class to draw words, without further elaboration.

// index.js
class Draw {
    // Pass in a Canvas DOM node
    constructor(canvasDom) {
        this._canvasDom = canvasDom;
        this.ctx = this._canvasDom.getContext('2d');
        this.width = this._canvasDom.width;
        this.height = this._canvasDom.height;
    }

    // Empty the canvas, after all, to make the notes move, not empty the canvas that is good
    clearCanvas() {
        this.ctx.clearRect(0.0.this.width, this.height);
    }

    // Draw text based on the parameters passed in
    drawText(textObj) {
        const {
            x,
            y,
            rotateRad,
            font,
            content,
            fillStyle = '# 000000',
            textAlign = 'start',
            textBaseline = 'middle'
        } = textObj;

        this.ctx.save();
        this.ctx.translate(x, y);
        this.ctx.rotate(rotateRad);
        this.ctx.fillStyle = fillStyle;
        this.ctx.textAlign = textAlign;
        this.ctx.textBaseline = textBaseline;
        this.ctx.font = font;
        this.ctx.fillText(content, 0.0);
        this.ctx.restore(); }}Copy the code

2.3 Creating text Conditions

When encapsulating the text class, we have found that it receives an object, and then draws according to the object, so we will create such an object according to the requirements. How to create? As follows:

// index.js
/ * * *@param {string} Content The content to draw *@param {object} Param {object} conditionsObj specifies the conditions required to make the text configuration */
function createTextObj(content, canvasObj, conditionsObj) {
    const {width, height} = canvasObj;

    const {
        fontMin = 20,
        fontMax = 40,
        direction = 3.// 0: left to right; 1: from right to left. 2. from top to bottom; 3: From the bottom up
        baseStep = 0.5
    } = conditionsObj;

    let textX = 0;
    let textY = 0;

    // Note: this position presets the direction condition, because we want the notes to move, so set which direction to float from
    // The prefabricated initial coordinates must not be visible, so we need to determine the initial coordinates according to the direction
    switch(direction) {
        case 0: {
            textX = (-0.1 - 0.1 * Math.random()) * width;
            textY = Math.random() * height;
            break;
        }
        case 1: {
            textX = (1.1 + 0.1 * Math.random()) * width;
            textY = Math.random() * height;
            break;
        }
        case 2: {
            textX = Math.random() * width;
            textY = (-0.1 - 0.1 * Math.random()) * height;
            break;
        }
        case 3: {
            textX = Math.random() * width;
            textY = (1.1 + 0.1 * Math.random()) * height;
            break; }}// All directions are the same, so we need to rotate
    const rotateRad = Math.PI * Math.random();
    const font = Math.random() * (fontMax - fontMin) + fontMin + 'px serif';
    // Set the step size for linear and rotational movements
    const step = Math.random() + baseStep;
    const rotateStep = Math.random() * Math.PI / 100;
    const fillStyle = 'rgba(' + Math.random() * 255 + ', ' + Math.random() * 255 + ', ' + Math.random() * 255 + ', ' + (0.5 + 0.5 * Math.random()) + ') ';

    return {
        x: textX,
        y: textY,
        rotateRad,
        font,
        fillStyle,
        content,
        step,
        rotateStep,
        direction
    };
}

Copy the code

2.4 Updating text configuration

Since the note will move, we need to update it frame by frame, so the update function cannot be avoided. The update function is as follows:

// index.js
/ * * *@param {object} CanvasObj Canvas related content *@param {Array} TextObjArr Array of text configuration objects * param {object} conditionsObj generates the conditions required for text configuration */
function updateTextObjArr(canvasObj, textObjArr, conditionsObj) {
    const {width, height} = canvasObj;

    textObjArr.forEach((textObj, index) = > {
        const {step, rotateStep, x, y, direction} = textObj;

        // Make corresponding updates according to the direction of motion
        // When a note moves out of the visible area, a new note is generated. After all, the number of notes must be guaranteed
        switch(direction) {
            case 0: {
                if (x > width + 10) {
                    textObjArr[index] = createTextObj(getRandomValFromArr(TEXT_CONTENT_ARR), canvasObj, conditionsObj);
                } else {
                    textObj.x += step;
                }
                break;
            }
            case 1: {
                if (x < -10) {
                    textObjArr[index] = createTextObj(getRandomValFromArr(TEXT_CONTENT_ARR), canvasObj, conditionsObj);
                } else {
                    textObj.x -= step;
                }
                break;
            }
            case 2: {
                if (y > height + 10) {
                    textObjArr[index] = createTextObj(getRandomValFromArr(TEXT_CONTENT_ARR), canvasObj, conditionsObj);
                } else {
                    textObj.y += step;
                }
                break;
            }
            case 3: {
                if (y < -10) {
                    textObjArr[index] = createTextObj(getRandomValFromArr(TEXT_CONTENT_ARR), canvasObj, conditionsObj);
                } else {
                    textObj.y -= step;
                }
                break;
            }
        }

        textObj.rotateRad += rotateStep;
    });

    return textObjArr;
}
Copy the code

2.5 move

Here are the key moments when we can use these functions to get the whole thing moving

<! DOCTYPEhtml>
<html>

<head>
    <title>Music character</title>
    <style>
        canvas {
            width: 500px;
            height: 300px;
            background: url('https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fwww.1230530.com%2Fpublic%2Fuploads%2Fimages%2F20211017%2F2538_202 11017231117 c46c5.jpg&refer=http%3a%2f%2fwww.1230530.com & app = 2002 & size = f9999, 10000 & q = a80 & n = 0 & g = 0 n & FMT = auto? The SEC = 1649658529 &t=2b38137ce9cb301fc27a869e40b58629');
            background-size: 100% 100%;
        }
    </style>
</head>

<body>
    <canvas width="500" height="300" id="canvasId"></canvas>
    <script src="./index.js"></script>
    <script>
        // Get a random value from the array
        function getRandomValFromArr(arr) {
            return arr[Math.floor(Math.random() * arr.length)];
        }

        // Create a series of text objects
        function createTextObjArr(count, canvasObj, conditionsObj) {
            const textObjArr = [];
            for (let i = 0; i < count; i++) {
                textObjArr.push(createTextObj(getRandomValFromArr(TEXT_CONTENT_ARR), canvasObj, conditionsObj));
            }

            return textObjArr;
        }

        // The initial note
        const TEXT_CONTENT_ARR = ['♪.♫ ' '.'♬'.'♭.'♯.'𝄞'.'♮'.'𓏢'];

        / / canvas node
        const canvasDom = document.getElementById('canvasId');

        // Get an instance of drawing text
        const drawInstance = new Draw(canvasDom);

        const canvasObj = {
            width: drawInstance.width,
            height: drawInstance.height
        };

        // Generate 30 random notes
        const count = 30;
        const conditionsObj = {
            direction: 2
        };

        const textObjArr = createTextObjArr(count, canvasObj, conditionsObj)

        // The animation moves
        function animate() {
            drawInstance.clearCanvas();
            textObjArr.forEach(textObj= > drawInstance.drawText(textObj));
            window.requestAnimationFrame(animate);
            updateTextObjArr(canvasObj, textObjArr, conditionsObj);
        }

        animate();
    </script>
</body>

</html>
Copy the code