首先,效果圖為:
H5代碼為
<canvas id="canvas"></canvas>
樣式設計CSS:
<style> html { height: 100% } body { margin: 0; height: 100%; background: #fff; } canvas { display: block; width: 100%; height: 100%; position: absolute; top: 0; left: 0; } </style>
最重要的就是js代碼了。這里一步步來實現上述粒子效果。
通過看圖我們可以發現,就是由很多圓點跟線條組成的動態效果,那么要實現的就是繪制N個圓形,以及在一定距離內的兩個原點之間連線。
1.首先獲取到canvas對象,以及獲取屏幕的寬,高,創建一個圓點列表。
var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d'); var w = canvas.width = canvas.offsetWidth; var h = canvas.height = canvas.offsetHeight; var circles = [];
2.繪制N個原點,這里隨便多少個都可以,暫定60個。要繪制圓形,需要用到canvas的arc()方法,具體思路見canvas繪制圖形,那么我們這里定義一個方法
function drawCircle(ctx) { ctx.beginPath(); ctx.arc(this.x, this.y, this.r, 0, 360); ctx.fillStyle = 'rgba(204, 204, 204, 0.3)'; ctx.fill(); }
該方法就可以通過canvas對象實現創建圓形了。那么看上述方法中的this.s, this.y表示的坐標,其實是這一方法定義在一個對象中,因為我們要創建很多圓形,所以定義了一個js對象,並且給該對象加入了畫圓,畫線,以及原點不斷移動的方法。
//創建對象:圓 //x, y為坐標點, r為半徑, _mx, _my為移動的距離 function circle(x, y) { this.x = x; this.y = y; this.r = Math.random() * 10; this._mx = 1 - (Math.random() * 2); this._my = 1 - (Math.random() * 2); this.drawCircle = drawCircle; function drawCircle(ctx) { ctx.beginPath(); ctx.arc(this.x, this.y, this.r, 0, 360); ctx.fillStyle = 'rgba(204, 204, 204, 0.3)'; ctx.fill(); } this.move = move; function move(w, h) { this._mx = (this.x < w && this.x > 0) ? this._mx : (-this._mx); this._my = (this.y < h && this.y > 0) ? this._my : (-this._my); this.x += this._mx / 2; this.y += this._my / 2; } this.drawLine = drawLine; function drawLine(ctx, _circle) { var dx = this.x - _circle.x; var dy = this.y - _circle.y; var d = Math.sqrt(dx * dx + dy * dy); if (d < 150) { ctx.beginPath(); ctx.moveTo(this.x, this.y); ctx.lineTo(_circle.x, _circle.y); ctx.strokeStyle = 'rgba(204, 204, 204, 0.3)'; ctx.stroke(); } } }
由上述代碼可以看到,只需要傳入相應的(x, y)坐標點,就可以創建一個原點。
其中r半徑是隨機函數產生的隨機數,所以最后生成的原點大小不一。
偏移量_mx,_my也是隨機數,由1 - (Math.random() * 2)可以看出偏移范圍為(-1, 1),所以我們可以看到有的點向左,有的向下,向左,向右,方向不一,能一定程度保證所有點均勻分布在屏幕中。
需要注意的是this.drawCircle = drawCircle;在對象中定義了方法之后還需要把方法添加到 JavaScript 對象。
移動方法move(),確保原點要在屏幕內移動,所以對於偏移量有個判斷,當坐標超出坐標范圍后,偏移量正負變換,即向之前移動方向的反方向移動。
兩點之間畫線的方法,這里設定兩點之間連線的范圍為0~150,超過150就不會再連線了。具體連線方法也可見canvas繪制圖形。方法中涉及到求兩點之間的直線距離,Math.sqrt(dx * dx + dy * dy),這個應該也好理解。
3. 定義了繪制圓點的方法,那么我們要再定義一個方法將所有的點畫出來。這里就要用到最開始定義的圓點列表circles。
<body onload="init(60)">
...
</body>
function init(num) { for(var i = 0; i < num; i ++) { circles.push(new circle(Math.random() * w, Math.random() * h)); } draw(); }
body上的onload方法表示網頁進入時觸發的方法,一打開網頁就要馬上加載init方法。
init方法,可以看到,是創建了60個circle實例push進原點列表中了,而創建的實例的坐標點都是隨機的范圍再(0, 0) ~ (w, h)。創建好實例后就要開始畫點了啊!調用draw方法。
function draw() { ctx.clearRect(0, 0, w, h); for(var i = 0; i < circles.length; i ++) { circles[i].move(w, h); circles[i].drawCircle(ctx); for(var j = i + 1; j < circles.length; j++) { circles[i].drawLine(ctx, circles[j]); } } requestAnimationFrame(draw); }
在上述方法中,遍歷所有原點,調用circle對象的方法,畫圓以及移動,里面的for循環表示每個當前點都與數組后面的點相連即可,即不需要考慮后面的點與前面的點相連問題,因為兩點之間連一次就可以了,所以j從1 + 1開始。
這里更新頁面用requestAnimationFrame替代setTimeout。
window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
4. 到目前為止,就實現了60個點在屏幕上移動,且150距離內的兩點會有連線,那么為了更好的體驗效果,我們可以加入鼠標進入屏幕的互動效果。
鼠標進入后會生成一個半徑確定的(這里假定8,都可以)不同顏色的原點,且該點在與任意點距離在150以內時要有連線。
那么,需要創建一個對象繼承circle,重寫畫圓方法。
function currentCircle(x, y) { circle.call(this, x, y); this.drawCircle = drawCircle; function drawCircle(ctx) { ctx.beginPath(); ctx.arc(this.x, this.y, 8, 0, 360); ctx.fillStyle = 'rgba(255, 77, 54, 0.6)'; ctx.fill(); } }
其實,這里也可以不定義這個對象,可以把circle對象改一下,將顏色也定為參數即可。
好了,對象定義好了,那么就要創建實例了,初始狀態為
var current_circle = new currentCircle(0, 0);
在屏幕中移動以及離開屏幕的事件響應也可以有了。clientX為鼠標指針向對於瀏覽器頁面(或客戶區)的水平坐標。
window.onmousemove = function(e) { e = e || window.event; current_circle.x = e.clientX; current_circle.y = e.clientY; } window.onmouseout = function() { current_circle.x = null; current_circle.y = null; }
最后,鼠標點與其他點的連線,修改draw方法
function draw() { ctx.clearRect(0, 0, w, h); for(var i = 0; i < circles.length; i ++) { circles[i].move(w, h); circles[i].drawCircle(ctx); for(var j = i + 1; j < circles.length; j++) { circles[i].drawLine(ctx, circles[j]); } } if (current_circle.x) { current_circle.drawCircle(ctx); for (var k = 0; k < circles.length; k++) { current_circle.drawLine(ctx, circles[k]) } } requestAnimationFrame(draw); }
加了一個判斷,如果current_circle的x坐標不為0了(初始狀態為0,見實例創建),那么就要畫鼠標點了,且遍歷所有的圓點,能連線的都連線。到這里,開頭的動圖上的效果都實現了。
最后,原碼下載地址GitHub:https://github.com/sakurayj/canvas