在上一篇文章 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();
}複製程式碼
這裡大概解釋一下:
- 首先獲取了文字框的輸入值,如果沒有輸入東西,我們就按輸入值是Beta來做;
- 然後,如果有正在執行的動畫,我們需要把正在執行的給幹掉;
- 之後這一步是設定畫到 Canvas 中的字型大小,我們先按預設的值給 Canvas 設定了一個字型大小,就是第一句
setFontSize(fontSize)
; - 實際上,Canvas 的空間有限,如果預設的字型大小不合適,很可能文字會現實不完全。於是這裡使用了
ctx.measureText(l)
方法來檢查字型的寬度,measureText()
方法返回一個物件,該物件包含以畫素計算的指定字型寬度,因為希望留點空間,所以這裡乘了一個0.8。同樣,對高度也做了一些處理,不能搞太高。 - 之後把我們處理過後的 fontSzie 值給重新設定給 Canvas;
- 使用
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 上,可以對照著來看,更重要的是自己親手實踐實踐。關於貼程式碼的建議,歡迎留言討論哈。
原始碼地址: github.com/bob-chen/ca…
Part 1 地址: gold.xitu.io/post/57cda0…
Part 2 地址: gold.xitu.io/post/57dd27…
碎碎念
最近總想記錄一些所思所想,寫寫科技與人文,寫寫生活狀態,寫寫讀書感悟,昨天終於動筆,在微信公眾號上寫,主要是扯淡和感悟,歡迎關注,交流。
微信公眾號:程式設計師的詩和遠方
公眾號ID : MonkeyCoder-Life
參考
www.w3school.com.cn/tags/canvas…