在上一篇文章 Canvas基础-粒子动画Part2 中讲了让图片做粒子动画。上篇写完不久,想起既然是用Canvas,写上去的东西都可以做粒子动画,那么就补充讲下文字做粒子动画,至于为什么拖了这么久,还不是因为去写公众号和研究微信小程序了,给公众号搞了一个2048形式的小游戏,叫『码工修炼之路』,在公众号 『程序员的诗和远方』回复关键字
2048
就可以玩了。
上一篇中我们是把图片给画到 Canvas 中,要写文字进去就简单多,直接有方便的接口就可以做,我们来试试,
先在页面上添加一个输入框,用来输入文字。
<body>
<div class="input-wrap">
<input id="txt" type="text" name="" value="" placeholder="输入发射文字...">
<button id="btn" class="btn">发射</button>
</div>
<canvas id="canvas" width="300" height="300" ></canvas>
</body>
之后修改一下我们的 init()
方法。
function init() {
var s = 0;
input = document.querySelector("#txt");
var l = input.value ? input.value : "Beta";
input.value = "";
// 有正在运行的动画要取消掉
if (rafId) cancelAnimationFrame(rafId);
setFontSize(fontSize);
s = Math.min(fontSize,
(canvas.width / ctx.measureText(l).width) * 0.8 * fontSize,
(canvas.height / fontSize) * (isNumber(l) ? 1 : 0.5) * fontSize);
setFontSize(s);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillText(l, 10, 0);
dotList = [];
handleCanvas();
ctx.clearRect(0, 0, canvas.width, canvas.height);
draw2();
}
这里大概解释一下:
setFontSize(fontSize)
;ctx.measureText(l)
方法来检查字体的宽度,measureText()
方法返回一个对象,该对象包含以像素计算的指定字体宽度,因为希望留点空间,所以这里乘了一个0.8。同样,对高度也做了一些处理,不能搞太高。fillText
方法把文字写进去,第一个参数为文本值,第二个为开始绘制文本的x坐标,第三个为y坐标,这里做了一点偏移。handleCanvas()
setFontSize(), isNumber(), handleCanvas()
函数如下:
var fontSize = 500,
fontFamily = 'Helvetica Neue, Helvetica, Arial, sans-serif';
function setFontSize(s) {
ctx.font = s + 'px ' + fontFamily;
}
function isNumber(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
}
function handleCanvas() {
var imgW = canvas.width,
imgH = canvas.height;
var imgData = ctx.getImageData(0, 0, imgW, imgH);
// console.log(imgData);
for(var x=0; x<imgData.width; x+=6) {
for(var y=0; y<imgData.height; y+=6) {
var i = (y*imgData.width + x) * 4;
if(imgData.data[i+3] > 128 && imgData.data[i] < 100){
var dot = new Dot(x, y, 2);
dotList.push(dot);
}
}
}
}
关于 handleCanvas()
函数有不明白的地方,可以看之前的文章 Canvas基础-粒子动画Part1 里面的 init()
函数,或者给我留言。
最后的 draw2()
函数,以及用到的几个函数,和 Canvas基础-粒子动画Part2 中基本一致,这里就不多做解释了,有不明白的欢迎留言。
function Dot(centerX, centerY, radius) {
this.x = centerX;
this.y = centerY;
this.radius = radius;
this.frameNum = 0;
this.frameCount = Math.ceil(3000 / 16.66);
this.sx = 400;
this.sy = 400;
this.delay = this.frameCount*Math.random();
this.delayCount = 0;
}
// t 当前时间
// b 初始值
// c 总位移
// d 总时间
function easeInOutCubic(t, b, c, d) {
if ((t/=d/2) < 1) return c/2*t*t*t + b;
return c/2*((t-=2)*t*t + 2) + b;
}
var rafId = null,
finishCount = 0;
function draw2() {
console.log(1);
ctx.clearRect(0, 0, winWidth, winHeight);
ctx.fillStyle = "#000";
var len = dotList.length,
curDot = null,
frameNum = 0,
frameCount = 0,
curX, curY;
finishCount = 0;
for(var i=0; i < len; i+=1) {
// 当前粒子
curDot = dotList[i];
// 获取当前的time和持续时间和延时
frameNum = curDot.frameNum;
frameCount = curDot.frameCount;
if(curDot.delayCount < curDot.delay){
curDot.delayCount += 1;
continue;
}
ctx.save();
ctx.beginPath();
if(frameNum < frameCount) {
curX = easeInOutCubic(frameNum, curDot.sx, curDot.x-curDot.sx, curDot.frameCount);
curY = easeInOutCubic(frameNum, curDot.sy, curDot.y-curDot.sy, curDot.frameCount);
ctx.arc(curX, curY, curDot.radius, 0, 2*Math.PI);
curDot.frameNum += 1;
} else {
ctx.arc(curDot.x, curDot.y, curDot.radius, 0, 2*Math.PI);
finishCount += 1;
}
ctx.fill();
ctx.restore();
if (finishCount >= len) {
cancelAnimationFrame(rafId);
return;
}
}
rafId = requestAnimationFrame(draw2);
}
出来的效果:
会发现文字被截掉了一部分,尿性又来了,这是因为 Canvas 画出来的文本在垂直方向并不是按顶部对齐的,类似 css 中的 vertical-align
属性,Canvas 可可以通过 contex
的 textBaseline
属性来设置文字的对其方式,加入代码 ctx.textBaseline="top";
即可。
最后出来的效果:
如果理解了前面对图片做粒子动画的话,理解这个应该不难。另外,本篇贴了很多代码,因为之前有读者留言说希望能贴完整代码出来,有助于理解。考虑到移动端看代码,上下左右翻来翻去体验实在不好,还是贴尽量完整的主要代码好了,源码都有放在 github 上,可以对照着来看,更重要的是自己亲手实践实践。关于贴代码的建议,欢迎留言讨论哈。
源码地址: https://github.com/bob-chen/canvas-demo/blob/master/basic/particle-part3.html
http://www.w3school.com.cn/tags/canvas_measuretext.asp
http://www.w3school.com.cn/tags/canvas_filltext.asp