用canvas让美女沉浸在音符的海洋里

简介: 用canvas让美女沉浸在音符的海洋里

今天跟大家聊点轻松的话题——《用canvas让美女沉浸在音符的海洋里》,效果如下所示:


640.gif


一、前期准备


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


  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 = {
            width: 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>
目录
打赏
0
0
0
0
5
分享
相关文章
性能工具之JMeter5.0核心类HashTree源码分析
【5月更文挑战第15天】性能工具之JMeter5.0核心类HashTree源码分析
155 2
性能工具之JMeter5.0核心类HashTree源码分析
野火F1开发板STM32案例-外部中断(按键)使用
野火F1开发板STM32案例-外部中断(按键)使用
255 0
Do not access Object.prototype method 'hasOwnProperty' 问题原因及解决方法
使用Vue.js启动新项目将自动生成配置为与ESLint一起使用的样板。ESLint是可插拔和可配置的Linter工具,可帮助您识别和报告JavaScript中的不良模式,因此您可以轻松维护代码质量。如果您在不受控制的情况下开始编程,则可能会引入一些对ESLint不利的做法。
573 0
基于BERT,神马搜索在线预测性能如何提升?
今天,阿里巴巴技术专家丁亦川梳理了神马搜索 BERT 在线预测过程中,遇到的性能问题,探索过程以及当前初步的成果,分享给大家。
4792 0
【软件设计师备考 专题 】虚拟存储器基本工作原理,多级存储体系的性能价格
【软件设计师备考 专题 】虚拟存储器基本工作原理,多级存储体系的性能价格
473 0
一元建站-基于函数计算 + wordpress 构建 serverless 网站
本文旨在通过快速部署一个 wordpress 网站到阿里云函数计算平台这个示例来展示 serverless web 新的开发模式, 包括 FUN 工具一键初始化 NAS, 同步网站到 NAS, 一键部署等能力, 展现函数计算的开发敏捷特性、自动弹性伸缩能力、免运维和完善的监控设施。
一元建站-基于函数计算 + wordpress 构建  serverless 网站
Android冷启动白屏问题
1. 冷启动与热启动 通常我们在使用某个应用程序时,都是点击桌面应用图标来启动该程序。你肯定或多或少的碰到过这种情况:应用启动的一刹那,手机会先白屏或者黑屏一段时间,然后再进入应用程序的主页,但是你退出应用后再次打开APP,确又发现白屏时间极短或者压根感觉不出来。
2294 0
阿里云云服务器ECS类云产品与云服务产品简介及适用场景介绍
阿里云的云服务器ECS类产品并不是只有云服务器和轻量应用服务器两种产品,还包括弹性裸金属服务器、GPU云服务器、专有宿主机、FPGA云服务器、VMware服务等产品和服务均属于云服务器ECS类云产品,本文为大家介绍一下哪些云产品属于云服务器ECS类产品,他们各自的适用场景有哪些。
469 0
|
9月前
SDL事件处理以及线程使用(2)
SDL库中事件处理和多线程编程的基本概念和示例代码,包括如何使用SDL事件循环来处理键盘和鼠标事件,以及如何创建和管理线程、互斥锁和条件变量。
114 1
SDL事件处理以及线程使用(2)

热门文章

最新文章

登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问