震驚,canvas文字粒子效果,只需要100行程式碼,簡單易懂。

有夢想的api搬運工發表於2020-03-17

震驚,canvas文字粒子效果,只需要100行程式碼,簡單易懂。

震驚,canvas文字粒子效果,只需要100行程式碼,簡單易懂。

canvas是使用JavaScript程式繪圖(動態生成),相比於css,可以更加簡單方便的繪製細節的樣式。其中最強大的功能莫過去畫素的處理。一個畫素一個畫素去繪製任何想要的展示效果。接下來,要為各位觀眾姥爺去介紹一下文字動態粒子效果,當然是一些比較簡單。如果各位觀眾姥爺感興趣,可以在此基礎上擴充套件

如何繪製文字粒子動態效果?

1.瞭解一下基本的canvas的Api,像畫點,畫圓,以及填充顏色等等。 2.文字打碎,記錄每個文字所在畫布中的位置,本文的重點。 3.生成隨機粒子,並且設定每個粒子的運動軌跡。 4.移動到步驟二記錄下來位置。 5.使用requestAnimationFrame來繪製每一幀的畫布(不瞭解的觀眾老爺還不抓緊去看看。戳這裡)

就這麼簡單,只要100行程式碼,就能學會簡單的文字動態效果

原始碼解析

主生成畫布

瞭解基本的canvas API,怎麼這麼懶!!!!還要我給找地址。戳這裡)

獲取文字位置資訊,還不想讓使用者看到,這就需要用到兩個畫布了,下面是建立主畫布,設定畫布的大小。

   let WIDTH,HEIGHT,cxt,raf,points;
   window.onload = () => {
       WIDTH = document.body.clientWidth;
       HEIGHT = document.body.clientHeight;
       const canvas =  document.getElementById('canvas'); //主畫布
       canvas.width = WIDTH;
       canvas.height = HEIGHT;
       ctx = canvas.getContext('2d');
       points =  createViceCanvas(); // 建立副畫布,寫出想展示的文字,並且獲取文字的位置資訊。
       init()
   }
複製程式碼

生成副畫布

建立一個副畫布,裡面寫入想要展示的文字,獲取到文字粒子的位置。這裡要注意了,主畫布和副畫布大小要一樣,這樣副畫布裡面的點位,才能正確的在主畫布中展示。副畫布建立好後,無需新增到dom中。這裡寫入了文字 www,

function createViceCanvas() {
    const viceCanvas = document.createElement('canvas')
    viceCanvas.width = WIDTH;
    viceCanvas.height = HEIGHT;
    let viceCxt = viceCanvas.getContext('2d')
    initCanvas(viceCxt)
    return getFontInfo(viceCxt); // 獲取文字粒子的位置資訊
}
function initCanvas(ctx){ //ctx 是副畫布
    const font = 'www'
    ctx.font = '200px Arial';
    const measure =  ctx.measureText(font)
    ctx.fillText(font, (WIDTH - measure.width) / 2, HEIGHT / 2);
}

複製程式碼

這裡使用了方法measureText,獲取文字的寬度,為了能夠在畫布中間繪製文字。高度居中,感興趣的可以自行嘗試。。

獲取文字位置資訊

如何獲取文字的位置?上課了,劃重點。

function getFontInfo(ctx) { //ctx是副畫布,文字取點,獲取每個文字在畫布中的座標。
    let imageData = ctx.getImageData(0,0,WIDTH,HEIGHT).data;
    const particles = [];
    for(let x = 0; x < WIDTH; x += 4) {
        for(let y=0; y < HEIGHT; y += 4) {
            const fontIndex = (x + y * WIDTH) * 4 + 3;
            if(imageData[fontIndex] > 0) {
                particles.push(new Particle({
                    x,
                    y,
                }))
            }
        }
    }
    return particles;
}
複製程式碼

data屬性返回一個 Uint8ClampedArray,它可以被使用作為檢視初始畫素資料。每個畫素用4個1bytes值(按照紅,綠,藍和透明值的順序; 這就是"RGBA"格式) 來代表。每個顏色值部份用0至255來代表。每個部份被分配到一個在陣列內連續的索引,左上角畫素的紅色部份在陣列的索引0位置。畫素從左到右被處理,然後往下,遍歷整個陣列

我這裡使用的畫布大小是 1080 * 768, 用座標系來表示就是x軸1080,y軸768

其實就是RGBA(255,255,255,0) 這四個類似的數字表示一個畫素,那1080*768這個畫布用Uint8ClampedArray陣列表示,總共由多少個元素呢? 就是1080 * 768 * 4 個元素

下面畫了一張簡陋的座標圖。

震驚,canvas文字粒子效果,只需要100行程式碼,簡單易懂。

比如x軸(1,1)這個位置,需要用Uint8ClampedArray陣列的前四位表示. x軸(2,1)這個位置,需要用Uint8ClampedArray索引4-7的元素表示。 那座標(1,2)第一位對應表示Uint8ClampedArray索引就是(1080*(2-1) + (1-1)) * 4 -1 . 座標(m,n)首位索引對應的就是(width*(n-1) + m-1)) * 4 -1。 不懂的觀眾姥爺可以慢慢品一下。~~~~~

這裡還有一個小技巧,rgba表示的顏色,第四個元素表示透明度,當我們畫布上並未繪製內容時,第四個元素位0。所以,原始碼中const fontIndex = (x + y * WIDTH) * 4 + 3 取到透明度不為0時候,則證明當前畫素是有內容的,即可獲取到文字在畫布中的位置。

每個粒子的移動軌跡

上面一步獲取了文字粒子在畫布中的位置,我們想要的效果,是粒子動畫, 則我們需要在隨機生成一個粒子, 然後移動到對應的獲取到的文字位置。

   class Particle {
       constructor(center) {
           this.x = center.x; // 記錄點位最終應該停留在的x軸位置
           this.y = center.y; // 記錄點位最終應該停留在的y軸位置
           this.item = 0;     // 貝塞爾曲線係數
           this.vx = 20;      // 點位在x軸的移動速度
           this.vy = 16;       // 點位在y軸的移動速度
           this.initX = Math.random() * WIDTH; // 點位隨機在畫布中的x座標
           this.initY = Math.random() * HEIGHT; // 點位隨機在畫布中的y座標                
       }
       draw(){ // 繪製點位
           ctx.beginPath();
           const {x, y} = threeBezier( // 貝塞爾曲線,獲取每一個tick點位所在位置
           this.item,
           [this.initX,this.initY],
           [this.x,this.y],
           [this.x,this.y],
           [this.x, this.y]
           )
           ctx.arc(x, y, 2, 0, 2 * Math.PI, true);
           ctx.fillStyle="red"
           ctx.fill();
           ctx.closePath();
           this.speed(); // 點位下次tick繪製時的座標
       }
       speed() { // 每個點位繪製後的座標
           this.initX +=this.vx;
           this.initY +=this.vy;
           this.item += 0.005;
       }
   }
複製程式碼

這裡,需要一個隨機粒子, 用來做移動,並且最後要組成一個文字,文字的最終位置我們已經獲取到了,就是constructor的引數center。那粒子該怎麼運動呢?我這裡使用的貝塞爾曲線,並且封裝成了一個方法。

const threeBezier = (t, p1, p2, cp1, cp2) => {
       const [startX,startY] = p1;
       const [endX,endY] = p2;
       const [cpX1,cpY1] = cp1;
       const [cpX2,cpY2] = cp2
       let x = startX * Math.pow(1-t,3) +
               3 * cpX1 * t * Math.pow(1-t,2) +
               3 * cpX2 * Math.pow(t,2) * (1-t) + 
               endX * Math.pow(t,3);
       let y = startY * Math.pow(1-t,3) +
               3 * cpY1 * Math.pow(1-t,2) * t + 
               3 * cpY2 *(1- t) * Math.pow(t,2) + 
               endY * Math.pow(t,3)
       return {
           x,
           y,
       }
   }
複製程式碼

簡單的對引數說明

t:貝塞爾曲線係數,0-1之前。 p1: 軌跡移動的起點。 p2: 軌跡移動的終點。 cp1: 第一個控制點。 cp2: 第二個控制點。 第四個引數和第五個引數可以瞎雞兒傳,主要是控制運動的軌跡,但是p1,p2這倆引數不能亂,尤其是p2。p2要是瞎雞兒傳,還想組成文字嗎?

什麼,不懂貝塞爾曲線? 還不戳這裡?)

擴充套件: 如果文字想要五顏六色,可以在獲取文字座標時,

particles.push(new Particle({
           x,
           y,
       }))
複製程式碼

隨機朝構造器中加入一個顏色, 在Particle類中draw方法繪製時,賦值傳入的顏色。

啟動動畫

文字位置,粒子運動軌跡也確定好了,下面該如何開啟動畫?如何暫停動畫?

function init() {
    ctx.clearRect(0,0,WIDTH,HEIGHT)
    points.forEach((value) => { //
        value.draw();
    })
    raf = window.requestAnimationFrame(init)
    if(points[0].item>=1){
        window.cancelAnimationFrame(raf)
    }
}
複製程式碼

requestAnimationFrame小記:cancelAnimationFrame取消動畫,要跟在requestAnimationFrame後面。 保證在遞迴呼叫init方法之前去執行cancelAnimationFrame。別忘了requestAnimationFrame是個非同步~~~~

總結

今天的介紹canvas文字粒子效果到這裡就結束了,如果還有問題的觀眾姥爺,可以在下面留言喲。

❤️ 如果各位看官看的還進行,請給一個贊,順手點個關注,就是對我的最大支援

呀,忘記原始碼地址了。戳這裡

相關文章