
你有沒有想過,讓你心愛的品牌 logo 或圖片不只是乖乖躺在頁面上,而是像魔法一樣,飛散、組合,最後化身為獨一無二的動態文字呢?🤩
今天我們來玩點不一樣的,用 Vue.js 搭配 p5.js,打造一個屬於你自己的個性化視覺效果,讓你的網站不再是單純的圖片展示,而是充滿創意、互動和樂趣!
而且不會與別人類似!
這不僅僅是一次技術挑戰,還是一次讓你的設計充滿創意的過程。
準備好展開這趟動態視覺之旅了嗎?
Let's go!🚀
在設計中,我們不再滿足於靜態的圖像展示,互動感和個性化成為提升使用者體驗的關鍵。
品牌設計也是如此,透過動畫和視覺動效,品牌的形象不僅變得更加生動,還能傳達更多情感和信息。
這裡我們的核心概念有三個:

這次我們的目標是使用 Vue.js 和 p5.js,將一張圖片轉換成點陣粒子,並實現點擊畫布後,粒子飛散再重新組合成文字的效果。
接下來,我們會逐步剖析這個實作的每個核心部分,並解釋其設計原因和技術細節。
npm install p5
p5.js TypeScript 型別定義
npm install @types/p5 --save-dev
這次我們的目標是使用 Vue.js 和 p5.js,將一張圖片轉換成點陣粒子,並實現點擊畫布後,粒子飛散再重新組合成文字的效果。接下來,我們會逐步剖析這個實作的每個核心部分,並解釋其設計原因和技術細節。

在 Vue 的 onMounted 階段,我們利用 p5.js 來初始化畫布並載入圖片,這是整個粒子效果的基礎。
const canvasContainer = ref<HTMLDivElement | null>(null);
onMounted(() => {
    const initP5 = () => {
        const sketch = (p: p5) => {
            p.preload = () => {
                img = p.loadImage('https://i.imgur.com/rGmeexe.png');
            };
            p.setup = () => {
                const canvas = p.createCanvas(window.innerWidth, window.innerHeight);
                canvas.parent(canvasContainer.value as HTMLDivElement); 
                img.loadPixels();  // 載入圖片的像素數據
                p.noStroke();      // 去除粒子的邊框
                const scaleFactor = 0.65;
                createParticleDotGrid(p, scaleFactor); // 建立粒子點陣
                createTextParticles(p, "Sunny.Cat", 100, p.height / 2); // 建立文字粒子
            };
        };
        new p5(sketch);
    };
    initP5();
});
canvasContainer:這個是用來放置 p5.js 畫布的容器。我們使用 Vue 的 ref 來存取 DOM 元素。onMounted:在 Vue 元件掛載後,我們初始化 p5.js 畫布。使用 p5.js 的 createCanvas 方法來設定畫布大小,並將其掛載到 canvasContainer 中。img.loadPixels():這個方法是關鍵,它會讀取圖片的像素數據,使我們能夠操作圖片中的每個像素,並將它們轉換成粒子。我們首先載入圖片並設定畫布,之後將圖片轉換為粒子點陣,並準備生成文字粒子。
我們使用 Particle 類別來表示圖片中的每一個像素粒子。
這些粒子可以在畫布上顯示、隨機飛散,並移動到指定的文字位置。
class Particle {
    pos: p5.Vector;             // 粒子當前位置
    originalPos: p5.Vector;      // 粒子原始位置
    col: p5.Color;               // 粒子的顏色
    vel: p5.Vector;              // 粒子的速度向量
    targetPos: p5.Vector | null = null;  // 粒子的目標位置
    constructor(x: number, y: number, col: p5.Color) {
        this.pos = new p5.Vector().set(x, y);  // 粒子位置
        this.originalPos = this.pos.copy();    // 保存原始位置
        this.col = col;                        // 粒子的顏色
        this.vel = p5.Vector.random2D().mult(5);  // 隨機速度,讓粒子可以隨機飛散
    }
    // 顯示粒子
    display(p: p5) {
        p.fill(this.col);
        p.noStroke();
        p.ellipse(this.pos.x, this.pos.y, particleSize, particleSize);  // 使用圓形表示粒子
    }
    // 粒子飛散效果
    scatter(p: p5) {
        this.pos.add(this.vel);  // 根據速度向量更新位置
        this.display(p);         // 顯示粒子
    }
    // 移動粒子到目標位置(文字組合)
    moveToText(p: p5) {
        if (!this.targetPos) {
            let randomTarget = p.random(textParticles);
            this.targetPos = p.createVector(randomTarget.x, randomTarget.y);  // 隨機選擇一個文字粒子的位置作為目標
        }
        let direction = p5.Vector.sub(this.targetPos, this.pos);  // 計算目標位置與當前位置的向量差
        this.pos.add(direction.mult(0.2));  // 更新粒子位置,逐漸移向目標
        this.display(p);
    }
}
Particle 類別:每個粒子都是一個獨立的物件,它擁有位置、顏色、速度等屬性。
這樣的設計方便我們管理和控制每個粒子的行為。
scatter 方法:當使用者點擊畫布時,粒子會根據隨機速度飛散開來,產生隨機運動效果。
moveToText 方法:這是粒子在飛散後的動作,會逐步移動到指定的目標位置(文字的某個字母像素點)。

這個函數將圖片的像素轉換為粒子,每個粒子代表圖片中的一個像素點。
const createParticlesFromImage = (p: p5, offsetX: number, scaleFactor: number, flipHorizontal: boolean) => {
    for (let y = 0; y < img.height; y += particleSize * 1.5) { 
        for (let x = 0; x < img.width; x += particleSize * 1.5) { 
            const index = (x + y * img.width) * 4;
            const r = img.pixels[index];
            const g = img.pixels[index + 1];
            const b = img.pixels[index + 2];
            let posX = flipHorizontal ? img.width - x : x;
            posX = posX * scaleFactor + offsetX; // 調整水平位置
            const posY = y * scaleFactor;        // 調整垂直位置
            const particle = new Particle(posX, posY, p.color(r, g, b));
            particles.push(particle);  // 將粒子存入 particles 數組
        }
    }
};
scaleFactor 對圖片進行縮放,並根據 flipHorizontal 決定是否翻轉粒子的水平位置,使粒子排列更加豐富。particles 數組,這些粒子將在畫布上展示。
這裡我們創建文字粒子,將它們作為粒子飛散後重新排列的目標位置。
const createTextParticles = (p: p5, text: string, _x: number, y: number) => {
    const textCanvas = p.createGraphics(p.width, p.height); // 使用 p5.js 創建一個隱藏的畫布來生成文字
    textCanvas.pixelDensity(1);
    textCanvas.background(255);
    textCanvas.textAlign(p.CENTER, p.CENTER);
    textCanvas.fill(0);
    textCanvas.textSize(100);
    textCanvas.text(text, p.width / 2, y);  // 在隱藏的畫布上繪製文字
    textCanvas.loadPixels();
    for (let i = 0; i < textCanvas.width; i += particleSize) {
        for (let j = 0; j < textCanvas.height; j += particleSize) {
            const index = (i + j * textCanvas.width) * 4;
            const r = textCanvas.pixels[index];
            if (r < 128) {  // 找出黑色部分(即文字部分)的像素點
                textParticles.push({ x: i, y: j }); // 將文字的像素點記錄為粒子的目標位置
            }
        }
    }
};
createGraphics:我們使用隱藏的 p5.Graphics 來繪製文字,不會在實際畫布上顯示,只用來捕捉文字的像素點。loadPixels:這個方法會讀取文字圖像的像素數據,我們只保留文字的黑色部分,並將其作為粒子的目標位置。當使用者點擊畫布時,粒子會先飛散,隨後逐漸移動並組成文字。
p.mousePressed = () => {
    if (!
isScattered && !isFormingText) {
        isScattered = true;
        setTimeout(() => {
            isScattered = false;
            isFormingText = true;
        }, 2000);
    }
};
mousePressed:當使用者點擊畫布時,觸發粒子飛散效果,並在 2 秒後開始組成文字。isScattered 與 isFormingText:這兩個變數控制著粒子的狀態,確保粒子在不同階段有不同的動作。
如何將圖片轉換為粒子效果:學會使用 p5.js 的 loadPixels 方法將圖片的像素轉化為粒子,並以點陣形式呈現在畫布上,實現圖像粒子化的效果。
粒子飛散與重新組合的動畫:通過使用隨機速度讓粒子隨機飛散,並且在飛散後逐漸移動到指定的文字位置,實現動畫效果,提升網頁的互動性和趣味性。
結合 Vue.js 與 p5.js 進行互動設計:透過 Vue.js 的元件化設計與 p5.js 的動畫功能,學會如何將畫布動畫與網頁結合,並且實現點擊觸發的互動動畫設計。
這三個重點可以幫助讀者掌握粒子動畫的製作流程,並靈活運用於不同的網頁設計中。
通過這次的實作,我們成功讓靜態圖片「活」了起來,變成了會飛散、會組合的點陣粒子動畫!
這不僅讓網站變得更有趣、更有吸引力,也為個性化展示打開了一扇創意的大門。
是不是覺得自己也能做出一個酷炫的專屬動畫呢?😉
無論是要為自己的作品加點魔法,還是想讓網頁充滿驚喜,只要勇敢嘗試,創意無限!
相信你在不斷挑戰自我的過程中,也會發現更多靈感和可能性!
加油吧,讓你的技術和創意一起翱翔!🚀🌟