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

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

一、前期准备

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

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

二、具体实现

美女图片仅仅是作为背景使用,所以就不过多的讲述了,直接设置一下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>

本文转载自微信公众号「前端点线面」,可以通过以下二维码关注。转载本文请联系前端点线面公众号。

责任编辑:武晓燕 来源: 前端点线面
相关推荐

2019-08-27 14:53:47

IOT大数据物联网

2018-09-09 23:42:19

2024-08-08 16:38:56

2023-05-10 07:29:01

元宇宙

2013-06-08 09:46:30

SDN软件定义网络

2017-09-19 09:28:55

2021-04-13 11:28:15

VRAR虚拟现实技术

2023-05-10 10:35:14

服务器代码

2016-11-18 08:41:23

2018-03-28 14:33:33

数据分析师工具Spark

2017-07-12 15:32:12

大数据大数据技术Python

2009-03-25 19:00:06

四核服务器

2022-08-24 14:32:35

技术引擎

2020-01-18 11:13:08

CPU程序存储

2020-11-04 09:34:48

LombokJavaJava14

2015-03-30 10:41:37

大数据

2020-03-16 11:55:28

PaxosRaft协议

2009-07-10 18:36:16

博科资讯中小企业利润

2015-03-12 16:38:52

思科交换机惠普

2011-10-20 15:06:39

iPad 3苹果视频
点赞
收藏

51CTO技术栈公众号