从0开始canvas系列
从0开始canvas系列终章 --- 打造炫酷粒子系统
知识点
基础知识:canvas画布/文本和动画
文本/图片粒子化核心api:getImageData
脑图
各单元代码及实现思路
粒子类
粒子就是一个圆,定义粒子类构造函数定义绘制圆需要的变量及运动的位置和速度
function Particle(x, y, r, color) {
/**
* @description: 粒子的构造函数
*/
this.endX = x; //目标位置x
this.endY = y; //目标位置y
this.startX = randomNumber(1, width); //开始位置x
this.startY = randomNumber(1, height); //开始位置y
this.r = r || 1; //粒子半径如果不指定
this.vx = (Math.random() - 0.5) * 10;
this.vy = (Math.random() - 0.5) * 10;
this.color = color || "#fff";
this.friction = Math.random() * 0.025 + 0.95;//阻力系数
}
Particle.prototype.particleMotion = function () {
/**
* @description: 粒子的绘制与运动
// */
//弹性运动模型,距离越远加速度越大
let disX = this.endX - this.startX;
let disY = this.endY - this.startY;
this.vx += disX / 1000;
this.vy += disY / 1000;
this.vx *= this.friction;
this.vy *= this.friction;
this.startX += this.vx;
this.startY += this.vy;
//画圆
ctx.save();
ctx.beginPath();
ctx.fillStyle = this.color;
ctx.arc(this.startX, this.startY, this.r, 0, C);
ctx.fill();
ctx.restore();
}
文字绘制
获得input框内值,并将文字绘制到canvas内
function drawText() {
//获得input内的值
const text = document.querySelector('.text').value;
/**
* @description: 文字绘制到canvas内
*/
ctx.clearRect(0, 0, width, height);
ctx.save();
ctx.font = 'bold 200px arial';
ctx.textAlign = 'center';
ctx.fillStyle = "#fff" //定义颜色为白色,为后续找到文字对应的像素点提供判断条件
ctx.fillText(text, width / 2, height / 1.5);
ctx.restore();
}
文字粒子化
- 通过canvas getImageData api获得每一个像素点数据数组Uint8ClampedArray,每四个数据代表一个像素
- 根据canvas 宽高将像素点数组转变为二维坐标得到文字的坐标
- 将文字对应的像素点取样(为了性能),并将抽样得到的像素点实例化成粒子
- 将所有的粒子保存到一个数组内
如果对getImageData和图形的像素化处理不清楚的,请回看从0开始canvas系列三 --- 图像像素级操作
function particleGenerator() {
/**
* @description: 粒子发生器,将canvas全部转化为像素点,canvas中的文字的像素点实例化一个粒子
* @param {*}
* @return {particles}
*/
const ctxUint8ClampedArray = ctx.getImageData(0, 0, width, height).data;
ctx.clearRect(0, 0, width, height);
particles = [] //每次执行初始化函数清空粒子
for (let i = 0; i < height; i += Math.round(width / 300)) {
for (let j = 0; j < width; j += Math.round(width / 300)) {
//Math.round(width / 300):粒子抽样,每隔300个像素点作为一个粒子
let key = (i * width + j) * 4;
if (ctxUint8ClampedArray[key] !== 0) {
const color =
`rgb(${randomNumber(1,255)},${randomNumber(1,255)},${randomNumber(1,255)})`;
//实例化粒子类并保存
particles.push(new Particle(j, i, randomNumber(1, 3), color));
}
}
}
return particles;
}
动画绘制
每一个粒子相同时间内重复执行粒子绘制方法
function render() {
ctx.clearRect(0, 0, width, height);
particles.forEach(particle => {
particle.particleMotion()
})
requestAnimationFrame(render);
}
HTML页面及交互
<div id="header">
<input type="text" class="text" placeholder="请输入文字">
<button id='btn'>更改</button>
</div>
<canvas id="canvas"></canvas>
<script>
const canvas = document.querySelector("#canvas");
const ctx = canvas.getContext('2d');
let width = canvas.width = window.innerWidth;
let height = canvas.height = window.innerHeight;
const C = Math.PI * 2;//弧度2PI对应360°
const btn = document.querySelector('#btn');
btn.addEventListener('click', start);
function start() {
drawText();
let particles = particleGenerator();
render();
}
function randomNumber(min, max) {
/**
* @description: 产生随机数
* @param {min,max}
* @return {number}
*/
return parseInt(Math.random() * (max - min)) + min
}
</script>