寫在最前
本次分享一個用canvas粒子渲染文字的“完整”版實現,功能包括:隨機粒子緩動動畫,粒子匯聚、發散,並拼出你想要的文字。本文繼續上面一節基於canvas使用粒子拼出你想要的文字的基礎效果,完善了在文字拼接過程中的粒子效果。
歡迎關注我的博客,不定期更新中——
上節回顧
自上次的分享基於canvas使用粒子拼出你想要的文字,我們實現了一個可配置的用粒子拼出想要的文字效果,不過這個效果是靜態的,就像這樣:
這次我們試圖對它進行了一些完善,讓其可以儘量完整的實現我們的訴求。
效果圖
慣例直接看下效果圖:
這是一個事先配置好的動畫效果用來展示一下粒子的完整運動軌跡。在這個例子中我們做了以下幾件事:
- 初始化一定數量的粒子,開始緩動
- 監聽到輸入事件後,拼出相應文字。其拼接的粒子是從緩動中的隨機粒子抽取,如果不夠則新加粒子
- 當再次監聽到輸入事件後,原文字散開到隨機位置,新文字按照上一步進行繪製,以此往復
故本次我們討論的重點則圍繞緩動動畫與粒子的匯聚與散開進行展開
整體邏輯
先來分析一下整體的實現思路
1、首先爲了增加粒子的重用性,不需要每次拼新的文字都new一堆新的粒子。故選擇維護了兩個數組進行存放相應粒子。即隨機緩動數組與展示效果數組
2、初始化一定數目粒子,粒子位置隨機,半徑隨機,加入到隨機粒子數組
3、對加入到隨機粒子數組中的對象執行緩動動畫
4、監聽事件被觸發,清空展示粒子數組,將當前頁面所有粒子全部加入到隨機粒子數組,同時更新粒子狀態,讓每個粒子重新出現在各個隨機位置
5、當拼接文字開始,每次需要繪製一個粒子到拼接的地點時則從隨機粒子數組中pop出一個粒子對象,更新粒子的位置等信息,push到展示粒子數組中,如果隨機粒子數組數量不夠,則新建對象添加到展示粒子數組。
6、展示粒子數組中的粒子收集完畢後,遍歷數組依次渲染到指定位置
新的監聽事件被觸發重複以上的4、5、6步驟
實現核心
- 隨機粒子的緩動動畫
- 從隨機粒子變爲展示粒子的過程中,需要繪製出粒子的運動軌跡以實現發散與匯聚的效果
緩動動畫
就像下面這樣:
觀察其中一個粒子的動畫行爲可以發現緩動動畫實現核心點在於:起始速度快,之後速度逐步遞減,直至停下。
所以速度是與起始點與中點距離相關的,距離越大,速度越快,反之亦然。那麼我們的速度就可以表示爲:相對路程 ✖️ 緩動係數(一個小數),即可使每一幀的位移距離從大到小,速度從快至慢。
代碼實現:
首先初始化了一些隨機的粒子:
for(var i = 0; i < 100; i++) {
var option = {
radius: ~~(Math.random() * 3) + 1,
x: ~~ (Math.random() * canvas.width),
y: ~~ (Math.random() * canvas.height),
color: 'rgba(255, 255, 255, 0.5'
}
var bubble = new Bubble(option)
circleArr.push(bubble)
}
之後再繪製粒子緩動:
var dis = ~~ Math.sqrt(Math.pow(Math.abs(this.x - this.randomX), 2) + Math.pow(Math.abs(this.y - this.randomY), 2)),
ease = 0.05
...
if( dis > 0) {
//當粒子在向目標點移動的過程中,由緩動係數與距離控制速度
if(this.x < this.randomX) {
this.x += dis * ease
} else {
this.x -= dis * ease
}
if(this.y < this.randomY) {
this.y += dis * ease
} else {
this.y -= dis * ease
}
} else {
//達到目標點後更新下一個目標點
this.randomX += ~~(Math.random() * (Math.random() > 0.5 ? 5 : -5) * 2)
this.randomY += ~~(Math.random() * (Math.random() > 0.5 ? 5 : -5) * 2)
}
ctx.beginPath()
ctx.arc(this.x, this.y, this.originRadius, 0, 2 * Math.PI, false)
ctx.fillStyle = 'rgba(255, 255, 255, 0.5)'
ctx.fill()
}
...
粒子的發散與匯聚
每一個粒子都會經歷由隨機粒子 => 展示粒子 => 隨機粒子的過程,在這其中我們需要控制好,粒子的座標,由於在文字與隨機移動的切換中,均爲緩動效果實現。故我們不單單要在每個狀態改變的時候維持好下一個狀態下該粒子的座標,還應該同步保持一個座標的副本。由於需要通過兩個狀態下的座標值算出需要移動的距離,並且這個距離是固定的,所以在狀態切換的過程中不能改變這兩個座標值,但是粒子時刻在動,故我們需要一個座標副本來實時表示當前的粒子座標位置。
...
if(this.isWord) { //如果該粒子當前是文字
var disLastPosition = ~~ Math.sqrt(Math.pow(Math.abs(this.lastX - this.randomX), 2) + Math.pow(Math.abs(this.lastY - this.randomY), 2))
var ease = 0.05
if (disLastPosition > 0) {
if (this.lastX < this.randomX) {
this.lastX += disLastPosition * ease
} else {
this.lastX -= disLastPosition * ease
}
if (this.lastY < this.randomY) {
this.lastY += disLastPosition * ease
} else {
this.lastY -= disLastPosition * ease
}
} else {
this.lastX = this.randomX
this.lastY = this.randomY
this.x = this.lastX //更新x,y值
this.y = this.lastY
this.isWord = false
}
ctx.beginPath()
ctx.arc(this.lastX, this.lastY, this.originRadius, 0, 2 * Math.PI, false)
ctx.fillStyle = 'rgba(255, 255, 255, 0.5)'
ctx.fill()
return
}
...
此時我們使用lastX,lastY屬性來作爲前一個座標值的變量,在計算出位移距離後,通過改變lastX,lastY來實現粒子的動畫效果,當粒子到達指定位置後再更新x,y的座標爲新的座標。
小結
至此我們大體完成了一個完整的粒子文字特效,當然和一些線上炫酷的��不能比,不過大體是那麼個意思,代碼細節部分有興趣的同學可以參照源碼(見最後),或者自己實現一版玩玩~
其他canvas相關文章
- 基於canvas使用粒子拼出你想要的文字
- 基於canvas使用貝塞爾曲線平滑擬合折線段
- 用canvas實現視頻播放與彈幕功能
- 基於canvas實現波浪式繪製圖片
- 基於 canvas 實現的一個截圖小 demo
最後
源代碼見:這裏
慣例po作者的博客,不定時更新中——
有問題歡迎在issues下交流。