🔥「新春烟火」 释放五彩文字烟花🎇,祝大家新年快乐🎉
PK创意闹新春,我正在参加「春节创意投稿大赛」,详情请看:春节创意投稿大赛
生活不止眼前的苟且,还有诗和远方
掘友们,大家好我是前端奶爸,入行5年的前端小学生🥜~ 工作八小时摸鱼四小时,喜欢跑步但不是为了让自己更瘦,而是为了让自己活得更久~ 活到九十九,卷到九十九~
前言
老话说的好,过了腊八就是年🥢
马上就要过年了,小伙伴们都躁动不已🔥
平台为了烘托年味,发布了新年活动,我也参加一波~🎉🎉🎉
论过年最有意思的事,我觉得第一想到的就是放鞭炮,小的时候家里穷,鞭炮也只能买几鞭,还要分给不同的时间进行正常仪式燃放,留给孩子们玩的会很少。
所以家里买鞭炮以后,会从大挂上一个个拆下来,然后拿一根粗香。年三十一大早换上新衣服,和几个小伙伴一起出去玩,左边裤兜里装满糖果(PS:只有过年这几天可以实现糖果自由)
,右边的裤兜自然就是鞭炮了,我们会去村里枯井,水塘,还有各种地方去做一些有意思的爆炸试验,还会拿着鞭炮去吓唬小女孩们(PS:尽量不要这么做)
。
现在过年的炮声是越来越少了,前几年的北京五环外还可以燃放,但是从今年开始也全面禁止了,现在的村里有些地方不烧煤,政府提供统一供暖。村里大部分也都是天气管道,所以也不让燃放烟花,过年的年味感觉缺失了不少。
还记得去年朵朵小朋友第一次见到烟花兴奋惊讶的样子,盯着窗外目不转睛,今年政府不让燃放烟花,那就用 canvas
简单的做一个烟花吧~ 希望她会喜欢~💗
具体实现
画布创建
html中首先创建一个 canvas
元素
<body>
<canvas id="canvas"></canvas>
<script src="http://cdn.static.runoob.com/libs/jquery/1.10.2/jquery.min.js">
<script src="./index.js"></script>
</body>
- 为了节约时间,直接引入了
jquery
开发获取dom
以及事件绑定操作。
初始化画布
- 获取
canvas
对象并且赋值宽高为满屏幕模式。 - 通过监听
windows
的resize
动态修改canvas
的宽高,以及重点位置的具体坐标。 - 初始化画布,设置画布初始属性。
const canvas = $("#canvas")[0];
canvas.width = $(window).width();
canvas.height = $(window).height();
const ctx = canvas.getContext("2d");
// 监听窗口改变
$(window).on("resize", function () {
canvas.width = $(window).width();
canvas.height = $(window).height();
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, canvas.width, canvas.height);
center = { x: canvas.width / 2, y: canvas.height / 2 };
});
// 初始化画布
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, canvas.width, canvas.height);
定义绘制素材
- 定义绘制文字的内容字符串。
- 初始化位置数组,把文字在画布进行布局排列。
- 绘制字母的矩阵位置数组创建。
// 初始化基础对象
const listText = []; // 画布填充粒子数组
const fireNumber = 10; // 初始化粒子基数
const center = { x: canvas.width / 2, y: canvas.height / 2 }; // 当前画布中心位置
const range = 100; // 粒子动画范围
let textIndex = 0; // 粒子动画索引
// 定义绘制文字内容
const textString = "happynewyear2022";
// 初始位置数组
const textMatrix = [
4.5, 0, 5.5, 0, 6.5, 0, 7.5, 0, 8.5, 0,
3, 1, 4, 1, 5, 1, 7, 1, 8, 1, 9, 1, 10, 1,
5, 2, 6, 2, 7, 2, 8, 2,
];
// 字母矩阵位置
const chars = {
h: [
0, 0, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 1, 3, 2, 3, 3, 3, 4, 3, 5,
0, 5, 1, 5, 2, 5, 3, 5, 4, 5, 5, 5, 6, 5, 7,
],
a: [
2, 0, 2, 1, 2, 2, 1, 2, 1, 3, 1, 4, 1, 5, 0, 5, 0, 6, 0, 7, 2, 5, 3, 0, 3,
1, 3, 2, 4, 2, 4, 3, 4, 4, 4, 1, 5, 5, 5, 6, 5, 7, 3, 5,
],
p: [
0, 0, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 1, 0, 2, 0, 3, 0, 4, 1, 5,
2, 4, 3, 3, 4, 2, 4, 1, 4,
],
y: [
0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 3, 5, 3, 6, 3, 7, 4, 3, 4,
2, 5, 2, 5, 1, 6, 1, 6, 0,
],
n: [
0, 0, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 1, 1, 1, 2, 2, 2, 2, 3, 2,
4, 3, 4, 3, 5, 4, 5, 4, 6, 5, 0, 5, 1, 5, 2, 5, 3, 5, 4, 5, 5, 5, 6, 5, 7,
],
e: [
0, 0, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 1, 0, 2, 0, 3, 0, 4, 0, 5,
0, 1, 3, 2, 3, 3, 3, 4, 3, 1, 7, 2, 7, 3, 7, 4, 7, 5, 7,
],
w: [
0, 0, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 6, 1, 7, 2, 7, 3, 7, 3.5, 6,
3.5, 5, 3.5, 4, 3.5, 3, 3.5, 2, 3.5, 1, 4, 7, 5, 7, 6, 7, 6, 6, 7, 6, 7,
5, 7, 4, 7, 3, 7, 2, 7, 1, 7, 0,
],
r: [
0, 0, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 1, 0, 2, 0, 3, 0, 4, 1, 5,
2, 4, 3, 3, 4, 2, 4, 1, 4, 1, 5, 2, 5, 3, 6, 4, 6, 5, 7,
],
0: [
0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 1, 0, 2, 0, 3, 0, 4, 0, 1, 7, 2, 7, 3,
7, 4, 7, 5, 1, 5, 2, 5, 3, 5, 4, 5, 5, 5, 6,
],
2: [
0, 0, 0, 1, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 5, 1, 5, 2, 5, 3, 4, 3, 3, 3, 2,
3, 2, 4, 1, 4, 1, 5, 0, 5, 0, 6, 0, 7, 1, 7, 2, 7, 3, 7, 4, 7, 5, 7, 5, 6,
],
};
具体的字母坐标如何绘制,看下图的详细说明,以数字 2
和字母 Y
为例:
数组按两位为一个单位,根据横纵坐标的位置,大概绘制出如下字母和数字符号。
具体绘制函数
具体的实现逻辑可拆分为5️⃣个函数:
- 初始化粒子文字函数
- 更新运动函数
- 绘制内容函数
- 循环动画执行函数
- 随机颜色生成函数
初始化粒子文字函数
- 判断 循环索引
i
长度在textString
长度内。 - 生成随机
velocity
参数。 - 根据
textMatrix
内对应内容的位置初始化多个粒子对象。 listText
填充生成的粒子对象。- 通过索引递增判断是否填充完毕,未填充完毕继续执行填充。
function initText() {
const i = textIndex;
if (i >= 0 && i < textString.length) {
const velocity = Math.random() * 0.25 + 1;
const shift = { x: -(Math.random() + 2), y: -(Math.random() + 3) };
const char = chars[textString[i]];
const width = 80;
const half = 6.5 * width;
const left = textMatrix[i * 2] * width - half;
const top = textMatrix[i * 2 + 1] * range * 1.2 - range * 2.4;
for (let j = 0; j < fireNumber * char.length * 0.25; j++) {
const rand = Math.floor(Math.random() * char.length * 0.5);
const x = char[rand * 2] + shift.x;
const y = char[rand * 2 + 1] + shift.y;
const text = {
x: center.x + left * 0.9,
y: center.y + top,
left: center.x + left,
size: Math.random() + 0.5,
fill: randColor(),
vx: x * (velocity + (Math.random() - 0.5) * 0.5),
vy: y * (velocity + (Math.random() - 0.5) * 0.5),
ay: 0.08,
alpha: 1,
life: Math.round((Math.random() * range) / 2) + range / 1.5,
};
text.base = {
life: text.life,
size: text.size,
};
text.direct = (text.left - text.x) * 0.08;
listText.push(text);
}
}
if (++textIndex < textString.length) {
setTimeout(initText, 10);
}
}
更新运动函数
- 循环
listText
粒子动画对象。 - 更新数组内每一项的运动轨迹,生命周期,以及大小变化。
- 每一项生命周期递减。
- 判断生命周期是否结束,如果结束清除该项。
- 更新粒子文字函数
function update() {
// 更新listText运动
for (let i = listText.length - 1; i >= 0; i--) {
const text = listText[i];
text.vx *= 0.9;
text.vy *= 0.9;
text.direct *= 0.9;
text.x += text.vx + text.direct;
text.y += text.vy;
text.vy += text.ay;
text.alpha = text.life / text.base.life;
text.size = text.alpha * text.base.size;
text.alpha = text.alpha > 0.6 ? 1 : text.alpha;
// 生命周期递减
text.life--;
if (text.life <= 0) {
listText.splice(i, 1);
}
}
initText();
}
绘制内容函数
- 每次绘制前清空画布。
- 设置新图像绘制目标图像具体属性。
- 具体
globalCompositeOperation
可参考 canvas globalCompositeOperation 属性 内容进行学习。
- 具体
- 按照初始绘制内容进行
canvas
绘制。
function draw() {
// 绘制前清空
ctx.globalCompositeOperation = "source-over";
ctx.globalAlpha = 0.2;
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, canvas.width, canvas.height);
// 设置新图像绘制目标图像具体属性
ctx.globalCompositeOperation = "screen";
// 绘制: HAPPY NEW YEAR 2020!
for (let i = 0; i < listText.length; i++) {
const text = listText[i];
ctx.globalAlpha = text.alpha;
ctx.fillStyle = text.fill;
ctx.fillRect(
text.x - text.size,
text.y - text.size,
text.size * 2,
text.size * 2
);
}
}
循环动画执行函数
- 使用
requestAnimationFrame
方法执行循环函数。- 具体
requestAnimationFrame
可参考 window.requestAnimationFrame 内容进行学习。
- 具体
(function loop() {
requestAnimationFrame(loop);
update();
draw();
})();
随机颜色生成函数
最后这个函数其实就比较简单了,为了生成一个彩色的各节点封装了一个随机生成 rgb
颜色的公共方法。
function randColor() {
const r = Math.floor(Math.random() * 256);
const g = Math.floor(Math.random() * 256);
const b = Math.floor(Math.random() * 256);
const color = `rgb(${r}, ${g}, ${b})`;
return color;
}
总结
到这里所有的代码内容就结束了,由于时间有限,就不一行一行分析,大家可以通过复制上方代码片段进行实际效果预览和修改。
有更好的想法和思路也欢迎大家在评论区留言,码字不易,看到的朋友们如果绝我的写的内容对你有一点点启发,希望不要吝惜点赞,你们的鼓励就是我写作的动力!
在这里提前给各位拜年了!🔥
祝大家:
工作顺利,身体健康,万事如意,幸福美满!
🧨 新年快乐,🐯 虎虎生威!
转载自:https://juejin.cn/post/7051506171361886244