到可配置特效模塊)
簡介這是一份面向前端開發(fā)者與網頁動效愛好者的H5 Canvas粒子爆炸動畫特效源碼包適合用于個人主頁、活動頁或產品展示頁的背景動畫開發(fā)幫助解決靜態(tài)頁面缺乏視覺沖擊力的問題。壓縮包共6個文件以3個JavaScript腳本為核心配合1個HTML與1個HTM頁面、1個CSS樣式表整體約59KB體積輕量便于直接嵌入現(xiàn)有項目。代碼圍繞Canvas 2D渲染上下文展開通過requestAnimationFrame驅動動畫循環(huán)構建粒子系統(tǒng)模擬彩色粒子的爆炸、位移、變色與消散過程并可將文字輪廓拆解為粒子形成字符隨爆炸動態(tài)聚合與散開的視覺效果。讀者可從中學習粒子對象的屬性定義、生命周期管理、重力與速度衰減等物理規(guī)則以及批量數(shù)組操作與閉包狀態(tài)保持的寫法。目前已有321人學習適合具備一定JavaScript基礎、希望掌握高級前端動態(tài)效果制作技巧的開發(fā)者參考借鑒。1. 從一次頁面改版說起這個粒子爆炸動畫到底能干什么前陣子幫朋友改一個活動落地頁對方丟過來一個壓縮包說“就要那種點一下炸開的效果越炫越好但別太重”。打開一看就是這份 H5 Canvas 粒子爆炸動畫特效。它干的事很純粹在 Canvas 畫布上生成一批粒子鼠標點擊或頁面加載時粒子從中心向四周炸開帶拖尾、漸隱、重力回落最后自然消散。整個包不依賴任何框架純原生 JavaScript 加 Canvas 2D 繪制丟進任何 H5 頁面、活動頁、游戲加載頁都能直接用。適合誰一是做營銷活動頁、需要點擊反饋動效的前端二是學 Canvas 動畫、想搞懂粒子系統(tǒng)怎么寫的初學者三是手里有現(xiàn)成項目想找個輕量特效模塊嵌進去的開發(fā)者。它不解決業(yè)務邏輯只解決“視覺炸裂”這一件事但這件事它做得足夠干凈代碼量小改起來不心疼。下面我按“能干什么 → 怎么跑 → 怎么改”的順序把這份源碼拆開講。2. 核心架構拆解一個粒子從生成到消散的完整鏈路2.1 目錄結構與文件職責拿到壓縮包解壓后結構通常很扁平常見做法是下面這樣particle-explosion/ ├── index.html # 演示入口含 canvas 容器和基礎樣式 ├── css/ │ └── style.css # 頁面重置、canvas 全屏鋪滿 ├── js/ │ ├── particle.js # 粒子類位置、速度、顏色、生命周期 │ ├── explosion.js # 爆炸控制器生成粒子、驅動動畫循環(huán) │ └── main.js # 入口綁定事件、初始化畫布 └── README.mdindex.html只做兩件事放一個canvas元素按順序引入三個 JS。style.css把 body 的 margin 清零canvas 設成display:block避免出現(xiàn)滾動條——這是 Canvas 全屏動畫最常見的翻車點很多人第一次跑發(fā)現(xiàn)右邊和下邊有白邊就是沒處理這個。particle.js是單個粒子的抽象explosion.js是粒子集合的管理器main.js負責把兩者和 DOM 事件接起來。這種分層的好處是你想換爆炸形狀只動explosion.js的生成邏輯想改粒子外觀只動particle.js的繪制方法互不干擾。2.2 粒子類位置、速度與生命周期的三要素粒子系統(tǒng)的核心就三個量位置、速度、壽命。位置決定畫在哪速度決定往哪飛壽命決定什么時候消失。看一段典型的粒子類實現(xiàn)// particle.js class Particle { constructor(x, y, options {}) { this.x x; // 當前橫坐標 this.y y; // 當前縱坐標 this.vx options.vx || 0; // 橫向速度分量 this.vy options.vy || 0; // 縱向速度分量 this.size options.size || 2; // 粒子半徑 this.color options.color || #ff6b35; this.life 1; // 生命值1 為滿血 this.decay options.decay || 0.015; // 每幀衰減量 this.gravity options.gravity || 0.05; // 重力加速度 } update() { this.vy this.gravity; // 縱向速度受重力影響 this.x this.vx; // 位置隨速度更新 this.y this.vy; this.life - this.decay; // 生命值遞減 } draw(ctx) { ctx.globalAlpha Math.max(this.life, 0); // 透明度跟隨生命值 ctx.fillStyle this.color; ctx.beginPath(); ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2); ctx.fill(); } isDead() { return this.life 0; } }update()每幀調用一次先加重力再更新位置順序不能反——先更新位置再加重力粒子會顯得“飄”下落感不自然。draw()里用globalAlpha控制透明度粒子越接近死亡越透明這是漸隱效果的關鍵。decay和gravity是最值得調的兩個參數(shù)decay越大粒子消失越快gravity越大下落越明顯。我一般把decay設在 0.01 到 0.02 之間gravity設在 0.03 到 0.08 之間具體看頁面節(jié)奏。2.3 爆炸控制器批量生成與動畫循環(huán)單個粒子沒意義爆炸是一批粒子同時朝不同方向飛??刂破髫撠熢谟|發(fā)點周圍生成 N 個粒子每個粒子的速度方向按角度均勻分布// explosion.js class Explosion { constructor(canvas) { this.canvas canvas; this.ctx canvas.getContext(2d); this.particles []; this.rafId null; } burst(x, y, count 80) { for (let i 0; i count; i) { // 角度均勻分布保證炸開是個圓 const angle (Math.PI * 2 * i) / count; // 速度加隨機擾動避免過于規(guī)整 const speed 2 Math.random() * 4; this.particles.push(new Particle(x, y, { vx: Math.cos(angle) * speed, vy: Math.sin(angle) * speed, size: 1 Math.random() * 2, color: hsl(${Math.random() * 60 10}, 90%, 60%), decay: 0.012 Math.random() * 0.008 })); } this.start(); } start() { if (this.rafId) return; // 防止重復啟動多個循環(huán) const loop () { this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); // 倒序遍歷方便刪除死亡粒子 for (let i this.particles.length - 1; i 0; i--) { const p this.particles[i]; p.update(); p.draw(this.ctx); if (p.isDead()) this.particles.splice(i, 1); } if (this.particles.length 0) { this.rafId requestAnimationFrame(loop); } else { this.rafId null; // 粒子清空后停掉循環(huán)省性能 } }; this.rafId requestAnimationFrame(loop); } }burst()里角度用(Math.PI * 2 * i) / count均勻鋪開保證爆炸是個完整的圓而不是一坨。速度加Math.random()擾動是為了避免所有粒子飛得一樣遠看起來像機械的同心圓。顏色用 HSL 的色相隨機控制在暖色區(qū)間10 到 70出來的效果偏火焰橙紅符合“爆炸”的直覺。start()里有兩個細節(jié)值得說。一是if (this.rafId) return防止連續(xù)點擊時啟動多個requestAnimationFrame循環(huán)那樣粒子會越跑越快是新手最容易踩的坑。二是粒子清空后把rafId置空并停止循環(huán)不然后臺會一直空轉頁面卡頓的元兇往往就在這。2.4 入口綁定畫布尺寸與事件接入main.js把畫布尺寸和事件接起來// main.js const canvas document.getElementById(stage); const explosion new Explosion(canvas); function resize() { canvas.width window.innerWidth; canvas.height window.innerHeight; } resize(); window.addEventListener(resize, resize); canvas.addEventListener(click, (e) { const rect canvas.getBoundingClientRect(); explosion.burst(e.clientX - rect.left, e.clientY - rect.top, 100); }); // 頁面加載后自動炸一次作為入場動效 window.addEventListener(load, () { explosion.burst(canvas.width / 2, canvas.height / 2, 120); });resize()里直接改canvas.width/height注意這會清空畫布內容并重置坐標系所以要在初始化時調用一次。點擊坐標用getBoundingClientRect()換算不能直接用e.clientX因為 canvas 不一定貼在頁面左上角這是坐標偏移問題的根源。load事件里自動炸一次適合做首屏入場如果不需要刪掉即可。3. 跑起來本地啟動、參數(shù)調試與效果驗證3.1 三種運行方式與選擇這份源碼是純靜態(tài)的不需要構建工具。最省事的方式是直接雙擊index.html瀏覽器就能跑。但如果你后續(xù)要改代碼、加模塊建議起個本地服務避免file://協(xié)議下某些瀏覽器對資源的限制。用 Python 起服務# 在項目根目錄執(zhí)行Python 3 自帶 python -m http.server 8080 # 瀏覽器訪問 http://localhost:8080用 Node 的話裝個serve也行npx serve . # 默認 3000 端口按提示訪問如果你打算把它嵌進現(xiàn)有項目直接把js/下三個文件和 canvas 標簽搬過去注意引入順序particle.js→explosion.js→main.js因為后者依賴前者的類定義。3.2 關鍵參數(shù)速查與調參方向調參是這份特效最常做的事把幾個核心參數(shù)列出來改的時候心里有數(shù)參數(shù)位置默認范圍調大效果調小效果countburst 調用處80~120粒子更多更密稀疏、性能更好speedexplosion.js2~6炸得更遠聚在中心decayparticle.js0.012~0.02消失更快拖尾更久gravityparticle.js0.03~0.08下落明顯懸浮感強sizeparticle.js1~3粒子更粗更細膩調參有個血淚經驗別一次改好幾個改一個刷新看一次。粒子效果是多個參數(shù)疊加出來的同時改容易找不到是哪個起的作用。我一般先定count和speed定整體形態(tài)再調decay和gravity定節(jié)奏最后微調size和顏色。3.3 驗證效果是否正常跑起來后正常表現(xiàn)是頁面加載后中心炸開一團暖色粒子向四周飛散并逐漸透明消失點擊任意位置在點擊處再炸一次。如果出現(xiàn)下面幾種情況說明哪里不對粒子全部往一個方向飛檢查angle計算是不是漏了Math.PI * 2粒子不消失越積越多檢查isDead()和splice邏輯多半是decay沒生效點擊位置偏移檢查有沒有用getBoundingClientRect()換算連續(xù)點擊越來越快檢查start()里的rafId防重入判斷。4. 避坑與常見問題排查4.1 粒子越點越多、頁面越來越卡現(xiàn)象連續(xù)快速點擊后粒子數(shù)量暴漲幀率明顯下降。原因每次burst()都往this.particles里 push但沒有上限控制點擊頻率高時粒子數(shù)會失控。解決在burst()開頭加個上限判斷比如if (this.particles.length 500) return;或者用對象池復用死亡粒子避免頻繁創(chuàng)建銷毀。4.2 畫布模糊、粒子邊緣發(fā)虛現(xiàn)象在高分屏上粒子看起來糊邊緣不銳利。原因canvas 的 CSS 尺寸和實際像素尺寸不一致瀏覽器做了拉伸。解決按devicePixelRatio放大畫布實際尺寸再用ctx.scale()縮放坐標系const dpr window.devicePixelRatio || 1; canvas.width window.innerWidth * dpr; canvas.height window.innerHeight * dpr; canvas.style.width window.innerWidth px; canvas.style.height window.innerHeight px; ctx.scale(dpr, dpr);4.3 頁面切到后臺再回來粒子瞬移或堆積現(xiàn)象切走標簽頁一段時間再切回粒子位置突變或一次性涌出。原因requestAnimationFrame在后臺會暫停但update()里的速度是按幀累加的恢復后時間差沒補償。解決用時間戳計算 delta把速度乘以時間因子而不是每幀固定加。簡單點也可以在visibilitychange事件里暫停和恢復動畫。4.4 移動端點擊沒反應或延遲現(xiàn)象手機上點半天不炸或者炸的位置不對。原因移動端有 300ms 點擊延遲且觸摸事件和鼠標事件模型不同。解決改用touchstart或pointerdown事件pointerdown能同時覆蓋鼠標和觸摸是現(xiàn)在更推薦的做法。坐標換算邏輯不變e.clientX在 pointer 事件里同樣可用。4.5 嵌入現(xiàn)有頁面后樣式沖突現(xiàn)象搬進項目后 canvas 尺寸不對或者被其他元素蓋住。原因canvas 默認是 inline 元素受父容器 line-height 和字體影響且 z-index 沒設。解決給 canvas 加display:block; position:fixed; top:0; left:0; z-index:9999; pointer-events:none;pointer-events:none讓 canvas 不攔截點擊這樣它作為背景特效時不會擋住下面的按鈕。5. 二次開發(fā)從固定爆炸到可配置特效模塊把這份源碼改造成能復用的模塊是我拿到手后做的第一件事。原始代碼把參數(shù)寫死在類里換個頁面就得改源碼不優(yōu)雅。我的做法是抽一個配置對象出來讓調用方傳參// 改造后的調用方式 const fx new Explosion(canvas, { count: 100, speedRange: [2, 6], decayRange: [0.012, 0.02], gravity: 0.05, hueRange: [10, 70], // 暖色 autoBurst: true // 是否加載后自動炸 }); // 想換冷色調只改配置 fx.setOptions({ hueRange: [180, 240] });配置化的關鍵是給每個參數(shù)一個合理的默認值調用方不傳也能跑。hueRange控制色相區(qū)間暖色是 10 到 70冷色是 180 到 240改一個數(shù)組就能換風格比翻源碼找顏色值高效得多。再進一步可以把爆炸形狀從圓形改成其他圖案。核心在burst()里生成速度向量的方式。圓形是角度均勻分布想改成心形就把角度和速度按心形參數(shù)方程映射// 心形爆炸的速度方向映射示意 const t (Math.PI * 2 * i) / count; const heartX 16 * Math.pow(Math.sin(t), 3); const heartY -(13 * Math.cos(t) - 5 * Math.cos(2*t) - 2 * Math.cos(3*t) - Math.cos(4*t)); const scale 0.3; this.particles.push(new Particle(x, y, { vx: heartX * scale, vy: heartY * scale, // ...其余參數(shù) }));這樣粒子會朝心形輪廓飛散形成心形爆炸。同理換成星形、文字輪廓都可以只要把目標形狀的參數(shù)方程代進去。這是粒子系統(tǒng)好玩的地方形狀邏輯和粒子邏輯解耦改形狀不影響粒子的生命周期管理。驗證改造是否成功我一般用三步先看默認配置下效果和原版一致再改一個配置項確認生效最后在移動端和桌面端各跑一遍確認沒有平臺差異。這套流程走下來基本能保證模塊在別的項目里不會翻車。從那以后我每次拿到這類特效源碼都強制先跑通默認效果、再抽配置、最后做跨端驗證三步缺一不可。希望這份拆解幫到你拿到包能直接跑起來也能按自己的需求改出想要的樣子。本文還有配套的精品資源點擊獲取