轉(zhuǎn)自博客原文連接:https://tong-h.github.io/2019/04/23/canvas-fontparticle/
想要往倉庫多填一些干貨,一個文字粒子效果 點這里看效果

效果圖
隨機初始化部分粒子
1、首先要明白每個粒子都是一個對象,都有自己的移動軌跡,起點,移動速度,終點
2、粒子活動軌跡:初始化 ---- 聚合拼合文字形狀 ---- 散開 ---- 再聚合 ---- 散開...
3、我們需要根據(jù)動畫時間調(diào)整粒子移動的速度來安排他們的位置
class Point{
constructor() {
this.startx = Math.floor(Math.random() * docsize[0]), // 初始起點
this.starty = Math.floor(Math.random() * docsize[1]),
this.speedx = (Math.random() * 2 - 1) * pointspeed, // 移動速度
this.speedy = (Math.random() * 2 - 1) * pointspeed,
this.endx = 0, // 終點
this.endy = 0,
this.color = Math.floor(Math.random() * 5) // 粒子顏色
}
endpoint(x, y) {
this.endx = x
this.endy = y
}
animal() {
this.startx += this.speedx
this.starty += this.speedy
// 到達邊界改變粒子運動方向
this.speedx *= this.startx > docsize[0] || this.startx < 0 ? -1 : 1
this.speedy *= this.starty > docsize[1] || this.starty < 0 ? -1 : 1
// 調(diào)整點的移動速度用以聚和拼合文字
if(time === 100 || time === 600 || time === 1100) {
this.speedx = (this.endx - this.startx) / joinspeed
this.speedy = (this.endy - this.starty) / joinspeed
}
// 到達終點后靜止不動
if(time === 100 + joinspeed || time === 600 + joinspeed || time === 1100 + joinspeed) {
this.speedx = 0
this.speedy = 0
}
// 散開
if(time === 300 || time === 800) {
this.speedx = (Math.random() * 2 - 1) * pointspeed
this.speedy = (Math.random() * 2 - 1) * pointspeed
}
maincontent.beginPath()
maincontent.fillStyle = color[this.color]
maincontent.arc(this.startx, this.starty, 7, 0, Math.PI * 2)
maincontent.fill()
}
}
使用 canvas 畫板生成文字
// 【文字面積,循環(huán)時用于判讀y軸高度,粒子大小間隔, 文字寬度】
let [imgdata, cyclic, size, textwith] = [{}, 1, 16, 0]
textcontext.font = "normal 900 " + fontsize +"px Avenir, Helvetica Neue, Helvetica, Arial, sans-serif"
textwith = Math.floor(textcontext.measureText(text).width)
textcontext.fillStyle = '#ff0000'
textcontext.fillText(text, (docsize[0] - textwith) / 2, (docsize[1]) / 2)
textwith = ~~ (textwith) * size + size
遍歷 imageData 獲取文字區(qū)域的像素坐標
不了解 imagedata 怎么用? 看看這篇文章cannvas的imagedata對象
獲取坐標這里有很多種方法,我看了一些教程好像沒人像我這么寫,要注意的是
- imageData 4個元素為一個像素,也就是一個R G B A 值,A 是 alpha 透明度
- 空白的區(qū)域rgba就是 0,0,0,0 , 文字區(qū)域就是有顏色的如果你沒有設(shè)置字體顏色默認是黑色 rgba 就是 0,0,0,255,通過判斷第四個元素可以獲取文字區(qū)域
- 但是我建議重新設(shè)置一個其他的顏色比如紅色 255,0,0,255,用第1個和2個數(shù)字來判斷這樣字體邊緣會圓滑些,因為在字體邊緣黑色和白色的交界處可能有某幾個像素不是透明的
- 每個坐標最后都會生成一個圓,所以這里獲取的是圓心的坐標,圓之間還需要留有空隙,所以遍歷的時候你要根據(jù)你的圓的大小掌握好間隔
- 獲取文字區(qū)域粒子數(shù)量后需要判斷,目前屏幕上現(xiàn)有的粒子是否足夠拼合和文字或者是否還需再添加粒子
- 確定粒子數(shù)量后再將文字坐標作為粒子移動終點賦值給粒子
// 獲取文字所在區(qū)域,盡可能減小面積
imgdata = textcontext.getImageData(0,0, textwith, fontsize * 2)
textcontext.clearRect(0, 0, docsize[0], docsize[1])
// 粒子圓心坐標,粒子數(shù)組
let [x, y, len] = [0, 0, 0]
// 遍歷data數(shù)據(jù)查找文字所在的坐標
for (var i = 0; i < imgdata.data.length; i += size * 4) {
if (imgdata.data[i] === 255 && imgdata.data[i+3] === 255) {
// 判斷當前粒子數(shù)量是否能夠拼合文字
if (len > pointarr.length - 1) pointarr.push(new Point)
// 獲取每個粒子聚攏的終點
pointarr[len].endpoint(i /4 % textwith, cyclic)
len ++
}
if (i/4 == cyclic * textwith) {
cyclic += size
i = textwith * (cyclic-1) * 4
}
}
pointarr.length - 1 - len > 0 ? pointarr.splice(len, pointarr.length - len) : ''
源碼帶有詳細的注解點這兒
更多效果
開源不易,覺得還不錯點個 start 吧 (′▽`???)