• 用canvas给背景添加音符 广东靓仔


    欢迎关注前端早茶,与广东靓仔携手共同进阶

    前端早茶专注前端,一起结伴同行,紧跟业界发展步伐~

    一、前期准备

    为了能够实现这个效果,要进行如下准备:

    1. 一张梦寐以求的美女图片;
    2. 能够简单使用canvas;
    3. 准备一些音乐符号;
    4. 准备编辑器,由于该内容很偏向于实战,边敲边看效果更佳。

    二、具体实现

    美女图片仅仅是作为背景使用,所以就不过多的讲述了,直接设置一下background属性即可,下面主要讲述一下音符动画的实现。

    2.1 音符符号

    音符符号都可以用文字表示出来,没必要一个个自己去画,自己收集了一些,如下表格所示:【哎、当时自己不知道,还想着怎么画呢,最后经过一波神奇的百度才知道原来直接写文字就行,展示毫无障碍】

    音符符号含义
    八分音符
    二个八分音符
    十六分音符
    降记号
    升记号
    音乐符号g谱号
    音乐自然标志
    竖琴
    ……  

    2.2    封装绘制文字的类

    既然音符符号可以用文字表示,那复杂的事情就变得简单了,直接封装一个绘制文字的类,话不多说开整。

    // index.js
    class Draw {
        // 传入一个canvas的DOM节点
        constructor(canvasDom) {
            this._canvasDom = canvasDom;
            this.ctx = this._canvasDom.getContext('2d');
            this.width = this._canvasDom.width;
            this.height = this._canvasDom.height;
        }
    
        // 清空画布,毕竟要让音符动起来,不清空画布那还了得
        clearCanvas() {
            this.ctx.clearRect(0, 0, this.width, this.height);
        }
    
        // 根据传入的参数绘制文字
        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();
        }
    }
     

    2.3 创建文字条件

    在封装文字类的时候已经发现其接收一个对象,然后根据对象来进行绘制,那我们接下来就是要根据需求创建一个这样的对象,怎么创建呢?如下所示:

    // index.js
    /**
    * @param {string} content 绘制的内容
    * @param {object} canvasObj canvas相关的内容
    * param {object} conditionsObj 生成文字配置所需要的条件
    */
    function createTextObj(content, canvasObj, conditionsObj) {
        const {width, height} = canvasObj;
    
        const {
            fontMin = 20,
            fontMax = 40,
            direction = 3, // 0:从左到右;1:从右到左;2:从上到下;3:从下到上
            baseStep = 0.5
        } = conditionsObj;
    
        let textX = 0;
        let textY = 0;
    
        // 注意:这个位置预制了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;
            }
        }
    
        // 都是一个方位也不好看呀,所以要旋转一下
        const rotateRad = Math.PI * Math.random();
        const font = Math.random() * (fontMax - fontMin) + fontMin + 'px serif';
        // 设置一下直线运动和旋转运动的步长
        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
        };
    }


    2.4 更新文字配置

    既然音符会动起来,则咱们就要逐帧进行更新,那更新函数就不能避免了,更新函数如下所示:

    // index.js
    /**
    * @param {object} canvasObj canvas相关的内容
    * @param {Array} textObjArr 文字配置对象的数组
    * param {object} conditionsObj 生成文字配置所需要的条件
    */
    function updateTextObjArr(canvasObj, textObjArr, conditionsObj) {
        const {width, height} = canvasObj;
    
        textObjArr.forEach((textObj, index) => {
            const {step, rotateStep, x, y, direction} = textObj;
    
            // 根据运动方向做对应的更新
            // 当音符符号运动出可视区域后直接在生成一个新的音符,毕竟要保证整个音符的数量
            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;
    }
     

    2.5 动起来

    万事俱备,只欠东风,下面就是我们调动这些函数让整个内容动起来的关键时刻

    <!DOCTYPE html>
    <html>
    
    <head>
        <title>音乐字符</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_20211017231117c46c5.jpg&refer=http%3A%2F%2Fwww.1230530.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?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>
            // 从数组中随机获取一个值
            function getRandomValFromArr(arr) {
                return arr[Math.floor(Math.random() * arr.length)];
            }
    
            // 创建一系列的文字对象
            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;
            }
    
            // 初始音符
            const TEXT_CONTENT_ARR = ['', '', '', '', '', '', '', ''];
    
            // canvas节点
            const canvasDom = document.getElementById('canvasId');
    
            // 获取绘制文字的实例
            const drawInstance = new Draw(canvasDom);
    
            const canvasObj = {
                 drawInstance.width,
                height: drawInstance.height
            };
    
            // 生成30个随机音符符号
            const count = 30;
            const conditionsObj = {
                direction: 2
            };
    
            const textObjArr = createTextObjArr(count, canvasObj, conditionsObj)
    
            // 动画动起来
            function animate() {
                drawInstance.clearCanvas();
                textObjArr.forEach(textObj => drawInstance.drawText(textObj));
                window.requestAnimationFrame(animate);
                updateTextObjArr(canvasObj, textObjArr, conditionsObj);
            }
    
            animate();
        </script>
    </body>
    
    </html>

    欢迎关注前端早茶,与广东靓仔携手共同进阶

    前端早茶专注前端,一起结伴同行,紧跟业界发展步伐~

  • 相关阅读:
    第九篇 python基础之函数,递归,内置函数
    第六篇:python基础之文件处理
    第五篇:python基础之字符编码
    第四篇:python基础之条件和循环
    第三篇:python基础之数据类型与变量
    第二篇:python基础之核心风格
    第一篇:初识Python
    作业
    作业3
    作业2
  • 原文地址:https://www.cnblogs.com/cczlovexw/p/16015918.html
Copyright © 2020-2023  润新知