做一个在线的孔明灯许愿,既环保又绿化,又可以寄托许愿者的美好愿望,先看效果:
制作思路:
孔明灯有二种:第一种,背景飘动的一群假孔明灯,第二种,是一个图片,用户实际放飞的孔明灯
制作背景孔明灯思路
1、轮廓制作思路:画一个孔明灯,计算每个点的坐标值,划线,最下面的线是有一点弧度的,用了赛贝尔曲线,整体颜色使用渐变填充
2、文字制作思路:给孔明灯加上随机文字,文字我用了一个数组,里面存了几个默认的祝福语,随机赋值给不同的孔明灯
3、飞行思路:根据屏幕宽度,计算需要多少孔明灯,给这些孔明灯定制:随机大小,随机轨迹,随机速度,和随机透明度,遇到屏幕方向变化的处理
制作点击放飞的前景孔明灯思路
1、图片:在网上找一个喜欢的孔明灯图片,photoshop抠图切割,以canvas加载图片方式加入
2、输入框:制作屏幕左下角的输入框和放飞按钮,输入文字字数限制
3、dom点击:输入框许愿文字赋值给孔明灯,调整文字在孔明灯上的大小和位置调整
4、飞行轨迹:起始位置高度在屏幕最下方,宽度中间,缓慢上升,左右方向随机,飞行速度随机,为了实现飞远效果,设置了上升过程逐渐变小,设置了最小值,达到最小值就不会再变小了,设置遇到屏幕边界处理方法。
剩余细节处理
1、背景夜色图片
2、飘动的小光点
github地址
代码我用vue写的,下载后,npm i 一下,就可以运行,直属代码在Test.vue里面了
代码解析
1、画背景孔明灯
分析孔明灯的形状:设置点坐标,最下面的边是弧度的,中间有一条分割线,标明了坐标的位置
代码如下:
moveto 是把画笔移动某个坐标,lineto是划线,赛贝尔曲线三个参数的意义我就不说了,有在线测试赛贝尔曲线的,可以搜一下在上面感受学习。
//---------画孔明灯外形 begin
this.ctx.beginPath();
this.ctx.moveTo(x + 10 * scale, y + 10 * scale); //左上
this.ctx.lineTo(x + 20 * scale, y + 38 * scale); //左下
this.ctx.quadraticCurveTo(
x + 25 * scale,
y + 41 * scale,
x + 30 * scale,
y + 38 * scale
); //赛贝尔曲线给最下面边,增加弧度
this.ctx.lineTo(x + 30 * scale, y + 38 * scale); //右下
this.ctx.lineTo(x + 40 * scale, y + 10 * scale); //右上
this.ctx.lineTo(x + 25 * scale, y); //顶部
this.ctx.closePath();
this.ctx.fill();
//画中间的那一条分割线,
this.ctx.moveTo(x + 25 * scale, y); //顶部
this.ctx.lineTo(x + 25 * scale, y + 38 * scale); //顶部中间
var lingrad2 = this.ctx.createLinearGradient(
x + 25 * scale,
y + 0 * scale,
x + 40 * scale,
y + 30 * scale
);
lingrad2.addColorStop(0, "#000");
lingrad2.addColorStop(1, "#ff8d1a");
this.ctx.strokeStyle = lingrad2; //增加渐变
this.ctx.lineWidth = 0.3;
this.ctx.stroke();
//---------孔明灯外形 end
2、给孔明灯加文字:使用filltext,四个参数,分别是文字内容,x轴位置,y轴位置,文字大小, 文字内容从数组下标0-10随机拿,文字大小我使用了0-1之间随机数乘以19
const textarr = ["股票", "回本","祖国","统一","国庆","快乐", "安康", "涨薪","平安", "如意", "暴富", "不卷"];
const randomtext = Math.ceil(p.scaleText * 10); //随机取一个词
//赋值给孔明灯文字
this.ctx.fillText(
textarr[randomtext],
x + 15 * scale,
y + 22 * scale,
scale * 19
);
3、循环:循环出很多灯,并且让这些灯动起来,这就需要requestAnimationFrame函数,用于实现动画效果。
requestAnimationFrame 我是这样理解的,每秒钟大概执行60次左右它的回调函数,只要我们随机加减x和y的位置,就看起来像是在动移动,实际上不是真的在移动,而是像电影一样一帧一帧的刷新
前景孔明灯
先声明好image,然后用drawImage 来画图,
mounted() {
this.imageObj = new Image();
this.imageObj.src = "/44.png";
requestAnimationFrame(this.drawOneLine); // 在此调用requestAnimationFrame回调单个大孔明灯的函数
}
methods: {
fly() {
const one ={} //存放灯的变量
this.arrclickFly.push(one); //点击一次存入数组一个变量
},
drawOneLine(timer) {
this.arrclickFly.forEach((item) => {
//循环许愿灯的数组,画图
this.ctx.drawImage(
this.imageObj,
item.p.x,
item.p.y,
item.size,
item.size
);
},
}
点击放飞按钮的时候,调用fly方法,给数组增加arrclickFly孔明灯的数量。在drawOneLine回调函数中回循环这个数组,然后给每个孔明灯计算位置,速度,缩放大小。
mounted() {
this.imageObj = new Image();
this.imageObj.src = "/44.png";
requestAnimationFrame(this.drawOneLine); // 在此调用requestAnimationFrame回调单个大孔明灯的函数
}
methods: {
fly() {
const text = this.$refs.text.value;
if (text.length > 10) {
this.message = "请输入10个字符以内的祝福";
return;
}
const rany = this.binRandom(0.5);
const one = {
p: { x: this.width / 2, y: this.height - 200 },
v: {
x: this.binRandom(0.5) ? this.random(1) : this.random(-1),
y: rany < 0.2 ? 0.2 : rany,
},
o: this.random(1) + 0.3,
scale: 5,
scaleText: text,
size: 200,
};
this.arrclickFly.push(one);
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
},
drawOneLine(timer) {
this.arrclickFly.forEach((item) => {
this.ctx.drawImage(
this.imageObj,
item.p.x,
item.p.y,
item.size,
item.size
);
item.p.x += item.v.x;
item.p.y -= item.v.y;
item.size = item.size - 0.05; //孔明灯越飞越远,
if (item.p.x > this.width + 20 || item.p.x < -60) item.v.x *= -1; //x轴的位置乘以速度,碰到浏览器边缘返回
if (item.p.y > this.height || item.p.y < -60) item.v.y *= -1; //y轴的位置乘以速度,碰到浏览器边缘返回
if (item.size <= 10) item.size = 10;
if (item.scaleText) {
//在孔明灯上写字
this.ctx.fillStyle = "#ffcc00"; //轮廓颜色
this.ctx.font = `${item.size / 18}px serif`;
let x = item.p.x + item.size / 2;
let y = item.p.y + item.size / 4;
this.ctx.fillTextVertical(item.scaleText, x, y, 10);
}
});
requestAnimationFrame(this.drawOneLine, timer);
},
}
如果需要代码的去git上自行下载吧。