🙇 前言
- 转眼间又到了一年春节,而今年的春节也正好是我的本命年🐯。
- 因为疫情的原因可能又回不了家过年了,还是很想回去看看烟花感受下年味的🧧。
- 既然回不去那就自己做一个吧~跟大家一起赏烟花🎆。
- Ps: 本文没有涉及到性能优化,请不要在正式项目使用喔~
🏮 ToDoList
- 获取文字像素点
- 初始化烟花
- 烟花发射
- 烟花爆炸
🧧 Just Do It
获取文字像素点
- 首先我们先创建一个
canvas
<!-- index.html --> <canvas id="textFireWorks"></canvas> 复制代码
- 初始化
canvas
并在canvas
中填充想要输出的文字
// fireWork.js let textCanvas, textCtx; const canvasWidth = window.innerWidth; const canvasHeight = window.innerHeight; const fontSize =180 function fireWorksShowBegin() { initCanvas(); } function initCanvas() { textCanvas = document.getElementById("textFireWorks"); textCtx = textCanvas.getContext("2d"); textCanvas.style.width = canvasWidth + "px"; textCanvas.style.height = canvasHeight + "px"; textCanvas.width = canvasWidth; textCanvas.height = canvasHeight; textCtx.textAlign = "center"; textCtx.textBaseline = "top"; textCtx.font = fontSize + 'px "宋体"'; textCtx.fillStyle = "#fff"; textCtx.fillText("新春快乐", canvasWidth / 2, 0); } 复制代码
canvas
初始化完成后我们需要获取每个像素点对应的位置
// fireWork.js let textPixels = []; function initCanvas() { // ... //获取画布位置 let pix = textCtx.getImageData(0, 0, canvasWidth, canvasHeight).data; let gap = 6; for (let h = 0; h < canvasHeight; h += gap) { for (let w = 0; w < canvasWidth; w += gap) { // 当前像素块相对于画布的索引位置 let position = (canvasWidth * h + w) * 4; let r = pix[position], g = pix[position + 1], b = pix[position + 2]; if (r + g + b !== 0) { textPixels.push({ x: w, y: h, }); } } } } 复制代码
- 在
canvas
中我们可以通过getImageData
方法拿到画布的信息 - 其中的
data
就是色彩信息,每4
个元素表示一个像素颜色 - 我们可以通过以上算法在水平和垂直方向均以固定间隙去读取
imageData
像素点信息,如果是完全不透明的像素点,则作为我们需要的关键坐标保存下来至textPixels
数组中用于我们之后烟花渲染。
初始化烟花
- 对于烟花的呈现在这里我使用了一个
PixiJS
库,这是一个HTML5
创建引擎,用它可以很方便的渲染2D
动画。 - 首先引入该库后我们创建一个容器和渲染器添加到我们的
dom
中。
// fireWork.js // 创建一个Container const stage = new PIXI.Container(); //自动检测渲染器 const renderer = PIXI.autoDetectRenderer(canvasWidth, canvasHeight); document.body.appendChild(renderer.view); 复制代码
- 接下来我们需要将烟花最终绽放的位置摆满烟花,也就是以上像素点的位置。
- 使用
PIXI.Texture
我将福的图标作为烟花占位符。 - 将每一个像素点都替换成福图标。
- 在
stage
容器中添加这些子节点,并把每一个firework
记录下来方便接下来使用。
// fireWork.js const fireworks = []; const yOffset = canvasHeight * 0.4; const textures = PIXI.Texture.from("https://s3.bmp.ovh/imgs/2022/01/0d7afb4d0700761e.png"); function fireWorksShowBegin() { //... initFireworks(); } function initFireworks() { // shuffle(textPixels); for (let i = 0, l = textPixels.length; i < l; i++) { createEmojiFirework(textures, textPixels[i], i); } } function createEmojiFirework(text, pos, i) { const size = 20; const firework = new PIXI.Sprite(text); firework.position.x = pos.x; firework.position.y = pos.y + yOffset; firework.width = size; firework.height = size; firework.image = text; fireworks.push(firework); stage.addChild(firework); } 复制代码
- 因为最终我们要一直循环渲染这个页面所以采用
requestAnimationFrame
,这个方法之前我在产品经理:你能不能让词云动起来?介绍过了,这里我就不细讲了。
// fireWork.js function fireWorksShowBegin() { //... requestAnimationFrame(fireWorksAnimate); } function fireWorksAnimate() { requestAnimationFrame(fireWorksAnimate); // 将对象渲染到其 WebGL 视图。 renderer.render(stage); } 复制代码
- 我们来看看现在的效果吧
烟花发射
- 现在最终需要呈现的效果已经做好了,我们只需要在循环动画
fireWorksAnimate
方法中将每一个图标都慢慢发射到对应的像素点即可。 - 在
fireWorksAnimate
中我们首先需要做一件事情,那就是将图标的位置从一个地方位移到我们真正想要烟花爆炸
💥的位置,但是在这之前我们已经将图标放到爆炸的位置了,现在我们需要对初始化创建的图标做处理重新给定一个起点并记录爆炸的位置让他缓慢位移。 - 我们需要改动
createEmojiFirework
方法,设置起点position(x,y)
和终点explodePositio(x,y)
并设置图标移动速度speed
。
// fireWork.js function createEmojiFirework(text, pos, i) { //... // 记录最终爆炸的位置 firework.explodePosition = { x: pos.x, y: pos.y + yOffset, }; //给图标一个起点位置 firework.position.x = 20; firework.position.y = 30; //设置图标移动速度 firework.speed = 0.02 } 复制代码
- 而在
fireWorksAnimate
中我们需要计算从起点到终点的每次位移。
// fireWork.js function fireWorksAnimate() { for (let i = 0; i < fireworks.length; i++) { fireworks[i].position.x += (fireworks[i].explodePosition.x - fireworks[i].position.x) * fireworks[i].speed; fireworks[i].position.y += (fireworks[i].explodePosition.y - fireworks[i].position.y) * fireworks[i].speed; } } 复制代码
- 现在的效果是这样的
- 好家伙效果是移动了,但是太整齐了,我们在
createEmojiFirework
中给烟花一个定时器让他延迟一个一个的发射,这下效果就好很多了。
烟花爆炸
- 烟花发射成功了,接下来我们要完善烟花爆炸的效果。
- 爆炸的原理其实也是跟生成图标差不多。
-
- 在每一次的动画中循环中我们都要计算图标是否快要到达终点。
- 如果快要到达就给它一个标记并触发爆炸事件。
- 在爆炸事件中我们需要生成多个粒子,给每个粒子初始化不同的
x轴
和y轴
的位移速度,并且记录大小和位置。
// fireWork.js let particles = []; function fireWorksAnimate() { //... for (let i = 0; i < fireworks.length; i++) { //... if (!fireworks[i].exploded) { //计算是否快要到达终点 if ( Math.abs(fireworks[i].position.x - fireworks[i].explodePosition.x) + Math.abs(fireworks[i].position.y - fireworks[i].explodePosition.y) < 100 ) { fireworks[i].exploded = true; explodeFirework(fireworks[i]); } } } //... } function explodeFirework(firework) { for (let i = 0; i < 20; i++) { const size = 20; let particle = new PIXI.Sprite(firework.image); particle.speed = { x: (Math.random() - 0.5) * (Math.random() * 10), y: (Math.random() - 0.5) * (Math.random() * 10), }; particle.position.x = firework.position.x; particle.position.y = firework.position.y; particle.width = size; particle.height = size; particles.push(particle); stage.addChild(particle); } } 复制代码
- 至此每个图标烟花就会在到达终点后生成
20
个相同大小的例子,但是这个例子一直存在于画布中,我们需要计算他的位移产生一种爆炸
的效果。 - 在每次画布重新渲染中我们将每一个粒子都按照它自身的速度分别在
x
和y
位移,并设置alpha
不透明度递减。
// fireWork.js function fireWorksAnimate() { //... for (let i = 0, l = particles.length; i < l; i++) { particles[i].position.x += particles[i].speed.x; particles[i].position.y += particles[i].speed.y; particles[i].speed.y += 0.03; particles[i].alpha -= 0.01; } //... } 复制代码
- 最后呈现的效果就是这样啦~~
- 但是现在的效果太统一了,如果当我们将
发射位置
终点位置
发射速度
爆炸大小
全部随机之后会发生什么呢?
最终效果
- 好啦这样想要的效果就制作完成了,接下来就加上小卢给大家的新年贺语就结束了(贺卡有彩蛋喔~😆)。
👋 写在最后
- 首先还是很感谢大家看到这里,这次的文章就分享到这里,提前祝大家春节快乐!!
- 在新的一年希望大家越来越好,顺顺利利,疫情早日过去!!!!
- 如果您觉得这篇文章有帮助到您的的话不妨🍉🍉关注+点赞+收藏+评论+转发🍉🍉支持一下哟~~😛您的支持就是我更新的最大动力。
- 如果想跟我一起讨论和学习更多的前端知识可以加入我的前端交流学习群,大家一起畅谈天下~~~